同余意义下的运算法则与逆元、和二次剩余、和数论四大定理

同余:(这里只讲整数的同余)

10 10 除以 7 7 余数是 3 3 17 17 除以 7 7 余数也是 3 3 ,那么就称 10 10 17 17 在模 7 7 意义下同余,符号表示为    10 17    ( m o d    7 ) \;10\equiv 17 \; (mod \; 7)
 
然后,很容易得到一些性质:

  • 自反性: a a    ( m o d    m ) a\equiv a \; (mod \; m)
  • 对称性:若    a b    ( m o d    m ) \;a\equiv b \; (mod \; m) ,则    b a    ( m o d    m ) \;b\equiv a \; (mod \; m)
  • 传递性:若    a b    ( m o d    m ) \;a\equiv b \; (mod \; m) ,且    b c    ( m o d    m ) \;b\equiv c \; (mod \; m) ,则    a c    ( m o d    m ) \;a\equiv c \; (mod \; m)

这些都显然,不重要,重要的在后面。
 
 

同余意义下的加减乘:

为了简单阅读,加减乘就证明了,直接贴代码。
基于一个原理,先加减乘再取模和先取模再加减乘,结果是不影响的,需读者自己思考想清楚。
 
模p意义下的 a + b a+b :(ps:这里代码的计算答案均赋值给c)

c=(a+b)%p;

 
模p意义下的 a b a-b

c=(a%p-b%p+p)%p;

这里需要注意的是,%对负数的处理是,先对其绝对值取模,再将符号乘给它,所以我们要保证它结果为正数,需取模 p p 后加上模数 p p 再取模。
 
模p意义下的 a × b a\times b ;

c=a*b%p;

不管是什么运算都要注意不要溢出,比如a和b均大于模数p,且相乘会溢出的话,应该改写乘c=(a%p)*(b%p)%p,这个需自行判断。

 
 
 

模意义下的除法:

思考一个问题,先除再模和先模再除,结果是否一样?
答案显然是否,因为会有除不尽的情况。
所以引入了逆元
 
什么是逆元?
通俗来讲, a a 的逆元就是 a 1 a^{-1}
那么我们要除以 a a ,相当于只要乘上 a 1 a^{-1} 即可。
a 1 a^{-1} a a p p 意义下的逆元,那么就有 a a 1 1    ( m o d    p ) aa^{-1}\equiv 1 \; (mod \; p)
 
逆元不是一定存在的!!
p p 意义下, a a 的逆元存在的充要条件是 [ g c d ( a , p ) = 1 ] [gcd(a,p)=1]
举个例子, 2 2 乘上 w h a t what 在模 4 4 意义下等于 1 1 ,不存在吧,这种情况就是没有逆元。
ps:准确来讲不应该是等于,而是与 1 1 同余,当模数为 1 1 时,任何数都与 1 1 同余
 
那么现在考虑怎么求逆元:
先上费马小定理: a p 1 1    ( m o d    p ) a^{p-1}\equiv 1 \; (mod \; p)
成立条件: p p 为素数,且 [ g c d ( a , p ) = 1 ] [gcd(a,p)=1]
然后就有了: a 1 a p 2    ( m o d    p ) a^{-1}\equiv a^{p-2} \; (mod \; p)
 
所以我们现在只要求 a p 2 a^{p-2} 就好了(前提要满足费马小定理),上模板:

//才疏学浅,不敢用位运算
ll fpow(ll a,ll n,ll mod)//快速幂
{
    ll sum=1,base=a%mod;
    while(n!=0)
    {
        if(n%2)sum=sum*base%mod;
        base=base*base%mod;
        n/=2;
    }
    return sum;
}

ll inv(ll a,ll mod)//逆元
{
    return fpow(a,mod-2,mod);
}

 
 
那么现在要研究不满足费马小定理的逆元求法。
当不满足    [ g c d ( a , p ) = 1 ]    \;[gcd(a,p)=1]\; 那么逆元不存在,所以我们只考虑 p p 不是素数。
要求方程    a x 1    ( m o d    p ) \;ax\equiv 1 \; (mod \; p) 的解。
等效成求    a x + p y = 1 \;ax+py=1 的解,然后就有拓展欧几里得求解。
传送门

 
 
