- admin 的博客
二次剩余小记
- @ 2026-7-10 8:45:16
二次剩余小记 更新于 2026/7/10 08:43:43 作者
command_block
P.S 本文只探讨模数是奇质数的情况。
目标/定义
求解 这个方程组。
如果方程有解,则称是模的二次剩余,否则称作二次非剩余。
(下面默认在模的剩余系下讨论)
欧拉判别法
给定,如何判定 是否有解呢?
$c^{\large{\frac{p-1}{2}}}=\begin{cases}\ \ \ 1\rightarrow\text{有解}\\ -1\rightarrow\text{无解}\end{cases}$
- 引理 :
由费马小定理得,可得
- 引理 不是二次剩余
采用反证法,假设有,可得
与费马小定理矛盾,原命题得证。
- 引理 不是二次剩余
假设有,但是不是同余系里面的数,可以视为虚数,只满足这一条性质。
由于不是同余系内的数,必定不满足费马小定理,可得,那么只能是了。
(费马小定理对且只对同余系生效)
综上三个引理可以逻辑推导出欧拉判别法,证毕。
二次剩余的个数
模下的二次剩余恰有个。
- 引理 :
显然成立,由此可得,如果有解,则必然有两解。
任选两个数,且
那么,即,
显然不成立,那么,即。
那么,对于一个,和它平方相同的有且仅有另外一个。
总共有个不同的平方,那么二次剩余,二次非剩余各有个。
Cipolla算法
现在真正来求解
首先判定方程是否无解(如果则输出0)
如果有解,我们任意找一个,使得是二次非剩余。
方法:不断随机然后判定。根据上文内容,期望随机次数次。
设,注意不是同余系里面的数,可以理解为虚数,只有这一条性质。
我们扩系,把每个数变成的形式,重载运算。容易证明扩系之后仍然是环。
P.S 设
$(x_1+y_1w)*(x_2+y_2w)=(x_1x_2+y_1y_2s)+(x_1y_2+x_2y_1)w$
那么,所以
-
引理
二项式展开可得 : $(a+b)^p=\sum\limits_{i=0}^p\dbinom{i}{p}a^{i}b^{p-i}$
由卢卡斯定理 : $\dbinom{i}{p}=\dbinom{i/p}{p/p}\dbinom{i\%p}{p\%p}=\dbinom{i/p}{1}\dbinom{i\%p}{0}$
这个式子只在或者的时候值为1,否则为0.
所以
-
引理
由欧拉判别法,,证毕。
那么 :
正确性得证。
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
int mod,c;
ll powM(ll a,int t)
{
ll ans=1;
while(t){
if (t&1)ans=ans*a%mod;
a=a*a%mod;
t>>=1;
}return ans;
}
ll sav;
struct Mcp
{
ll x,y;
Mcp operator * (Mcp const& B) const
{return (Mcp){(x*B.x+y*B.y%mod*sav)%mod,(x*B.y+y*B.x)%mod};}
};
Mcp powM(Mcp a,int t)
{
Mcp ans=(Mcp){1,0};
while(t){
if (t&1)ans=ans*a;
a=a*a;
t>>=1;
}return ans;
}
void solve()
{
scanf("%d%d",&c,&mod);
if (c==0)
{puts("0");return ;}
if (powM(c,(mod-1)/2)==mod-1)
{puts("Hola!");return ;}
int a;
while(1){
a=(rand()%(mod-1)+mod)%(mod-1)+1;
sav=(1ll*a*a-c+mod)%mod;
if (powM(sav,(mod-1)/2)==mod-1){
Mcp W=(Mcp){a,1};
W=powM(W,(mod+1)/2);
ll x1=W.x,x2=mod-W.x;
if (x1>x2)swap(x1,x2);
printf("%lld %lld\n",x1,x2);
break;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)solve();
return 0;
}