二次剩余Cipolla算法

欧拉准则

欧拉准则:设p为奇素数,则
a p − 1 2 ≡ ( a p ) ( m o d   p ) a^{\frac{p-1}{2}}\equiv (\frac{a}{p})(mod\ p) a2p1(pa)(mod p)
其中 a p \frac{a}{p} pa为勒让德符号

证明:假设a为二次剩余,如 a ≡ b 2 ( m o d   p ) a\equiv b^2(mod\ p) ab2(mod p),由费马小定理可知
a ( p − 1 ) / 2 ≡ ( b 2 ) ( p − 1 ) / 2 ≡ 1 ( m o d   p ) a^{(p-1)/2}\equiv (b^2)^{(p-1)/2}\equiv 1(mod\ p) a(p1)/2(b2)(p1)/21(mod p)
∵ ( a p ) = 1 \because (\frac{a}{p})=1 (pa)=1所以上式也可以写作 a ( p − 1 ) / 2 ≡ ( a p ) ( m o d   p ) a^{(p-1)/2}\equiv (\frac{a}{p})(mod\ p) a(p1)/2(pa)(mod p)由此当p为二次剩余时,欧拉准则成立

在证明二次非剩余也满足欧拉准则前,我们需要先观察上式子,据上面的式子,我们可以发现每个二次剩余都是同余方程 x ( p − 1 ) / 2 − 1 ≡ ( m o d   p ) x^{(p-1)/2}-1\equiv(mod\ p) x(p1)/21(mod p)的解,而之前已经证过模p多项式根定理,这个方程至多有 p − 1 2 \frac{p-1}{2} 2p1个解,而p有 p − 1 2 \frac{p-1}{2} 2p1这样我们就会发现,凡二次剩余均满足上式,且满足上式的均是二次剩余.有了这个结论之后我们再对非二次剩余满足欧拉准则进行证明.

假设a为二次非剩余,由费马小定理,有 a p − 1 ≡ 1 ( m o d   p ) a^{p-1}\equiv1(mod\ p) ap11(mod p),所以
0 ≡ a p − 1 − 1 ≡ ( a ( p − 1 ) / 2 − 1 ) ( a ( p − 1 ) / 2 + 1 ) ( m o d   p ) 0\equiv a^{p-1}-1\equiv(a^{(p-1)/2}-1)(a^{(p-1)/2}+1)(mod\ p) 0ap11(a(p1)/21)(a(p1)/2+1)(mod p)
由于a为二次非剩余,因此 p ̸ ∣ ( a ( p − 1 ) / 2 − 1 ) p\not \mid(a^{(p-1)/2}-1) p̸(a(p1)/21) a ( p − 1 ) / 2 ≡ − 1 ( m o d   p ) a^{(p-1)/2}\equiv -1(mod\ p) a(p1)/21(mod p)由于a为二次非剩余时 ( a q ) = − 1 (\frac{a}{q})=-1 (qa)=1因此 a ( p − 1 ) / 2 ≡ ( a q ) ( m o d   p ) a^{(p-1)/2}\equiv(\frac{a}{q})(mod\ p) a(p1)/2(qa)(mod p)再a为二次非剩余时也成立

二次剩余Cipolla算法

在介绍Cipolla算法之前我们需要先引入一些定理与引理

算法的用处是解 x 2 ≡ n ( m o d   p ) x^2\equiv n(mod\ p) x2n(mod p)因此下面所称的方程均是左式

下面的引理或者定理中,所有的p均为奇素数

引理 n ( p − 1 ) / 2 ≡ ± 1 ( m o d   p ) n^{(p-1)/2}\equiv ±1 (mod\ p) n(p1)/2±1(mod p)

证明:即欧拉准则,一个数或为二次剩余数,或为二次非剩余数.

引理:方程 x 2 ≡ n ( m o d   p ) ​ x^2\equiv n(mod\ p)​ x2n(mod p)有解当且仅当 n ( p − 1 ) / 2 ≡ 1 ( m o d   p ) ​ n^{(p-1)/2}\equiv 1(mod\ p)​ n(p1)/21(mod p)

证明:方程有解则n为二次剩余

