BSGS算法(Baby Step Giant Step)
拔山盖世算法/北上广深算法
先看一道同余题目:
已知a,p互质,求解$a^xequiv bpmod p$
我们该怎么做呢?
设$m=ceil(sqrt{p})$,$x=i imes m-j$,$jin [0,m)$。(ceil为向上取整)
所以$iin [0,m]$。
对于所有$jin [0,m-1]$,把$b imes a^jpmod p$插入一个哈希表或map表中。(Baby Step)
然后直接暴力枚举i,$iin [0,m]$,计算出$(a^m)^ipmod p$,在哈希表中查找是否存在对应的j,更新结果即可。(Giant Step)
时间复杂度为$O(sqrt{p})$。
代码:
# include<bits/stdc++.h> using namespace std; map<int,int>hash; int a,b,p,ans; int pow(int a,int b){ int r=1; while(b){ if(b%2)r=r*a%p; b/=2; a=a*a%p;; } } int bsgs(){ int t,m=ceil(sqrt(p)),k=pow(a,p-m-1); hash[1]=m; for(int i=1;i<m;i++){ t=(t*a)%p; if(!hash[t])hash[t]=i; } for(int i=0;i<m;i++){ if(hash[b]){ int res=hash[b]; hash.clear(); if(res==m)return i*m; else return i*m+res; } b=(b*k)%p; } return -1; } int main(){ scanf("%d%d%d",&a,&b,&p); ans=bsgs(); if(ans==-1)printf("no solution "); else printf("%d",ans); }
拓展BSGS算法
回到上题,当a和p不互质时,又该怎么办呢?
原方程等价于$a^x+py=b$
令$g_1=gcd(a,p)$,此时b为$g_1$倍数时有整数解,否则无解。
方程两边同时除以$g_1$可得:
$frac{a}{g_1}a^{x-1}+frac{p}{g_1}y=frac{b}{g_1}$
再令$g_2=gcd(frac{p}{g_1},a)$,若$g_2 e 1$,除以$g_2$可得:
$frac{a^2}{g_1g_2}a^{x-2}+frac{p}{g_1g_2}y=frac{b}{g_1g_2}$
不断检查$gcd(frac{p}{g_1g_2...g_i},a)$直到互质,则可写成:
$frac{a……{cnt}}{g}a^{x-cnt}+frac{p}{g}y=frac{b}{g}$
其中$g=g_1g_2...g_cnt$,cnt为检查的次数,此时$gcd(frac{p}{g},a)=1$
上方程等价于$frac{a……{cnt}}{g}a^{x-cnt}equivfrac{b}{g}pmod{frac{p}{g}}$
最后再用BSGS求出x-cnt即可。
注意:这样只能得到大于cnt的解,可能会漏掉小于cnt的解,所以一般会先从1到50枚举一遍。
#include<bits/stdc++.h> using namespace std; map<int,int>hash; int a,p,b,ans; int gcd(int a,int b){ if(!b)return a; return gcd(b,a%b); } int pow(int a,int b){ int r=1; while(b){ if(b%2)r=r*a%p; b/=2; a=a*a%p; } return r; } int exbsgs(){ a%=p,b%=p; if(b==1)return 0; int d=gcd(a,p),k=1,t=0; while(d^1){ if(b%d)return -1; ++t,b/=d,p/=d,k=k*a/d%p; if(b==k)return t; d=gcd(a,p); } int s=b,m=ceil(sqrt(p)); for(int i=0;i<m;i++){ hash[s]=i;s=s*a%p; } s=k,k=pow(a,m); for(int i=1;i<=m;i++){ s=s*k%p; if(hash[s])return i*m-hash[s]+t; } return -1; } int main(){ scanf("%d%d%d",&a,&p,&b); ans=exbsgs(); if(ans==-1)printf("no solution"); else printf("%d",ans); }