学完了加减乘除,就可以轻松实战一下了。
求解方程: a x b c    ( m o d    p ) ax-b\equiv c \; (mod \; p)
那么只要两边同时加上 b b ,再同乘 a 1 a^{-1} 即可。
就得到了: x a 1 ( b + c )    ( m o d    p ) x\equiv a^{-1}(b+c) \; (mod \; p)
是不是很快就上手了。
 
 

再介绍一下快速乘:

先来一个场景,模数 p = 1 e 9 + 7 p=1e9+7 ,要算 a × b ( m o d    p ) a\times b(mod\; p) ,那么取模后的 a a b b 都不会大于 1 e 9 + 7 1e9+7 ,两个乘起来也不会爆long long,这时候开long long 就可以了。
那么如果模数 p = 1 e 18 + 7 p=1e18+7 ,那么取模后相乘仍有爆long long的危险,这时候要怎么办,这里介绍两种办法。
 
一种是江湖邪术:__int128
顾名思义,就是128位存储的整数,大到 2 127 1 2^{127}-1
不过它只能在Linux环境下能够使用,在大部分oj上都能用,本地编译器无法运行,可以用long long 过了样例再换掉提交。
它自带了各种基本运算,以及强制转换符号,但是没有特定的读入和输出,所以可以用long long读进来后,强制转化成(__int128)再进行运算。
或者手写一个读入和输出,原理就是用getchar()和putchar()以及char类型和__int128型的强制转换。
__int128读入读出模板:

ll read()
{
   int X=0,w=0; char ch=0;
   while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
   while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
   return w?-X:X;
}
void print(ll x)
{    
   if(x<0){putchar('-');x=-x;}
   if(x>9) print(x/10);
   putchar(x%10+'0');
}

同样可以适用于long long的读入,使用时只要把typedef long long ll;改成typedef __int128 ll;即可。
 
 
第二种办法就是快速乘:
原理和快速幂一样,只是乘法运算变成了加法运算,复杂度是log。
直接上o(log)的模板:

ll fmul(ll a,ll b,ll mod)
{
    ll sum=0,base=(a%mod+mod)%mod;
    while(b)
    {
        if(b%2)sum=(sum+base)%mod;
        base=(base+base)%mod;
        b/=2;
    }
    return sum;
}

还有一种o(1)的写法,因为C++内有128位的long double型,可以利用它优化成o(1)。
o(1)的快速乘模板:

ll fmul(ll x,ll y,ll mod)
{
	ll tmp=(x*y-(ll)((long double)x/mod*y+1.0e-8)*mod);
	return tmp<0?tmp+mod:tmp;
}

注意,o(1)快速乘因为原理是利用128位的long double,所以将__int128和其混用,并不能改善爆__int128的问题。
但是用o(log)的快速乘和__int128混用却可以解决模数大至__int128的乘法问题。

 
 
 

模意义下的开根号(二次剩余):

什么是剩余
多出来的;遗留下来的:剩余物资。出自《现代汉语词典》
剩余就是某个物体分成若干份等量或不等量的物体,使用后还有没使用的,没使用的对于已使用过的就算是剩余。
总之就是余数。
剩余类: 设模为n,则根据余数可将所有的整数分为n类,把所有与整数a模n同余的整数构成的集合叫做模n的一个剩余类
剩余系: 对于特定的 n n ,一个整数集合里所有数对其取模后剩下来的集合。
完全剩余系: 从模n的每个剩余类中各取一个数,得到一个由n个数组成的集合,叫做模n的一个完全剩余系。差不多就是 0 0 n 1 n-1

