【模板】二次剩余

二次剩余,俗称模意义开根
也就是对于常数 nn 解这样一个方程:

x2≡n(modp)x2≡n(modp)

这里只介绍模数 pp 为奇素数的解法,也就是 Cipolla 算法。

以下运算皆指模 pp 意义下的运算。

解的数量

严格来讲,非 0 数 nn 是二次剩余当且仅当方程 x2≡nx2≡n 有解,也就是能开根。
上述方程无解的非 0 数 nn 称作非二次剩余。

对于二次剩余 nn ,x2≡nx2≡n 有多少解?

假设有多组解,对于任意两个不相等的解 x0,x1x0​,x1​ ,有 x02≡x12x02​≡x12​ 。
移项后平方差,得到 (x0−x1)(x0+x1)≡0(x0​−x1​)(x0​+x1​)≡0 。

由于 pp 是奇素数,且 x0≠x1x0​=x1​ , x0−x1x0​−x1​ 在模 pp 意义下是不会为 0 的。
故有 x0+x1≡0x0​+x1​≡0 ,也就是说两个不相等的解一定是相反数,
换言之,该方程只有两个解,且它们互为相反数。
而当 pp 为奇素数时模意义的两个相反数不会相等,因为奇偶性不同。

还可以知道,任意一对相反数都对应一个二次剩余,而且这些二次剩余是两两不同的。
也就说二次剩余的数量恰为 p−122p−1​ ,其他的非 0 数都是非二次剩余,数量也是 p−122p−1​ 。

欧拉准则

如何快速判断一个数 nn 是否为二次剩余?

以下讨论假定 n 不为 0 。

观察费马小定理 np−1≡1np−1≡1 ,由于 pp 是奇素数,可以得到 n2(p−12)−1≡0n2(2p−1​)−1≡0 ,
也就是说 np−12n2p−1​ 是 1 开根的结果,根据上面所说, 1 开根只有两个解 1 和 -1 。
那么 np−12n2p−1​ 只能是 1 或 -1 。

若 nn 是二次剩余,则有 np−12≡(x2)p−12≡xp−1≡1n2p−1​≡(x2)2p−1​≡xp−1≡1 。

若 np−12≡1n2p−1​≡1 ,将 nn 表示为 gkgk , 其中 gg 是模 pp 意义下的原根。
那么有 gkp−12≡1gk2p−1​≡1 由于 gg 是原根,必有 p−1∣kp−12p−1∣k2p−1​ ,
也就是说 kk 一定是偶数,那么令 x≡gk2x≡g2k​ 即是 nn 开根的结果,这说明 nn 是二次剩余。

也就是说 np−12≡1n2p−1​≡1 与 nn 是二次剩余是等价的,
由于 np−12n2p−1​ 不为 1 是只能是 -1 ,那么 np−12≡−1n2p−1​≡−1 与 nn 是非二次剩余等价。

ps: 网上一堆伪证(包括楼下一篇题解)说若 nn 是非二次剩余,不存在 xx 使得上式为 1 ,但这只能说明上式为 -1 时 nn 是非二次剩余,并不能推翻“当 nn 是非二次剩余时上式为 1”

Cipolla

对于二次剩余解方程 x2≡nx2≡n 。

找到一个 aa 满足 a2−na2−n 是非二次剩余,由于非二次剩余的数量接近 p22p​ ,
通过随机 + 检验的方式期望约 2 次可以找到这样一个 aa 。

接下来定义 i2≡a2−ni2≡a2−n 。
但是 a2−na2−n 不是二次剩余,怎么找得到这样一个 ii ?

类比实数域到复数域的推广,定义这样一个 ii ,然后可以将所有数表示为 A+BiA+Bi 的形式,
其中 A,BA,B 都是模 pp 意义下的数,类似于实部和虚部。

那么 (a+i)p+1≡n(a+i)p+1≡n ,考虑证明。

引理 1 : ip≡−iip≡−i 。

证明: ip≡i(i2)p−12≡i(a2−n)p−12≡−iip≡i(i2)2p−1​≡i(a2−n)2p−1​≡−i

引理 2 : (A+B)p≡Ap+Bp(A+B)p≡Ap+Bp 。

证明:二项式定理展开后,由于 pp 是质数,除了 Cp0,CppCp0​,Cpp​ 外的组合数分子上的阶乘没法消掉,模 pp 都会为 0 ,剩下来的就是 Cp0A0Bp+CppApB0Cp0​A0Bp+Cpp​ApB0 。

现在证明上述结论:

(a+i)p+1≡(ap+ip)(a+i)≡(a−i)(a+i)≡a2−i2≡n(a+i)p+1≡(ap+ip)(a+i)≡(a−i)(a+i)≡a2−i2≡n

那么 (a+i)p+12(a+i)2p+1​ 即是一个解,其相反数是另一个解。

然而还剩最后一个问题, (a+i)p+12(a+i)2p+1​ 的“虚部”一定为 0 吗?

幸运的是,的确如此,假设存在 (A+Bi)2≡n(A+Bi)2≡n 且 B≠0B=0 ,
那么有 A2+B2i2+2ABi≡nA2+B2i2+2ABi≡n ,即 A2+B2(a2−n)−n≡−2ABiA2+B2(a2−n)−n≡−2ABi 。
式子的左边“虚部”为 0 ,那么式子右边的虚部也一定为 0 ,也就是说 AB≡0AB≡0 。
既然假设了 B≠0B=0 那么一定是 A≡0A≡0 ,也就是说 (Bi)2≡n(Bi)2≡n 。
也就是 i2≡nB−2i2≡nB−2 ,由于 B2B2 是个二次剩余,其逆元 B−2B−2 一定也是二次剩余,乘上二次剩余 nn 后一定还是二次剩余,这与 i2i2 是个非二次剩余产生矛盾。

实现

实现的时候弄个“复数”类(据说也可以不用)即可。

参考实现:

#include <cstdio>
#include <cstdlib>
typedef long long lolong;

int mod;
lolong I_mul_I; // 虚数单位的平方

struct complex {
	lolong real, imag;
	complex(lolong real = 0, lolong imag = 0): real(real), imag(imag) { }
};
inline bool operator == (complex x, complex y) {
	return x.real == y.real and x.imag == y.imag;
}
inline complex operator * (complex x, complex y) {
	return complex((x.real * y.real + I_mul_I * x.imag % mod * y.imag) % mod,
			(x.imag * y.real + x.real * y.imag) % mod);
}

complex power(complex x, int k) {
	complex res = 1;
	while(k) {
		if(k & 1) res = res * x;
		x = x * x;
		k >>= 1;
	}
	return res;
}

bool check_if_residue(int x) {
	return power(x, (mod - 1) >> 1) == 1;
}

void solve(int n, int p, int &x0, int &x1) {
	mod = p;

	lolong a = rand() % mod;
	while(!a or check_if_residue((a * a + mod - n) % mod))
		a = rand() % mod;
	I_mul_I = (a * a + mod - n) % mod;

	x0 = int(power(complex(a, 1), (mod + 1) >> 1).real);
	x1 = mod - x0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值