我们通常所说的二次剩余一般是指p%4==3,然而对于p我们还有很多情况 例如p%4==1,这样的话,解法会有所不同。
下面给出两个经典解法的脚本
from random import *
def legendre(a, p):
symbol = pow(a, (p - 1) // 2, p)
if symbol == p - 1:
return -1
return symbol
def tonelli(a, p): #Tonelli
if a == 0 or legendre(a, p) != 1:
return 0
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
if s == 1:
return pow(a, (p + 1) // 4, p)
z = 2
while legendre(z, p) != -1:
z += 1
m = s
c = pow(z, q, p)
t = pow(a, q, p)
r = pow(a, (q + 1) // 2, p)
while t != 1:
t2 = t
i = 0
while t2 != 1 and i < m:
t2 = pow(t2, 2, p)
i += 1
b = pow(c, 2 ** (m - i - 1), p)
m = i
c = (b * b) % p
t = (t * c) % p
r = (r * b) % p
return r
#x^2=c mod p
def Cipolla_algorithm(c,p): #Cipolla
# 定义模p复数域上的乘法
def multiplication(x1,y1,x2,y2,w,p):
x=(x1*x2+y1*y2*w)%p
y=(x1*y2+x2*y1)%p
return x,y
# 获取w,使得w=-1mod(p),w是复数元的平方
def get_w(c,p):
a=randint(1,p)
w=pow(a, 2) - c
while pow(w,(p - 1)//2,p)!=p-1:
a=randint(1,p)
w=pow(a,2)-c
return w,a
#主体部分
w,a=get_w(c,p)
power=(p+1)//2
x1=a
y1=1
x=1
y=0
while(power!=0):
if(power!=(p+1)//2):
x1,y1=multiplication(x1,y1,x1,y1,w,p)
if(power & 1):
x,y=multiplication(x,y,x1,y1,w,p)
power>>=1
return x
print(tonelli(4,13))
print(Cipolla_algorithm(4,13))