二次剩余

二次剩余

PS:以下内容大概都是抄的。
大概是解这样一个方程:

[x^2 equiv n pmod p ]

 
这里讨论 (p) 为奇素数。

称非 (0)(n) 是二次剩余当 (n) 存在上述方程的解,反之成为非二次剩余。

二次剩余的数量为 (frac{p-1}{2}),原因是这样的:

1.若一个数 (n) 为二次剩余,设它有两组解,(x_1,x_2)
那么 (x_1^2 equiv x_2^2 equiv n),由平方差公式得到 ((x_1-x_2)(x_1+x_2) equiv 0)
那么 (x_1 equiv -x_2),因为 (p) 为奇素数,所以 (x_1 eq x_2)

2.若 (n) 为二次剩余,存在解 (x)(-x) 也为解。

欧拉准则

大概是判断一个数 (n) 是否是二次剩余。

做法是这样的,求 (n^{frac{p-1}{2}}),若为 (1) 则是,否则不是。

首先有这样一个事情,(n^{p-1} equiv 1),则 ((n^{frac{p-1}{2}})^2 equiv 1)
若其取值为 (1),可以令 (n=g^k),其中 (g) 为原根。
则有 (g^{kfrac{p-1}{2}} equiv 1),即 ((p-1)| kfrac{p-1}{2})
(2|k),所以 (n) 为二次剩余。

然后还有这样一个事情,若 (n) 为二次剩余,则存在 (x^2 equiv n)
(n^{frac{p-1}{2}}equiv x^{p-1} equiv 1)
所以 (n^{frac{p-1}{2}}equiv 1)(n) 是二次剩余是等价的条件。

故可以通过 (n^{frac{p-1}{2}})(1)(-1) 来判断 (n) 是否为二次剩余。

Cipolla

大概是对于 (n) 为二次剩余,找到 (x) 满足 (x^2 equiv n)

做法是这样的,首先找到一个 (a),满足 (a^2-n) 为非二次剩余,这个过程通过随机并检验即可实现。
定义一个类似虚部的东西 (i),满足 (i^2 equiv a^2-n)
然后有 ((a+i)^{p+1} equiv n)((a+i)^{frac{p+1}{2}}equiv x)

有这样一些事情:

1.((A+B)^p=A^p + B^p)
通过二项式定理展开为 (sum limits_{j=0}^{p} A^j B^{p-j} inom{p}{j})
其中 (inom{p}{j}) 中的质因子 (p) 只在 (j=0,j=p) 时不存在。

[egin{align*} i^p&=i(i^2)^{frac{p-1}{2}}\ &=i(a^2-n)^{frac{p-1}{2}}\ &=-i\ end{align*}]

这样的话考虑 ((a+i)^{p+1}) 这个东西。
可以展开为 ((a+i)^p(a+i)=(a^p+i^p)(a+i)=(a-i)(a+i)=a^2-i^2=n)
((a+i)^{frac{p+1}{2}})(-(a+i)^{frac{p+1}{2}}) 是两个解。

可以证明 ((a+i)^{frac{p+1}{2}}) 不存在虚部。
假设虚部存在,则有 ((x+yi)^2=n,y eq 0)
展开得 (x^2+y^2(a^2-n)-n=2xy)
其中左侧不含虚部,故 (y eq 0,x=0)
代入得到 (y^2i^2=n),显然无解,与假设不成立。
 
 

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int mod,n,a;
struct node{
	ll r,i;
	node(int r=0,int i=0):r(r),i(i){}
};
inline node operator * (const node &x,const node &y){
	return node((x.r*y.r%mod+x.i*y.i%mod*(1ll*a*a%mod-n+mod))%mod,(x.r*y.i+x.i*y.r)%mod);
}
inline ll qpow(ll x,int k,ll r=1){
	for(;k;k>>=1,x=x*x%mod) if(k&1) r=r*x%mod;
	return r;
}
inline node qpow(node x,int k,node r=node(1,0)){
	for(;k;k>>=1,x=x*x) if(k&1) r=r*x;
	return r;
}
bool check(int x){
	return qpow(x,mod-1>>1)==1;
}
int main(){
	int T; scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&mod);
		if(!n){ puts("0"); continue; }
		if(!check(n)){ puts("Hola!"); continue; }
		for(a=rand()%(mod-1)+1;check((1ll*a*a-n+mod)%mod);a=rand()%(mod-1)+1);
		int x=qpow(node(a,1),mod+1>>1).r,y=mod-x; if(y<x) swap(x,y);
		printf("%d %d
",x,y);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/skyh/p/13357396.html