给定奇素数 (p) 和 正整数(n) ,求所有 (x) 满足 (x^2 equiv n pmod p) 。
如果有多个解,假设有两个不同解 (x_1, x_2) ,那么有 (x_1^2 equiv x_2^2 equiv n) ,即 (x_1^2-x_2^2 equiv (x_1-x_2)(x_1+x_2) equiv 0 pmod p) 。
因为 (x_1
eq x_2) ,所以 (x_1 equiv -x_2) 。因此如果有两个解不同则它们互为相反数。
又因为 (p) 为奇素数,所以不存在 (x equiv -x) 的情况,所以如果有解就一定有两个解。
现在我们定义一个数 (n) 是二次剩余,当且仅当方程 (x^2 equiv n pmod p) 有解。
根据欧拉准则, (n) 是二次剩余当且仅当 (n^{frac{p-1}{2}} equiv 1) 。
证明:
如果 (n) 是二次剩余,假设 (x^2 equiv n pmod p) 且 (n^{frac{p-1}{2}} equiv 1 pmod p) ,则 (x^{p-1} equiv 1 pmod p) ,通过费马小定理可知这是成立的。
如果 (n^{frac{p-1}{2}} equiv 1 pmod p) ,设 (g^k equiv n pmod p) ,其中 (g) 是 (p) 的一个原根,那么有 (g^{kfrac{p-1}{2}} equiv 1 pmod p) 。因为 (g) 是原根,那么一定有 (p-1 | kfrac{p-1}{2}) ,也就是说 (2|k) 。那么显然有 (x equiv g^{frac{k}{2}} pmod p) ,即 (n) 是二次剩余。
证毕。
所以一个数 (n) 如果是二次剩余,假设 (n equiv g^k pmod p) ,当且仅当 (2|k) 。而因为 (g) 是原根,这样的 (k) 一共有 (frac{p-1}{2}) 个,也就是说模 (p) 意义下的二次剩余个数一共有 (frac{p-1}{2}) 个。这就告诉我们一件事:如果随机取一个 ([0,p-1]) 中的数,期望取两个数就能得到一个二次剩余/非二次剩余的数。而 (n^{p-1} equiv 1 pmod 1) ,我们知道 (1) 开根要么是 (1) 要么是 (-1) ,所以如果 (n) 不是二次剩余则 (n^{frac{p-1}{2}} equiv -1 pmod p) 。
求解二次剩余的解,对于 (p) 是奇素数的情况,通常使用 (
m Cipolla) 算法 (mathcal{O}(log n)) 求解。
首先我们找一个 (a) 满足 (a^2-n) 不是二次剩余。根据刚才的证明,我们期望两次能找出这个 (a) 。
接下来定义 (i^2 equiv a^2-n pmod p) 。但前面说了 (a^2-n) 不是二次剩余,怎么办呢?
这里就是 (
m Cipolla) 的核心思想,类似于实数到复数的扩域过程,定义 (A+Bi equiv a^2-n) 。
引理(1) : (i^p equiv -i pmod p) 。
证明: (i^p equiv i(i^2)^{frac{p-1}{2}} equiv i(a^2-n)^{frac{p-1}{2}} equiv -i pmod p) 。
引理(2) : ((A+B)^p equiv A^p+B^p) 。
这个很显然,用二项式定理展开后为: (sum_{i=0}^p {p choose i}A^iB^{p-i}) 。
当 (0<i<p) 时,有 ({p choose i} equiv 0 pmod p) 。
否则 ({p choose 0} equiv {p choose p} equiv 1 pmod p) 。
因此 ((A+B)^p equiv A^p+B^p) 。
定理 : ((a+i)^{p+1} equiv n pmod p) 。
根据引理 (2) 把式子展开得 ((a+i)^{p+1} equiv a^{p+1}+i^{p+1} equiv a^2+i^{p+1} pmod p) 。根据引理 (1) 化掉 (i^{p+1}) 得 (a^2-i^2 equiv n pmod p) 。
因此我们的答案貌似就是 (x equiv (a+i)^{frac{p+1}{2}} pmod p) ,唯一的问题就是 (x) 是否虚部为 (0) 。
假设存在 ((A+Bi)^2 equiv n) 且 (B
eq 0) ,则 (A^2+2ABi+B^2i^2 equiv A^2+2ABi+B^2(a^2-n) equiv n) 。
因为右边的虚部为 (0) ,所以左边的虚部也为 (0) ,即 (AB equiv 0) 。因为 (B
eq 0) ,所以 (A equiv 0) ,即 ((0+Bi)^2 equiv B^2(a^2-n) equiv n pmod p) ,即 (a^2-n equiv nB^{-2}) 。
因为 (n) 是二次剩余,而 (B^{-2}) 也肯定是二次剩余,而 (a^2-n) 不是二次剩余,所以矛盾,因此 (B = 0) 。
#include<bits/stdc++.h>
#define rg register
#define il inline
#define cn const
using namespace std;
int T, n, mod, iimg;
struct com{int x, y;};
il com operator * (cn com &a, cn com &b){return (com){(int)((1ll*a.x*b.x+1ll*a.y*b.y%mod*iimg)%mod), (int)((1ll*a.x*b.y+1ll*a.y*b.x)%mod)};}
il int fpow(int a, int b, int ans = 1){
for(; b; b >>= 1, a = 1ll*a*a%mod)if(b&1)ans = 1ll*ans*a%mod;
return ans;
}
il int fpow(com a, int b, com ans = (com){1, 0}){
for(; b; b >>= 1, a = a*a)if(b&1)ans = ans*a;
return ans.x;
}
int main(){
cin >> T, srand(time(0));
while(T--){
cin >> n >> mod;
if(mod == 2){printf("%d
", n%mod); continue;}
if(n == 0){puts("0"); continue;}
if(fpow(n, mod-1>>1) == mod-1){puts("Hola!"); continue;}
rg int a, ans1, ans2;
while(1){
a = 1ll*rand()*rand()%mod;
if(fpow((1ll*a*a-n+mod)%mod, mod-1>>1) == 1)continue;
iimg = (1ll*a*a-n+mod)%mod, ans1 = fpow((com){a, 1}, mod+1>>1), ans2 = mod-ans1;
break;
}
if(ans1 > ans2)ans1 ^= ans2 ^= ans1 ^= ans2;
printf("%d %d
", ans1, ans2);
}
return 0;
}