模素数平方根

开始开凿石头之前,你应该先带好必要工具

背包:二次剩余的概念,二次剩余的欧拉判别条件,费马小定理

开凿之前,我们先观摩一下石头,我们打几张张 i^pow%mod的表,横轴为i,纵轴为pow

mod7(7=2*3+1):

pow=0: 1 1 1 1 1 1   
pow=1: 1 2 3 4 5 6
pow=2: 1 4 2 2 4 1   //全体模7二次剩余
pow=3: 1 1 6 1 6 6   //欧拉判别条件的体现
pow=4: 1 2 4 4 2 1   //全体四次剩余,发现与二次剩余一样,意味着什么?
pow=5: 1 4 5 2 3 6
pow=6: 1 1 1 1 1 1   //费马小定理,欧拉定理的体现

mod13(13=2*2*3+1):

pow=0: 1 1 1 1 1 1 1 1 1 1 1 1
pow=1: 1 2 3 4 5 6 7 8 9 10 11 12
pow=2: 1 4 9 3 12 10 10 12 3 9 4 1   //全体模13二次剩余
pow=3: 1 8 1 12 8 8 5 5 1 12 5 12 
pow=4: 1 3 3 9 1 9 9 1 9 3 3 1    //四次剩余,比二次剩余少掉了4 10 12,我们注意一下
pow=5: 1 6 9 10 5 2 11 8 3 4 7 12
pow=6: 1 12 1 1 12 12 12 12 1 1 12 1   //欧拉判别条件的体现
pow=7: 1 11 3 4 8 7 6 5 9 10 2 12
pow=8: 1 9 9 3 1 3 3 1 3 9 9 1   //八次剩余,和四次剩余一样,又意味着什么呢?
pow=9: 1 5 1 12 5 5 8 8 1 12 8 12
pow=10: 1 10 3 9 12 4 4 12 9 3 10 1
pow=11: 1 7 9 10 8 11 2 5 3 4 6 12
pow=12: 1 1 1 1 1 1 1 1 1 1 1 1  //费马小定理的体现

mod17(17=2*2*2*2+1):

pow=0: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
pow=1: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
pow=2: 1 4 9 16 8 2 15 13 13 15 2 8 16 9 4 1   //全体二次剩余
pow=3: 1 8 10 13 6 12 3 2 15 14 5 11 4 7 9 16
pow=4: 1 16 13 1 13 4 4 16 16 4 4 13 1 13 16 1   //四次剩余,比二次剩余少了9 8 2 15
pow=5: 1 15 5 4 14 7 11 9 8 6 10 3 13 12 2 16
pow=6: 1 13 15 16 2 8 9 4 4 9 8 2 16 15 13 1
pow=7: 1 9 11 13 10 14 12 15 2 5 3 7 4 6 8 16
pow=8: 1 1 16 1 16 16 16 1 1 16 16 16 1 16 1 1   //八次剩余,比四次剩余少了4 13
pow=9: 1 2 14 4 12 11 10 8 9 7 6 5 13 3 15 16
pow=10: 1 4 8 16 9 15 2 13 13 2 15 9 16 8 4 1
pow=11: 1 8 7 13 11 5 14 2 15 3 12 6 4 10 9 16
pow=12: 1 16 4 1 4 13 13 16 16 13 13 4 1 4 16 1
pow=13: 1 15 12 4 3 10 6 9 8 11 7 14 13 5 2 16
pow=14: 1 13 2 16 15 9 8 4 4 8 9 15 16 2 13 1
pow=15: 1 9 6 13 7 3 5 15 2 12 14 10 4 11 8 16
pow=16: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1  //十六次剩余,比八次剩余少了15

我们从上述观察中总结出以下规律:

a. 2^(k+1)次剩余一定是2^k次剩余(四次剩余一定是二次剩余,八次剩余一定是四次剩余……这是显然的)

b.  存在一个临界k使得2^k次剩余的集合等于2^(k+1)次剩余的集合(设S是一个交换群,那么S*S是S的子群,(S*S)*(S*S)是(S*S)的子群也是S的子群,S的2^(k+1)次方是S的2^k次方的子群,单调有界)