什么是二次剩余
对于二次同余方程    x 2 n    ( m o d    p ) \;x^{2}\equiv n \; (mod \; p) ,若 [ g c d ( n , p ) = 1 ] [gcd(n,p)=1] ,且存在一个 x x 满足该方程,则称 n n 是模 p p 意义下的二次剩余,若无解,则称n为p的二次非剩余。

 
欧拉判别法:(书上还写了一个高斯引理,可以判定与p互素的整数是否是二次剩余,看不进去了)
对于 p p 奇素数,我们有办法来判断 n n 是否为 p p 的二次剩余。
我们假设存在解 x 0 x_{0} 使得    x 0 2 n    ( m o d    p ) \;x_{0}^{2}\equiv n \; (mod \; p)
式子转化后变成 x 0 p 1 n p 1 2 ( m o d    p ) x_{0}^{p-1}\equiv n^{\frac{p-1}{2}} (mod\; p)
根据费马小定理,就有了 n p 1 2 1 ( m o d    p ) n^{\frac{p-1}{2}} \equiv 1 (mod\; p) ,此时方程有解。
( n p 1 2 1 ) ( n p 1 2 + 1 ) 0 ( m o d    p ) (n^{\frac{p-1}{2}}-1)(n^{\frac{p-1}{2}}+1) \equiv 0 (mod\; p) ,得到 n p 1 2 ± 1 ( m o d    p ) n^{\frac{p-1}{2}} \equiv \pm 1 (mod\; p)
另外只需要证明等于 1 -1 时无解即可,此处证明略。
 
我们引入勒让德符号 ( n p ) n p 1 2 ( m o d    p ) (\frac{n}{p}) \equiv n^{\frac{p-1}{2}} (mod\; p) ,其中 n n 不能被 p p 整除,整除时答案即为 0 0
有个结论,对于 p p 为奇素数,若 ( n p ) = 1 (\frac{n}{p})=1 ,则 n n p p 的二次剩余,反之若 ( n p ) = 1 (\frac{n}{p})=-1 ,则 n n p p 的非二次剩余。
还有三个定理:

  • a b ( m o d    p ) a \equiv b(mod\;p) ,则 ( a p ) = ( b p ) (\frac{a}{p})=(\frac{b}{p})
  • ( a p ) ( b p ) = ( a b p ) (\frac{a}{p})(\frac{b}{p})=(\frac{ab}{p})
  • ( a 2 p ) = 1 (\frac{a^{2}}{p})=1

定理自行证明。
 
还有一个更流批 一点的定理,对于奇素数 p p ,方程    x 2 n ( m o d    p ) \;x^{2}\equiv n(mod\; p) ,共有 p 1 2 + 1 \frac{p-1}{2}+1 n n 使得方程有解,换句话说有一半的二次剩余。
 
 
现在问题来了!!!传送门
如何求解方程    x 2 n ( m o d    p ) \;x^{2}\equiv n(mod\; p) ,其中 p p 为奇素数。
偶素数其实就一个 2 2 嘛,特判一下就好了,就可以知道 p p 是素数的了。
这显然可以用BSGS算法来求解。
我们考虑比 o ( p ) o(\sqrt{p}) 快的做法,但是仅限于 p p 为奇素数的情况,如果没有这个条件,还是老老实实地BSGS吧。
 
考虑 o ( l o g ) o(log) 的做法:Cipolla算法
首先用欧拉判别法判断是否有解, n = 0 n=0 的情况特殊考虑,没解就写个无解然后下一题。
然后有非 0 0 解的话,则一定是两个解,互为相反数,即 x 0 x_{0} p x 0 p-x_{0}
我们先随机找一个 a a ,使得 a 2 n a^{2}-n p p 的非二次剩余,因为非二次剩余占一半,所以期望大约两次就可以找到了。
于是乎, x ( a + a 2 n ) p + 1 2 ( m o d    p ) x\equiv (a+\sqrt{a^{2}-n})^{\frac{p+1}{2}}(mod\; p) 就是方程的解了!!
????????!
好吧我们来证明一下。
我们令 i = a 2 n i=\sqrt{a^{2}-n} ,因为 a 2 n a^{2}-n 是二次非剩余,所以 i i 的值是不存在的,我们将数域扩张,引入类似复数的东西。
现在我们有了下面的东西,

  • i 2 a 2 n i^{2}\equiv a^{2}-n
  • i p 1 1 i^{p-1}\equiv -1 (这条好像没用到)
  • ( A + B ) p A p + B p (A+B)^{p}\equiv A^{p}+B^{p}

