• 二次剩余学习笔记


    定义

    求解方程 (x^2 equiv n(mod p))

    保证 (p) 是奇素数

    欧拉准则

    用来判断一个数 (n) 是否为二次剩余

    根据费马小定理,有 (n^{p-1} equiv1(mod p))

    因为 (p) 为奇素数

    所以 (n^{2frac{p-1}{2}} equiv1(mod p))

    所以 (n^{frac{p-1}{2}})(1) 开根后的结果,它的值只能等于等于 (1)(p-1)

    (n) 为二次剩余,则 (n equiv x^2(mod p))

    所以 (n^{frac{p-1}{2}} equiv x^{p-1} equiv 1 (mod p))

    因此可以认为 (n^{frac{p-1}{2}} equiv 1(mod p))等价于 (n) 是二次剩余

    (n^{frac{p-1}{2}} equiv p-1(mod p)) 等价于 (n) 是非二次剩余

    求解

    找到一个数 (a) 使得 (a^2-n) 是非二次剩余

    我们可以随机寻找,每次都有 (frac{1}{2}) 的概率寻找成功

    接下来定义 (i^2 equiv a^2-n(mod p))

    因为 (a^2-n) 是非二次剩余,所以 (i) 用实数是无法表示出来的

    但是我们可以像虚数那样定义一个 (i)

    将所有的数都表示为 (A+Bi) 的形式

    此时就有 (i^p=ii^{2frac{p-1}{2}}=i(a^2-n)^{frac{p-1}{2}}=-i)

    ((a+i)^{p+1} equiv (a^p+i^p)(a+i) equiv (a-i)(a+i) equiv a^2-i^2 equiv n (mod p))

    ((a+i)^p) 在进行二项式展开时除了第一项和最后一项分子的位置都会有一个 (p) 没有消去,在模意义下就变成了 (0)

    那么 ((a+i)^{frac{p+1}{2}}) 就是我们想要的一组解

    它的相反数就是另一组解

    可以证明得出来的解是没有虚部的

    代码

    P5491 【模板】二次剩余

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cstdlib>
    #include<iostream>
    #include<cmath>
    #define rg register
    inline int read(){
    	rg int x=0,fh=1;
    	rg char ch=getchar();
    	while(ch<'0' || ch>'9'){
    		if(ch=='-') fh=-1;
    		ch=getchar();
    	}
    	while(ch>='0' && ch<='9'){
    		x=(x<<1)+(x<<3)+(ch^48);
    		ch=getchar();
    	}
    	return x*fh;
    }
    int t,n,p,w,now;
    long long mulmod(rg long long now1,rg int now2){
    	return now1*=now2,now1>=p?now1%p:now1;
    }
    int delmod(rg int now1,rg int now2){
    	return now1-=now2,now1<0?now1+p:now1;
    }
    int addmod(rg int now1,rg int now2){
    	return now1+=now2,now1>=p?now1-p:now1;
    }
    struct fs{
    	int x,y;
    	fs(){}
    	fs(rg int aa,rg int bb){
    		x=aa,y=bb;
    	}
    	friend fs operator *(const fs& A,const fs& B){
    		return fs(addmod(mulmod(A.x,B.x),mulmod(w,mulmod(A.y,B.y))),addmod(mulmod(A.x,B.y),mulmod(A.y,B.x)));
    	}
    };
    int ksm1(rg int ds,rg int zs){
    	rg int nans=1;
    	while(zs){
    		if(zs&1) nans=mulmod(nans,ds);
    		ds=mulmod(ds,ds);
    		zs>>=1;
    	}
    	return nans;
    }
    int ksm2(rg fs ds,rg int zs){
    	rg fs nans=fs(1,0);
    	while(zs){
    		if(zs&1) nans=nans*ds;
    		ds=ds*ds;
    		zs>>=1;
    	}
    	return nans.x;
    }
    int solve(){
    	n%=p;
    	if(ksm1(n,(p-1)/2)==p-1) return -1;
    	while(1){
    		now=rand()%p+1;
    		w=mulmod(now,now);
    		w=delmod(w,n);
    		if(ksm1(w,(p-1)/2)==p-1) break;
    	}
    	return 1;
    }
    int main(){
    	srand(time(0));
    	t=read();
    	while(t--){
    		n=read(),p=read();
    		if(n==0){
    			printf("0
    ");
    			continue;
    		}
    		rg int haha=solve();
    		if(haha==-1){
    			printf("Hola!
    ");
    			continue;
    		}
    		rg int ans1=ksm2(fs(now,1),(p+1)/2);
    		rg int ans2=p-ans1;
    		if(ans1>ans2) std::swap(ans1,ans2);
    		if(ans1==ans2) printf("%d
    ",ans1);
    		else printf("%d %d
    ",ans1,ans2);
    	}
    	return 0;
    }
    
  • 相关阅读:
    地铁项目结对编程
    地铁项目初步计划及简单设计
    构建之法浅读感想
    集美大学1511,1512软件工程课程作业总结
    第二次作业小结
    第二次作业评分可能要 delay 一些
    第一次作业小结
    关于我
    面向对象设计与构造第四单元总结及期终总结
    面向对象设计与构造第三单元作业总结
  • 原文地址:https://www.cnblogs.com/liuchanglc/p/14246392.html
Copyright © 2020-2023  润新知