定理:设a满足 ω = a 2 − n \omega=a^2-n ω=a2n不是模p的二次剩余,即 x 2 ≡ ω ( m o d   p ) x^2\equiv \omega(mod\ p) x2ω(mod p)无解,则 x ≡ ( a + ω ) p + 1 2 x\equiv (a+\sqrt\omega)^{\frac{p+1}{2}} x(a+ω )2p+1是方程的解

证明:首先处理 ( a + ω ) p ≡ a p + C p 1 ω a p − 1 + . . . C p p − 1 ω p − 1 a + ω p ( m o d   p ) (a+\sqrt\omega)^p\equiv a^p+C_p^1\sqrt\omega a^{p-1}+...C_p^{p-1}\sqrt\omega ^{p-1}a+\sqrt\omega^p(mod\ p) (a+ω )pap+Cp1ω ap1+...Cpp1ω p1a+ω p(mod p)由于 p ∣ C n i p\mid C_n^i pCni故原式可以转化为 ( a + ω ) p ≡ a p + ω p − 1 2 ω (a+\sqrt\omega)^p\equiv a^p+\omega^{\frac{p-1}{2}}\sqrt\omega (a+ω )pap+ω2p1ω ω \omega ω为二次非剩余可知 ω p − 1 2 ≡ − 1 ( m o d   p ) \omega^{\frac{p-1}{2}}\equiv -1(mod\ p) ω2p11(mod p),和费马小定理 a p ≡ a ( m o d   p ) a^p\equiv a(mod\ p) apa(mod p)可知原式可以化为 ( a + ω ) p ≡ a + ω ( m o d   p ) (a+\sqrt\omega)^p\equiv a+\sqrt\omega(mod\ p) (a+ω )pa+ω (mod p)故有

x 2 = ( a + ω ) p + 1 ≡ ( a + ω ) ⋅ ( a − ω ) x^2= (a+\sqrt\omega)^{p+1}\equiv (a+\sqrt\omega)\cdot(a-\sqrt\omega) x2=(a+ω )p+1(a+ω )(aω )

                                ≡ a 2 − ω ​ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \equiv a^2-\omega​                                a2ω

                                ≡ n ( m o d   p ) ​ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \equiv n(mod\ p)​                                n(mod p)

得证.
例题http://acm.timus.ru/problem.aspx?space=1&num=1132
Cipolla算法裸题

Cipolla算法C++实现

#include
#include
using namespace std;
typedef long long LL;
struct pii{
	int x,y;
	pii(int x=0,int y=0):x(x),y(y){}
};
LL w;
pii multi(pii a,pii b,LL p)
{
	return pii((a.x*b.x%p+a.y*b.y%p*w%p)%p,(a.x*b.y%p+a.y*b.x%p)%p);
}
LL quick_pow_D(pii a,LL b,LL p)
{
	pii ans(1,0);
	while(b)
	{
		if(b&1) ans=multi(ans,a,p),b--;
		a=multi(a,a,p);
		b>>=1;
	}
	return ans.x;
}
LL quick_pow(LL a,LL b,LL p)
{
	LL ans=1;a%=p;
	while(b)
	{
		if(b&1) ans=ans*a%p;
		a=a*a%p;
		b>>=1;
	}
	return ans;
}
int check(LL n,LL p)
{
	return quick_pow(n,(p-1)/2,p);
}
LL mod(LL a,LL n)
{
	a%=n;
	if(a<0) return a+n;
	return a;
}
void solve(int b,int p)
{
	LL a,t;
	while(1)
	{
		a=rand()%p;
		t=a*a-b;
		w=mod(t,p);
		if(check(w,p)==p-1) break;
	}
	pii temp(a,1);
	int ans=quick_pow_D(temp,(p+1)/2,p);
	int ans2=p-ans;
	if(ans>ans2) swap(ans,ans2);
	printf("%d %d\n",ans,ans2);
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a,n;
		scanf("%d%d",&a,&n);a%=n;
		if(n!=2&&check(a,n)==n-1) printf("No root\n");
		else if(n==2)
		{
			if(a==1) printf("1\n");
			else printf("No root\n");
		}
		else
		{
			solve(a,n);	
		}
	}
}

你可能感兴趣的:(数论)