Tonelli–Shanks Algorithm 二次剩余系解法 (Ural 1132. Square Root)
wikipedia上的解释和证明:http://en.wikipedia.org/wiki/Tonelli%E2%80%93Shanks_algorithm
The Tonelli–Shanks algorithm (referred to by Shanks as the RESSOL algorithm) is used within modular arithmetic to solve a congruence of the form
where n is a quadratic residue (mod p), and p is an odd prime.
Tonelli–Shanks cannot be used for composite moduli; finding square roots modulo composite numbers is a computational problem equivalent to integer factorization.[1]
An equivalent, but slightly more redundant version of this algorithm was developed by Alberto Tonelli in 1891. The version discussed here was developed independently by Daniel Shanksin 1973, who explained:
"My tardiness in learning of these historical references was because I had lent Volume 1 of Dickson's History to a friend and it was never returned."[2]
(Note: All are taken to mean , unless indicated otherwise).[edit]The algorithm
Inputs: p, an odd prime. n, an integer which is a quadratic residue (mod p), meaning that the Legendre symbol .
Outputs: R, an integer satisfying .
- Factor out powers of 2 from p − 1, defining Q and S as: with Q odd. Note that if , i.e. , then solutions are given directly by .
- Select a z such that the Legendre symbol (that is, z should be a quadratic non-residue modulo p), and set .
- Let
- Loop:
- If , return R.
- Otherwise, find the lowest i, , such that ; e.g. via repeated squaring.
- Let , and set and .
Once you have solved the congruence with R the second solution is p − R.
Example
Solving the congruence . It is clear that is odd, and since , 10 is a quadratic residue (by Euler's criterion).
- Step 1: Observe so , .
- Step 2: Take as the quadratic nonresidue (2 is a quadratic nonresidue since (again, Euler's criterion)). Set
- Step 3:
- Step 4: Now we start the loop: so ; i.e.
- Let , so .
- Set . Set , and
- We restart the loop, and since we are done, returning
Indeed, observe that and naturally also . So the algorithm yields two solutions to our congruence.
Proof
First write . Now write and , observing that . This latter congruence will be true after every iteration of the algorithm's main loop. If at any point, then and the algorithm terminates with .
If , then consider , a quadratic non-residue of . Let . Then and , which shows that the order of is .
Similarly we have , so the order of divides . Suppose the order of is . Since is a square modulo , is also a square, and hence .
Now we set and with this , and . As before, holds; however with this construction both and have order . This implies that has order with .
If then , and the algorithm stops, returning . Else, we restart the loop with analogous definitions of , , and until we arrive at an that equals 0. Since the sequence of S is strictly decreasing the algorithm terminates.
//#pragma comment(linker,"/STACK:327680000,327680000") #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define REP(i, n) for((i) = 0; (i) < (n); ++(i)) #define FOR(i, l, h) for((i) = (l); (i) <= (h); ++(i)) #define FORD(i, h, l) for((i) = (h); (i) >= (l); --(i)) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("data.out", "w", stdout); typedef long long LL; const double eps = 1e-8; const double pi = acos(-1.0); const double inf = ~0u>>2; using namespace std; LL MOD; LL mod_exp(LL a, LL b) { LL res = 1; while(b > 0) { if(b&1) res = (res*a)%MOD; a = (a*a)%MOD; b >>= 1; } return res; } LL solve(LL n, LL p) { int Q = p - 1, S = 0; while(Q%2 == 0) { Q >>= 1; S++; } if(S == 1) {return mod_exp(n, (p + 1)/4);} int z; while(1) { z = 1 + rand()%(p - 1); if(mod_exp(z, (p - 1)/2) != 1) break; } LL c = mod_exp(z, Q); LL R = mod_exp(n, (Q + 1)/2); LL t = mod_exp(n, Q); LL M = S, b, i; while(1) { if(t%p == 1) break; for(i = 1; i < M; ++i) { if(mod_exp(t, 1<<i) == 1) break; } b = mod_exp(c, 1<<(M-i-1)); R = (R*b)%p; t = (t*b*b)%p; c = (b*b)%p; M = i; } return (R%p + p)%p; } int main() { //Read(); int T, a, n, i; scanf("%d", &T); while(T--) { scanf("%d%d", &a, &n); if(n == 2) { //*** if(a%n == 1) printf("1\n"); else puts("No root"); continue; } MOD = n; if(mod_exp(a, (n-1)/2) != 1) {puts("No root"); continue; } i = solve(a, n); if(i == n - i) printf("%d\n", i); else printf("%d %d\n", min(i, n - i), max(i, n - i)); } return 0; }
//#pragma comment(linker,"/STACK:327680000,327680000") #include <iostream> #include <cstdio> #include <cmath> #include <vector> #include <cstring> #include <algorithm> #include <string> #include <set> #include <functional> #include <numeric> #include <sstream> #include <stack> #include <map> #include <queue> #define CL(arr, val) memset(arr, val, sizeof(arr)) #define REP(i, n) for((i) = 0; (i) < (n); ++(i)) #define FOR(i, l, h) for((i) = (l); (i) <= (h); ++(i)) #define FORD(i, h, l) for((i) = (h); (i) >= (l); --(i)) #define L(x) (x) << 1 #define R(x) (x) << 1 | 1 #define MID(l, r) (l + r) >> 1 #define Min(x, y) (x) < (y) ? (x) : (y) #define Max(x, y) (x) < (y) ? (y) : (x) #define E(x) (1 << (x)) #define iabs(x) (x) < 0 ? -(x) : (x) #define OUT(x) printf("%I64d\n", x) #define Read() freopen("data.in", "r", stdin) #define Write() freopen("data.out", "w", stdout); typedef long long LL; const double eps = 1e-8; const double pi = acos(-1.0); const double inf = ~0u>>2; using namespace std; LL MOD; LL mod_exp(LL a, LL b) { LL res = 1; while(b > 0) { if(b&1) res = (res*a)%MOD; a = (a*a)%MOD; b >>= 1; } return res; } int main() { //Read(); LL a, p; int T, cas = 0; scanf("%d", &T); while(T--) { scanf("%lld%lld", &a, &p); MOD = p; printf("Scenario #%d:\n", ++cas); if(mod_exp((a%p+p)%p, (p - 1)/2) == 1) puts("1"); //注意a有可能是负数 else puts("-1"); cout << endl; } }