「学习笔记」二次剩余

说明:本文涉及变量除特殊说明外,全部属于整数集合。

前置芝士——二次剩余

二次剩余

当存在 x 使得 x2a(modp) ,那么我们称 a 是取模 p二次剩余(Quadratic residue)

对于一个奇素数 p ,定义它的二次剩余集合为 Fp ,并且对于其中每一个数 n 满足 x[0,p),xxn(modp)

二次剩余的个数

对于前面定义的集合,有 Fp∣=p+12 ,证明如下:

u2v2n(modp),uv ,那么

p(u2v2)p(uv)(u+v)

显然,若 p(uv) ,因为 p 为奇素数,那么 uv=p ,但这是不可能满足的。

即一定有 p(uv) ,那么就有 p(u+v) ,同理, u+v=p

即在 u,v[0,p) 的范围中,除 0 以外,其他的数两两对应。

那么个数就是 p12+1=p+12 ,证毕。

欧拉准则

用来判定一个非零数是否是二次剩余。

对于一个 d 满足 (d,p)=1,d>0 ,那么一定满足

dp12(modp)={1,dFp1,dFp

证明:

根据费马小定理,一定有

dp110(modp)(dp121)(dp12+1)0(modp)

x,x2d(modp) ,那么就有 dp12xp1(modp)

那么,就有满足 dFp 时,有 dp121(modp)

过程

现在我们要求一个 x 满足

x2a(modp)

用以下步骤可求得 x

  1. 随机一个 t ,满足 t2aFp
  2. ω=t2a ,则 x=(t+ω)p+12

证明过程需要几个小定理...

定理一

  • 内容:

(1)(a+b)pap+bp(modp)

  • 证明:

(a+b)p=i=0p(pi)aibpi

i0ip ,满足 (pi)0(modp)

定理二

  • 内容:

(2)ωpω(modp)

  • 证明:

因为 t2aFp ,所以满足

ωp1=(ω2)p12=(t2a)p121(modp)

定理三

  • 内容:

(3)(a+ω)paω(modp)

(1)(2) 显然。

证明

由前面的定理可知

x2=(t+ω)p+1=(t+ω)(t+ω)p(t+ω)(tω)(modp)=t2ω2=t2(t2a)=a

关于 ω

方程 x2a=0 在任何域中都有两个根(由勒让德定理),并且并且我们前面得到了这两个根都在模 p 的剩余系中,所以没有 ω

代码实现

代码处理的时候,只需要记录公式中 a+bω 的两个系数 ab 即可。

struct Z{
    uint x;
    Z(const uint _x=0):x(_x){}
    inline Z operator +(const Z &rhs)const{return x+rhs.x<MOD?x+rhs.x:x+rhs.x-MOD;}
    inline Z operator -(const Z &rhs)const{return x<rhs.x?x-rhs.x+MOD:x-rhs.x;}
    inline Z operator -()const{return x?MOD-x:0;}
    inline Z operator *(const Z &rhs)const{return (ull)x*rhs.x%MOD;}
    inline Z operator +=(const Z &rhs){return x=x+rhs.x<MOD?x+rhs.x:x+rhs.x-MOD,*this;}
    inline Z operator -=(const Z &rhs){return x=x<rhs.x?x-rhs.x+MOD:x-rhs.x,*this;}
    inline Z operator *=(const Z &rhs){return x=(ull)x*rhs.x%MOD,*this;}
    inline bool operator ==(const Z &rhs){return x==rhs.x;}
};
#define pzz pair<Z,Z>
inline pzz Mul(pzz x,pzz y,Z f){
//计算 a+bω 部分的 pzz 的乘法
    return mp(x.ft*y.ft+x.sd*y.sd*f,x.ft*y.sd+x.sd*y.ft);
}
inline Z Quadratic_residue(Z a){
//常数在模意义下的开根
    if(a.x<=1)return a.x;
    //使用欧拉定律判断有无解
    if(qkpow(a,(MOD-1)>>1).x!=1)return -1;
    Z x,f;
    //找到一个 x 使得 x^2 - a 不是二次剩余
    do x=(((ull)rand()<<15)^rand())%(a.x-1)+1;
    while(qkpow(f=x*x-a,(MOD-1)>>1)==Z(1));
    //初始变量
    pzz ans=mp(1,0),t=mp(x,1);
    //类似于快速幂
    for(uint i=(MOD+1)>>1;i>0;i>>=1,t=Mul(t,t,f))if(i&1)ans=Mul(ans,t,f);
    //返回较小根
    return Min(ans.ft.x,MOD-ans.ft.x);
}
posted @   Arextre  阅读(875)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示