程序笔记   发布时间:2022-07-21  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了二次剩余大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

(text{Problem}:)【模板】二次剩余

(text{Solution}:)

本文介绍 (text{Cipolla}) 算法,下面默认模数 (p) 为奇质数。

定义:

非零数 (n) 是模 (p) 的二次剩余,当且仅当模 (p) 意义下方程 (x^{2}equiv n) 有解。无解情况对应的 (n) 称为非二次剩余。

解的数量:

(p) 意义下的二次剩余有 (dfrac{p-1}{2}) 个,非二次剩余有 (dfrac{p-1}{2}) 个。

证明:

显然,如果模 (p) 意义下 (x^{2}equiv n) 有解,那么当 (xin[1,dfrac{p-1}{2}]) 时可以取到所有 (n)。现在只需证明当 (xin[1,dfrac{p-1}{2}]) 时,(x^{2}text{ mod }p) 互不相同。

假设存在 (x,yin[1,dfrac{p-1}{2}],xnot=y) 满足 (x^{2}equiv y^{2}pmod p),则有 ((x+y)(x-y)equiv 0pmod p)。而 (x-ynot=0,x+ynot=0),故假设不成立,原命题得证。


欧拉判别准则:

(n) 是模 (p) 意义下的二次剩余,当且仅当 (n^{frac{p-1}{2}}equiv1pmod p)

(n) 是模 (p) 意义下的非二次剩余,当且仅当 (n^{frac{p-1}{2}}equiv -1pmod p)

证明:

由费马小定理,有 (n^{p-1}equiv 1pmod p),即 ((n^{frac{p-1}{2}}-1)(n^{frac{p-1}{2}}+1)equiv 0 pmod p)。故有:

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

(n) 是模 (p) 意义下的二次剩余,有 (n^{frac{p-1}{2}}equiv (x^{2})^{frac{p-1}{2}}equiv x^{p-1}equiv 1pmod p)

(n^{frac{p-1}{2}}equiv 1pmod p),设 (a)(p) 的一个原根,则存在 (kin[1,p-1]),使得 (nequiv a^{k}pmod p),有:

[(a^{k})^{frac{p-1}{2}}equiv 1pmod p ]

(p-1mid dfrac{k(p-1)}{2}),得出 (k) 为偶数。令 (j=dfrac{k}{2},jin z),满足 ((a^{j})^2equiv npmod p),即 (n) 是模 (p) 意义下的二次剩余。

由以上证明,(n) 是模 (p) 意义下的二次剩余,与 (n^{frac{p-1}{2}}equiv 1pmod p) 等价。原命题得证。


(text{Cipolla}) 算法:

随机一个数 (a) 满足 (a^{2}-n) 是模 (p) 意义下的非二次剩余。期望约 (2) 次即可找到满足条件的 (a)

定义 (i^{2}equiv a^{2}-npmod p),使得所有数可以用 (A+Bi) 的形式表达(可以借助复数域理解)。

引理 (1)

[i^{p}equiv -ipmod p ]

证明:

[begin{aligneD} i^{p}&equiv (i^{2})^{frac{p-1}{2}}cdot i\ &equiv(a^{2}-n)^{frac{p-1}{2}}cdot i\ &equiv -ipmod p end{aligneD} ]

引理 (2)

[(a+b)^p=a^p+b^ppmod p ]

证明:

[begin{aligneD} (a+b)^p&=sumlimits_{i=0}^{p}binom{p}{i}a^ib^{p-i}\ &=sumlimits_{i=0}^{p}cfrac{a^ib^{p-i}(p-1)!cdot p}{i!(p-i)!}\ &=a^{p}+b^{p}pmod p end{aligneD} ]

现在开始推导 (n) 的变换,有:

[begin{aligneD} n&equiv a^2-i^2\ &equiv (a+i)(a-i)\ &equiv (a+i)(a+i^p)\ &equiv (a+i)(a^p+i^p)\ &equiv (a+i)^{p+1} end{aligneD} ]

由于 (p+1) 是偶数,所以存在 (X),满足 (x^2equiv nequiv (a+i)^{p+1}pmod p)。利用快速幂即可在 (O(log p)) 的时间复杂度内求解。

(text{CodE}:)

#include <bits/stdc++.h>
#pragma GCC optimize(3)
//#definE int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_BACk
#define eb emplace_BACk
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std;
inlinE int read()
{
	int s=0, w=1; ri char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
	return s*w;
}
int n,P,I2;
inlinE int ksc(int x,int p) { int res=1; for(;p;p>>=1, x=1ll*x*x%p) if(p&1) res=1ll*res*x%P; return res; }
struct Node
{
	int x,y;
};
inline Node operator + (Node a,Node b) { return (NodE){a.x+b.x,a.y+b.y}; }
inline Node operator - (Node a,Node b) { return (NodE){a.x-b.x,a.y-b.y}; }
inline Node operator * (Node a,Node b) { return (NodE){(1ll*a.x*b.x%P+1ll*a.y*b.y%P*I2%p)%P,(1ll*a.x*b.y%P+1ll*a.y*b.x%p)%P}; }
inline bool check(int X)
{
	return ksc(x,(P-1)/2)==1;
}
inlinE int Rand()
{
	int w=1ll*rand()*rand()%P;
	w+=rand()-rand();
	w=(w%P+p)%P;
	return w;
}
inline Node KSC(Node x,int p)
{
	Node res=(NodE){1,0};
	for(;p;p>>=1, x=x*X) if(p&1) res=res*x;
	return res;
}
inline void Solve()
{
	n%=P;
	int a=0;
	if(!n) { puts("0"); return; }
	if(!check(n)) { puts("Hola!"); return; }
	while(check((1ll*a*a%P-n+p)%p)) a=Rand();
	I2=(1ll*a*a%P-n+p)%P;
	int X1=KSC((NodE){a,1},(P+1)/2).x;
	int X2=P-X1;
	if(X1>x2) swap(X1,x2);
	if(X1==x2) printf("%dn",X1);
	else printf("%d %dn",X1,x2);
}
signed main()
{
	srand(time(NULL));
	for(ri int T=read();T;T--)
	{
		n=read(), P=read();
		Solve();
	}
	return 0;
}

大佬总结

以上是大佬教程为你收集整理的二次剩余全部内容,希望文章能够帮你解决二次剩余所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。