我们一般不区分 Legendre 符号和 Jacobi 符号,因为由完全积性可知 Jacobi 符号具有和 Legendre 符号一样的性质,所以这两种符号的计算方法是一致的。但是有一点需要注意:当 m不是奇素数 时,(ma) 的值与 a 是否是模 m 的二次剩余 无关,但是若 (ma)=−1,则说明 m 至少存在一个(实际上是奇数个)素因子 p 使得 a 是模 p 的二次非剩余,从而此时 a 是模 m 的二次非剩余。
#include <iostream>#include <random>long long p, v; // 分别是模数和 r^2 - a 的值struct Poly { long long a, b; Poly(long long _a = 0, long long _b = 0) : a(_a), b(_b) {}};Poly operator*(const Poly& x, const Poly& y) { // 重载乘法,可以参考上面有关运算性质的说明 return Poly((x.a * y.a + v * (x.b * y.b % p)) % p, (x.a * y.b + x.b * y.a) % p);}// 多项式的快速幂,用于计算答案Poly modpow(Poly a, long long b) { Poly res(1, 0); while (b) { if (b & 1) res = res * a; a = a * a; b >>= 1; } return res;}// 普通的快速幂,用于判断二次非剩余long long modpow(long long a, long long b) { long long res = 1; while (b) { if (b & 1) res = res * a % p; a = a * a % p; b >>= 1; } return res;}// 用于生成随机数std::mt19937 rng(std::random_device{}());long long cipolla(long long a, long long _p) { p = _p; if (a == 0) return 0; // 特判一下 0 的情况 else if (modpow(a, (p - 1) / 2) == p - 1) return -1; // 判断二次非剩余,此时无解 else { // 随机 r,使得 r^2 - a 是一个二次非剩余 long long r; for (r = rng() % p;; r = rng() % p) { if (modpow((r * r - a + p) % p, (p - 1) / 2) == p - 1) break; } v = (r * r - a + p) % p; return modpow(Poly(r, 1), (p + 1) / 2).a; // 根据结论式计算结果 }}int main() { int t, a, p; std::cin >> t; while (t--) { std::cin >> a >> p; int ans = cipolla(a, p); if (ans == -1) std::cout << "Hola!" << std::endl; else if (ans == 0) std::cout << 0 << std::endl; else { // 相反数是另一个解 int ans2 = (p - ans) % p; if (ans2 < ans) std::swap(ans, ans2); std::cout << ans << " " << ans2 << std::endl; } } return 0;}
[xn]1+k2x+k3x2k0+k1x=⎩⎨⎧[x(n−1)/2]1+(2k3−k22)x+k32x2k1−k0k2+k1k3x,[xn/2]1+(2k3−k22)x+k32x2k0+(k0k3−k1k2)x,if nmod2=1else if n=0
Tonelli–Shanks 算法是基于离散对数求解同余方程 x2≡a(modp) 的算法 7,其中 p 为奇素数且 a 为模 p 的二次剩余。
令 p−1=m2n,其中 m 为奇数。仍然使用随机方法寻找 r∈Fp 满足 r 为二次非剩余。令 g≡rm(modp) 且 b≡a(m−1)/2(modp),那么存在整数 e∈{0,1,2,…,2n−1} 满足 ab2≡ge(modp)。若 a 为二次剩余,那么 e 为偶数且 (abg−e/2)2≡a(modp).
证明
根据费马小定理可知
g2n≡rm2n=rp−1≡1(modp).
又由于 r 是二次非剩余,有
g2n−1≡rm2n−1=r2p−1≡−1(modp).
所以,g 模 p 的阶是 2n。又因为 ab2≡am(modp) 是 x2n≡1(modp) 的解,所以 am 是 g 的幂次。记 am≡ge(modp)。
因为 a 是二次剩余,所以
ge2n−1≡am2n−1=a2p−1≡1(modp).
由阶的性质可知,2n∣e2n−1,所以,e 是偶数。因此,abg−e/2modp 是良定义的,且
(abg−e/2)2=a2b2g−e≡am+1g−e≡a(modp).
剩下的问题是如何计算 e。Tonelli 和 Shanks 提出一次确定 e 的一个二进制位。令 e 在二进制下表示为 e=e0+2e1+4e2+⋯,其中 ek∈{0,1}。因为 a 是二次剩余,所以开始时 e0=0,然后利用如下性质逐位确定 ek 的取值:
A. Menezes, P. van Oorschot and S. Vanstone. Handbook of Applied Cryptography, 1996. ↩
Alin Bostan, Ryuhei Mori. A Simple and Fast Algorithm for Computing the N-th Term of a Linearly Recurrent Sequence. Available at https://arxiv.org/abs/2008.08822. ↩
S. Müller, On the computation of square roots in finite fields, Design, Codes and Cryptography, Vol.31, pp. 301-312, 2004. ↩
Daniel. J. Bernstein. Faster Square Roots in Annoying Finite Fields. ↩