zoukankan      html  css  js  c++  java
  • 关于 BSGS 以及 ExBSGS 算法的理解

    BSGS

    引入

    求解关于(X)的方程,

    [A^Xequiv B pmod P ]

    其中(Gcd(A,P)=1)

    求解

    我们令(X=i*sqrt{P}-j),其中(0<=i,j<=sqrt{P})
    则原式可以变为:

    [A^Xequiv B pmod P ]

    [A^{i*sqrt{P}-j}equiv B pmod P ]

    由于(Gcd(A,P)=1),则可以恒等变化为:

    [A^{i*sqrt{P}}equiv B*A^j pmod P ]

    则我们可以先预处理出所有的(A^{i*sqrt{P}}),存入哈希表。
    再枚举(B*A^j),在哈希表里查找即可得解。

    代码

    int quick_Pow(int x,int y,int p){
    	if(y==0)return 1;
    	if(y==1)return x;
    	if(y%2)return 1ll*x*quick_Pow(1ll*x*x%p,y/2,p)%p;
    	return quick_Pow(1ll*x*x%p,y/2,p);
    }
    void BSGS(int x,int y,int p){
    	x%=p;y%=p;
    	if(x==0&&y!=0){puts("-1");return ;}
    	if(x==0&&y==0){puts("1");return ;}
    	if(y==1){puts("0");return ;}
    	int st=int(sqrt(p))+1;Mp.clear();
    	for(int i=1,rt=1;i<=st;i++,rt=(1ll*rt*x)%p)Mp[rt]=i;
    	int sum=quick_Pow(x,st,p);
    	for(int i=1,rt=1;i<=st;i++){
    		rt=(1ll*rt*sum)%p;
    		if(Mp[rt]){
    			printf("%d
    ",i*st-Mp[rt]);
    			return ;
    		}
    	}
    	puts("-1");
    }
    

    ExBSGS

    引入

    求解关于(X)的方程,

    [A^Xequiv B pmod P ]

    其中(Gcd(A,P))无特殊条件。

    由于(Gcd(A,P))可能不为(1),所以(A)关于(P)可能没有逆元。
    故不能用一般的 BSGS 求解。

    求解

    我们设(D=Gcd(A,P))
    则显然有 (frac{A}{D}equiv 1pmod P)
    则原式$$A^Xequiv B pmod P$$
    可恒等变形为$$A^{X-1}cdotfrac{A}{D}equiv frac{B}{D} pmod {frac{P}{D}}$$
    而由于(Gcd(frac{A}{D},frac{P}{D})=1),则有

    [A^{X-1}equiv frac{B}{D}cdot({frac{A}{D}})^{-1} pmod {frac{P}{D}} ]

    则此时(frac{P}{D})就相当于新的(P)值,(frac{B}{D}cdot({frac{A}{D}})^{-1})就相当于新的(B)值,
    就可以这样递推下去了。(注:下一次的(D)是新的(P)值与(A)(Gcd)

    考虑边界状态:
    ①:若当前的(D=1),则问题转化为普通的 BSGS。
    ②:若(B)值等于(1)了,则迭代到的(Ans)值为(1)
    ③:若(D mid B),即(D)不是(B)的因数,则(frac{B}{D})没有意义,则无解。

    综上,出解。

    例题及代码

    板题
    题意:求满足(A^Xequiv B pmod{P})的最小整数(X)

    #include<map>
    #include<cmath>
    #include<cstdio>
    #include<algorithm>
    #include<tr1/unordered_map>
    using namespace std;
    #define LL long long
    tr1::unordered_map<LL,int>Mp;
    LL Gcd(LL x,LL y){
    	if(x==0)return y;
    	return Gcd(y%x,x);
    }
    LL quick_Pow(LL x,LL y,LL p){
    	if(y==0)return 1;
    	if(y==1)return x;
    	if(y%2)return x*quick_Pow(x*x%p,y/2,p)%p;
    	return quick_Pow(x*x%p,y/2,p);
    }
    int ExBSGS(int x,int y,int p){
    	if(y==1)return 0;//特判解为0的情况.
    	LL k=0,a=1;
    	while(1){
    		int d=Gcd(x,p);if(d==1)break;
    		if(y%d)return -1;
    		y/=d;p/=d;k++;a=(1ll*a*x/d)%p;
    		if(a==y)return k;//同特判.
    	}Mp.clear();
    	LL st=int(sqrt(p))+1,sum=quick_Pow(x,st,p);
    	for(LL i=0,rt=y;i<=st;i++,rt=(1ll*rt*x)%p)Mp[rt]=i+1;
    	for(LL i=1,rt=(a*sum)%p;i<=st;i++,rt=(1ll*rt*sum)%p){//不将求解中的A/D移项.
    		if(!Mp[rt])continue;
    		return 1ll*i*st-(Mp[rt]-1)+k;
    	}
    	return -1;
    }
    int A,B,C;
    int main(){
    	while(~scanf("%d%d%d",&A,&B,&C)&&A){
    		int Ans=ExBSGS(A,C,B);
    		if(Ans!=-1)printf("%d
    ",Ans);
    		else puts("Orz,I can’t find D!");
    	}
    }
    

    注:一般的,题目所给的A,B,C都是正整数。

  • 相关阅读:
    Leetcode 18. 4Sum
    Leetcode 15. 3Sum
    Leetcode 16. 3Sum Closest
    String类型的理解
    求字符串中某个字符出现的次数
    用StringBuilder来实现经典的反转问题
    String/StringBuilder 类 用对象数组实现登录注册功能
    String/StringBuilder 类 统计字符串中字符出现的次数
    String/StringBuilder 类 判断QQ号码
    C++ 面向对象: I/O对象的应用
  • 原文地址:https://www.cnblogs.com/ftotl/p/11621342.html
Copyright © 2011-2022 走看看