且p=2^k*r+1(r为奇数)手敲证明过于痛苦,读者若是对数学兴趣浓厚想必可以自证,如果只是想学会开方算法,可不求甚解。

c. 两个2^(k+1)次剩余的乘积是2^(k+1)次剩余(类比两个二次剩余的乘积是二次剩余)

d. 两个2^k次剩余中非2^(k+1)次剩余的乘积是2^(k+1)次剩余(类比两个非二次剩余的乘积是二次剩余)

上述规律均能得以证明,我们先来应用:

欲计算a的模p平方根(下面的运算都在模p意义下)

 

1.改写p=2^k*r+1(r为奇数),即p-1=2^k*r(r为奇数)

此式的意义??应规律b,我们想判断一个数是模p的2的多少次方剩余

 

2.求解x^2=a mod p

所以这玩意儿怎么解??

我们讨论一下

如果a是2^k次剩余,即a=y^(2^k),那么a^r=y^(2^k*r)=y^(p-1)=1,我们就得到了判断2^k次剩余的必要条件,那么充分性呢,算出来等于1就是2^k次剩余?

还不简单,我们直接给开出来!

a^r=1 => a^(r+1)=r => sqrt(a)=a^((r+1)/2)

于是你期待着下一个情况:

如果a是2^(k-t)次剩余,即a=y^(2^(k-t)),那么a^(2^t*r)=y^(2^k*r)=y^(p-1)=1,又轻松找到了必要条件,那么这次怎么给出充分性?

还是太难敲公式了,自己类比欧拉判别条件的证明看去吧

话说回来,我们怎么开方?

我们要能够判断a是2的最高多少次方剩余,也就是刚才我们打表时那些数是啥时候丢掉的

显然一种简单的方法就是取t=0……k计算a^(2^t*r)是否等于1

判断出a是2^(k-t)次剩余非2^(k-(t-1))次剩余后

利用规律d,我们可以逐步把问题转化为2^k次剩余的开方问题

怎么转换?任意找一个非二次剩余b,b的2^(k-t)次方就是一个2^(k-t)次剩余非2^(k-(t-1))次剩余(why?自己思考吧!)

那么a*b就是2^(k-(t-1))次剩余了,而b的开方不是问题

也就是方程x^2=a等价于求解(x*b^(2^(k-t-1)))^2=a*b^(2^(k-t))

递归下去到右部为2^k次剩余时就solve了!

 

#include<iostream>
using namespace std;

//将一个数分解为 p=2^k*r+1(r为奇数)
void p2kr1(int p,int&k,int&r){
    k=0;r=p-1;
    while(!(r&1)){
        k++;
        r>>=1;
    }
}

//快速幂 
int mpow(int x,int p,int mod){
    int res=1;
    while(p){
        if(p&1)res=res*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return res;
}

//计算a是模p的2的k-t次剩余
int ft(int a,int p,int k,int r){
    for(int i=0;i<k;i++){
        if(mpow(a,r,p)==1)return i;
        r<<=1;
    }
    return k;
}

//已知p的分解和一个非二次剩余求模平方根
int _sqrt(int a,int b,int p,int k,int r){
    int t=ft(a,p,k,r);
    if(t==0)return mpow(a,(r+1)>>1,p);
    int factor=mpow(b,1>>(k-t-1),p);
    int inverse=mpow(b,p-1-(1>>(k-t-1)),p);
    return _sqrt((a*factor*factor%p),b,p,k,r)*inverse%p;
}

//寻找一个非二次剩余
int fb(int p){
    for(int i=1;i<p;i++){
        if(mpow(i,p>>1,p)==p-1)return i;
    }
}

//开方 
int sqrt(int a,int p){
    //分解p+寻找b 
    int b,k,r;
    p2kr1(p,k,r);
    b=fb(p);
    return _sqrt(a,b,p,k,r); 
}

int main(){
    int p=13;
    for(int i=1;i<p;i++){
        if(mpow(i,p>>1,p)==1){
            cout<<"sqrt("<<i<<")="<<sqrt(i,p)<<endl;
        }
    }
} 

 

posted @ 2019-11-23 11:25  讲成大家听不懂的样子  阅读(1354)  评论(0编辑  收藏  举报