二次剩余(Cipolla)
Posted xiaovsun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二次剩余(Cipolla)相关的知识,希望对你有一定的参考价值。
question
求解
使用((Cipolla))算法,仅可求解 (p) 是奇素数的情况
解的个数
我们称一个数为二次剩余当且仅当存在 (x^2 equiv n (mod p)) 否则为非二次剩余
考虑二次剩余 (n) 有多少个解
从 (n) 的解中任取出 2 个 (x_0, x_1, x_0 e x_1) 则
移项
已知 (x_0 e x_1) 则 (x_0 + x_1 equiv 0)
由此可知 (x_0, x_1) 互为相反数,且每一对相反数都对应不同的二次剩余
而且由于 (p) 是奇素数 (x_0 e x_1)
那么我们知道了,奇素数 (p) 有 (frac {p - 1} {2}) 个二次剩余
欧拉准则
我们知道了解的数量,如何判断一个数 (n) 是不是二次剩余呢?
设 (n e 0)
- (n) 是二次剩余 (iff n^{frac{p-1}{2}} equiv 1)
引理1
- 证明
由欧拉定理得知
即 (n ^ {frac {p - 1} 2}) 是 1 开方的结果,所以只能是 (pm 1)
证明
设 (g) 为 (p) 的原根,(n equiv g^k)
若 (n ^ {frac {p - 1} 2} equiv 1) 则 (g ^ {k frac {p - 1} 2} equiv 1)
因为 (g) 是原根 ((p - 1) | ( k frac{p - 1} 2))
那么 (k) 一定是偶数
则 (g^{frac k 2}) 就是 (n) 的一个解
由此我们得出如果 (n ^ {frac{p-1}{2}} equiv 1) 则 (n) 是二次剩余
如果 (n) 是二次剩余 (n ^ {frac{p - 1} 2} equiv (x^2)^{frac{p - 1} 2} equiv x^{p - 1} equiv 1)
(Cipolla) 算法
对于 (x^2 equiv n) 找到一个 (a) 使得 (a^2 - n) 是非二次剩余
这一步期望复杂度 (O(2)) 因为非二次剩余个数接近 (frac n 2)
定义 (i^2 equiv a^2 - n, i) 是类似复数一样的东西
所有数都可以表示成 (A + Bi)
((a + i)^{frac{p + 1} 2}) 即为一个解
引理 1
- 证明
因为 (a^2 - n) 是非二次剩余,((a^2 - n)^ {frac{p - 1} 2} equiv -1)
引理 2
因为 (p) 是素数,所有包含 (p) 的阶乘都被模成了 (0) 只剩下了 (inom p 0 B^p + inom p p A^p)
证明
则 ((a + i)^{frac{p + 1} 2}) 就是答案
那么 ((a + i)^{frac{p + 1} 2}) 的虚部是否为 0 呢
假设存在 ((A + Bi) ^ 2 equiv n, B e 0)
左边虚部为 0 说明右边也为 0
则 (AB equiv 0),因为 (B e 0) 则 (A equiv 0),那么
因为 (n) 是二次剩余,设 (x^2 equiv n)
(i ^ 2) 显然是二次剩余,与前提矛盾
证毕
板子
#include <bits/stdc++.h>
using namespace std;
#define I inline
#define int long long
int imuli, mod, n;
struct cpx{
int a, b;
cpx(int a, int b): a(a), b(b) {}
I cpx operator * (const cpx &rhs) const {
return cpx((a * rhs.a % mod + b * rhs.b % mod * imuli % mod) % mod, (a * rhs.b % mod + b * rhs.a % mod) % mod);
}
};
I cpx ksm(cpx a, int b){
cpx ans(cpx(1, 0));
while(b){ if(b & 1) ans = ans * a; b >>= 1; a = a * a; }
return ans;
}
I int ksm(int a, int b){
int ans = 1;
while(b){ if(b & 1) ans = ans * a % mod; b >>= 1; a = a * a % mod; }
return ans;
}
I bool check(int n){
return ksm(n, (mod - 1) / 2) == 1;
}
I void Main(){
cin >> n >> mod;
n %= mod;
if(!n) return cout << 0 << ‘
‘, void();
if(n == 2) return cout << "1" << ‘
‘, void();
if(!check(n)) return cout << "Hola!" <<‘
‘, void();
int a;
do{
a = (1ll * rand() + 1ll * rand() * rand()) % mod;
}while(check((a * a + mod - n) % mod));
imuli = (a * a % mod + mod - n) % mod;
int x0 = ksm(cpx(a, 1), (mod + 1) / 2).a, x1 = mod - x0;
if(x0 > x1) swap(x0, x1);
cout << x0 << " " << x1 << ‘
‘;
}
signed main(){
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T;
while(T--) Main();
return 0;
}
- (tps):
cpx
里面的b
可以% mod
是因为最终虚部为 0 剩下的b
在做乘法的时候需要% mod
以上是关于二次剩余(Cipolla)的主要内容,如果未能解决你的问题,请参考以下文章