第一个根据定义。第二个是利用二次非剩余的欧拉判别性质。第三个是用二项式定理展开后,中间项都模 p p 消除了。
然后开始操作起来:
( a + i ) p + 1 ( a p + i p ) ( a + i ) ( a i ) ( a + i ) a 2 i 2 n (a+i)^{p+1}\equiv(a^{p}+i^{p})(a+i)\equiv(a-i)(a+i)\equiv a^{2}-i^{2}\equiv n
所以 ( a + i ) p + 1 2 n 1 2 (a+i)^{\frac{p+1}{2}}\equiv n^{\frac{1}{2}}
并且可以自证左边展开后是一个实数,否则若仍有虚部,则无法解决问题。
就证好了。
然后只要写一个复数类,再套快速幂就可以了。
 
上代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

typedef struct cp
{
    ll r,i;
    cp(const int&x,const int &y){r=x,i=y;}
}cp;

cp mul(cp a,cp b,ll p,ll w)
{
    return cp((a.r*b.r%p+a.i*b.i%p*w%p)%p,(a.r*b.i%p+a.i*b.r%p)%p);
}

cp cp_fpow(cp a,ll n,ll p,ll w)
{
    cp sum(1,0),base=a;
    while(n!=0)
    {
        if(n%2)sum=mul(sum,base,p,w);
        base=mul(base,base,p,w);
        n/=2;
    }
    return sum;
}

ll fpow(ll a,ll n,ll p)
{
    ll sum=1,base=a%p;
    while(n!=0)
    {
        if(n%2)sum=sum*base%p;
        base=base*base%p;
        n/=2;
    }
    return sum;
}

bool OulaPb(ll n,ll p)//欧拉判别
{
    if(fpow(n,(p-1)/2,p)==p-1)return false;
    else return true;
}

ll Cipolla(ll n,ll p)//无解返回-1
{
    n=(n%p+p)%p;
    if(n==0)return 0;
    if(p==2&&n==1)return 1;
    if(!OulaPb(n,p))return -1;
    ll a=rand()%p*rand()%p;
    while(OulaPb((a*a%p-n%p+p)%p,p))a=rand()%p*rand()%p;
    cp ans=cp_fpow(cp(a,1),(p+1)/2,p,(a*a%p-n%p+p)%p);
    return ans.r%p;
}

int main()
{
    srand(time(0));
    ll T;
    scanf("%lld",&T);
    while(T--)
    {
        ll n,p;
        scanf("%lld%lld",&n,&p);
        ll ans=Cipolla(n,p);
        if(ans==-1)printf("Hola!\n");
        else if(ans==(p-ans)%p)printf("%lld\n",ans);
        else printf("%lld %lld\n",min(ans,p-ans),max(ans,p-ans));
    }
    return 0;
}

 
关于 p p 不为素数的,书上有介绍一种方法,将模数因子分解,成若干个奇素数,求出若干个方程的解,然后用中国剩余定理合并。
比如 x 2 860 ( m o d    11021 ) x^{2}\equiv 860(mod \;11021)
其中 11021 = 103 × 107 11021=103\times 107
根据 a ( m o d    d ) = a ( m o d    n ) ( m o d    d ) a(mod\;d)=a(mod\; n)(mod \;d) [ d n ] [d|n] 成立。
可以得到 x 2 860 36 ( m o d    103 ) x^{2}\equiv 860\equiv 36(mod\; 103) ,和 x 2 860 4 ( m o d    107 ) x^{2}\equiv 860\equiv 4(mod\; 107)
每个方程分别有两个解,然后用中国剩余定理合并,得到四个解。
具体操作就不说了。
 
 
 
 
 

最后再扔一个数论四大定理:

  • 威尔逊定理:p可整除(p-1)!+1是p为质数的充要条件。
  • 欧拉定理:若    g c d ( a , n ) = 1 \;gcd(a,n)=1 ,则 a φ ( n ) 1 ( m o d    n ) a^{\varphi (n)}\equiv 1(mod \; n) ,其中 φ ( n ) \varphi (n) 为欧拉函数。
  • 孙子定理:用来求解模数两两互质的同余方程组,传送门
  • 费马小定理:若 p p 为素数,且 g c d ( a , p ) = 1 gcd(a,p)=1 ,则 a p 1 1 ( m o d    p ) a^{p-1}\equiv 1(mod \; p) 。(其实 p p 为素数时, φ ( n ) = p 1 \varphi (n)=p-1 )。
发布了6 篇原创文章 · 获赞 4 · 访问量 1910

猜你喜欢

转载自blog.csdn.net/weixin_43785386/article/details/104086765