我正在尝试计算Python中数百位数模P的立方根,并且失败了。
我找到了Tonelli-Shanks算法的代码,据说这个算法很容易从平方根修改为立方根,但是这让我望而却步。我搜索过网络和数学图书馆以及一些书都无济于事。代码会很精彩,算法也会用简单的英语解释。
这是用于查找平方根的Python(2.6?)代码:
def modular_sqrt(a, p):
""" Find a quadratic residue (mod p) of 'a'. p
must be an odd prime.
Solve the congruence of the form:
x^2 = a (mod p)
And returns x. Note that p - x is also a root.
0 is returned is no square root exists for
these a and p.
The Tonelli-Shanks algorithm is used (except
for some simple cases in which the solution
is known from an identity). This algorithm
runs in polynomial time (unless the
generalized Riemann hypothesis is false).
"""
# Simple cases
#
if legendre_symbol(a, p) != 1:
return 0
elif a == 0:
return 0
elif p == 2:
return n
elif p % 4 == 3:
return pow(a, (p + 1) / 4, p)
# Partition p-1 to s * 2^e for an odd s (i.e.
# reduce all the powers of 2 from p-1)
#
s = p - 1
e = 0
while s % 2 == 0:
s /= 2
e += 1
# Find some 'n' with a legendre symbol n|p = -1.
# Shouldn't take long.
#
n = 2
while legendre_symbol(n, p) != -1:
n += 1
# Here be dragons!
# Read the paper "Square roots from 1; 24, 51,
# 10 to Dan Shanks" by Ezra Brown for more
# information
#
# x is a guess of the square root that gets better
# with each iteration.
# b is the "fudge factor" - by how much we're off
# with the guess. The invariant x^2 = ab (mod p)
# is maintained throughout the loop.
# g is used for successive powers of n to update
# both a and b
# r is the exponent - decreases with each update
#
x = pow(a, (s + 1) / 2, p)
b = pow(a, s, p)
g = pow(n, s, p)
r = e
while True:
t = b
m = 0
for m in xrange(r):
if t == 1:
break
t = pow(t, 2, p)
if m == 0:
return x
gs = pow(g, 2 ** (r - m - 1), p)
g = (gs * gs) % p
x = (x * gs) % p
b = (b * g) % p
r = m
def legendre_symbol(a, p):
""" Compute the Legendre symbol a|p using
Euler's criterion. p is a prime, a is
relatively prime to p (if p divides
a, then a|p = 0)
Returns 1 if a has a square root modulo
p, -1 otherwise.
"""
ls = pow(a, (p - 1) / 2, p)
return -1 if ls == p - 1 else ls
答案 0 :(得分:10)
后来添加的注意事项:在Tonelli-Shanks算法中,假设p
是素数。如果我们能够快速地计算模块化平方根到复合模量,我们可以快速地计算数字。我为假设你知道p是素数而道歉。
请参阅here或here。注意,以p为模的数字是带有p个元素的有限域。
编辑:另见this(这是这些论文的祖父。)
简单的部分是当p = 2 mod 3时,那么一切都是立方体,并且a的立方根只是a**((2*p-1)/3) %p
补充:这是代码,除了素数1 mod 9之外的所有内容。我将在本周末试着去做。如果没有其他人先到达它
#assumes p prime returns cube root of a mod p
def cuberoot(a, p):
if p == 2:
return a
if p == 3:
return a
if (p%3) == 2:
return pow(a,(2*p - 1)/3, p)
if (p%9) == 4:
root = pow(a,(2*p + 1)/9, p)
if pow(root,3,p) == a%p:
return root
else:
return None
if (p%9) == 7:
root = pow(a,(p + 2)/9, p)
if pow(root,3,p) == a%p:
return root
else:
return None
else:
print "Not implemented yet. See the second paper"
答案 1 :(得分:1)
这是纯python中的完整代码。
#assumes p prime, it returns all cube roots of a mod p
def cuberoots(a, p):
#Non-trivial solution of x^r=1
def onemod(p,r):
t=p-2
while pow(t,(p-1)/r,p)==1: t-=1
return pow(t,(p-1)/r,p)
def solution(p,root):
g=onemod(p,3)
return [root%p,(root*g)%p,(root*g^2)%p]
#---MAIN---
a=a%p
if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)/3, p)] #Eén oplossing
#There are 3 or no solutions
#No solution
if pow(a,(p-1)/3,p)>1: return []
if p%9 == 4: #[13, 31, 67]
root = pow(a,(2*p + 1)/9, p)
if pow(root,3,p) == a: return solution(p,root)
else: return []
if p%9 == 7: #[7, 43, 61, 79, 97
root = pow(a,(p + 2)/9, p)
if pow(root,3,p) == a: return solution(p,root)
else: return []
if p%27 == 10: #[37, 199]
root = pow(a,(2*p +7)/27, p)
h=onemod(p,9)
for i in xrange(9):
if pow(root,3,p) == a: return solution(p,root)
root*=h
return []
if p%27 == 19: #[19, 73, 127, 181]
root = pow(a,(p + 8)/27, p)
h=onemod(p,9)
for i in xrange(9):
if pow(root,3,p) == a: return solution(p,root)
root*=h
return []
#We need an algorithm for the remaining cases
return tonelli3(a,p,True)
def tonelli3(a,p,many=False):
def solution(p,root):
g=p-2
while pow(g,(p-1)/3,p)==1: g-=1 #Non-trivial solution of x^3=1
g=pow(g,(p-1)/3,p)
return [root%p,(root*g)%p,(root*g^2)%p]
#---MAIN---
a=a%p
if p in [2,3] or a==0: return [a]
if p%3 == 2: return [pow(a,(2*p - 1)/3, p)] #Eén oplossing
#No solution
if pow(a,(p-1)/3,p)>1: return []
#p-1=3^s*t
s=0
t=p-1
while t%3==0: s+=1; t/=3
#Cubic nonresidu b
b=p-2
while pow(b,(p-1)/3,p)==1: b-=1
c,r=pow(b,t,p),pow(a,t,p)
c1,h=pow(c,3^(s-1),p),1
c=pow(c,p-2,p) #c=inverse_mod(Integer(c),p)
for i in xrange(1,s):
d=pow(r,3^(s-i-1),p)
if d==c1: h,r=h*c,r*pow(c,3,p)
elif d!=1: h,r=h*pow(c,2,p),r*pow(c,6,p)
c=pow(c,3,p)
if (t-1)%3==0: k=(t-1)/3
else: k=(t+1)/3
r=pow(a,k,p)*h
if (t-1)%3==0: r=pow(r,p-2,p) #r=inverse_mod(Integer(r),p)
if pow(r,3,p)==a:
if many:
return solution(p,r)
else: return [r]
else: return []
您可以使用以下方法进行测试:
test=[(17,1459),(17,1000003),(17,10000019),(17,1839598566765178548164758165715596714561757494507845814465617175875455789047)]
for a,p in test:
print "y^3=%s modulo %s"%(a,p)
sol=cuberoots(a,p)
print "p%s3=%s"%("%",p%3),sol,"--->",map(lambda t: t^3%p,sol)
应该产生(快速):
y ^ 3 = 17模1459
p%3 = 1 [483,329,647] ---> [17,17,17]
y ^ 3 = 17模1000003
p%3 = 1 [785686、765339、448981] ---> [17、17、17]
y ^ 3 = 17模10000019
p%3 = 2 [5188997] ---> [17]
y ^ 3 = 17模1839598566765178548164758165715596714561757494507845814465617175875455789047
p%3 = 1 [753801617033579226225229229063063663938352746555486783903392457865386777137044,655108821219252496141403141945148550782812009720868259303598196387356108990,430688128512346825798124773706784225225426198929300193651769561114101322543013] ---> [17,17,17]