二次剩余(Cipolla)

Posted xiaovsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二次剩余(Cipolla)相关的知识,希望对你有一定的参考价值。

question

求解

[x^2 equiv n (mod p) ]

使用((Cipolla))算法,仅可求解 (p) 是奇素数的情况

解的个数

我们称一个数为二次剩余当且仅当存在 (x^2 equiv n (mod p)) 否则为非二次剩余

考虑二次剩余 (n) 有多少个解

(n) 的解中任取出 2 个 (x_0, x_1, x_0 e x_1)

[x_0^2 equiv x_1^2 ]

移项

[x_0^2 - x_1^2 equiv 0 ]

[(x_0 + x_1) (x_0 - x_1) equiv 0 ]

已知 (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} equiv pm 1 (mod p) ]

  • 证明

由欧拉定理得知

[n ^ {p - 1} equiv 1 ]

[n ^ {2 frac {p - 1} 2} equiv 1 ]

[n ^ {frac {p - 1} 2} equiv sqrt 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

[i^p equiv -i (mod p) ]

  • 证明

[i ^ p equiv (i^2) ^ {frac{p - 1} 2} i equiv (a^2 - n) ^ {frac{p - 1} 2} i equiv -i ]

因为 (a^2 - n) 是非二次剩余,((a^2 - n)^ {frac{p - 1} 2} equiv -1)

引理 2

[(A + B)^p equiv A^p + B^p (mod p) ]

[(A + B)^p = sum_{i = 0} ^ p inom p i A^iB^{p - i} ]

[inom p i = frac{p!}{i!(p - i)!} ]

因为 (p) 是素数,所有包含 (p) 的阶乘都被模成了 (0) 只剩下了 (inom p 0 B^p + inom p p A^p)

证明

[(a + i)^{p + 1} equiv (a^p + i^p) (a + i) equiv (a + i) (a - i) equiv a^2 - i^2 equiv n ]

((a + i)^{frac{p + 1} 2}) 就是答案

那么 ((a + i)^{frac{p + 1} 2}) 的虚部是否为 0 呢

假设存在 ((A + Bi) ^ 2 equiv n, B e 0)

[A^2 + B^2 i^2 - n equiv 2ABi ]

左边虚部为 0 说明右边也为 0

(AB equiv 0),因为 (B e 0)(A equiv 0),那么

[(A + Bi)^2 equiv B^2 i^2 equiv n ]

[i^2 equiv n B^{-2} ]

因为 (n) 是二次剩余,设 (x^2 equiv n)

[i ^ 2 equiv (xB^{-1}) ^ 2 ]

(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)的主要内容,如果未能解决你的问题,请参考以下文章

二次剩余(Cipolla)

二次剩余(Cipolla)

二次剩余-Cipolla

二次剩余定理及Cipolla算法入门到自闭

二次剩余从csdn

Cipolla算法学习小记