BSGS算法 名拔山盖世算法,又称北上广深算法。
如果理解了模线性方程,A * x B (mod C) , BSGS 算法更好理解。
求解A^x ≡ B mod N (N不一定是质数)的最小非负正整数解
先放几个同余定理的相关性质:
费马引理:
1 (mod p)
一、判断如果B==1,那么x=0,算法结束
如果B=1,就是 % N
1 ,可得x=0,因为N不可能为1,且任何数的0次幂都是1,所以算法结束。
二、若gcd(A,N)不能整除 B,则 无解,算法结束
可以证明: d =gcd(A,N), 有 d | A (代表gcdd是A的因子), d | N, 即有A=a* d,N=n-d;
代入原式即可得 : - (n * d) *y = B ,整理即可得:d *( a*
- n*y)= B
很显然 d | B; 所以,若gcd(A,N)不能整除 B,则 无解,算法结束。
三、若gcd(A,N)!=1,令d=gcd(A,N),若d不能整除B,则无解,算法结束。
假设d | B , 利用剩余定理的相关性质有: B (mod N) ;
*
(mod
);
四、持续步骤三,直至 gcd(A,)=1
有 *
(mod
)
五、枚举 0<x<k,若有解,输出x,算法结束
六、对于x>=k,
这就是经典的BSGS算法,方法如下:
令x=i*m− j, m = ,(向上取整) 则
≡ b(mod p)
费马引理:
1 (mod p) 并有
mod p
证明如下:
移项,得(am)i≡baj(modp)(am)i≡baj(modp)
首先,从0−m0−m枚举jj,将得到的bajbaj的值存入hash表;
然后,从1−m1−m枚举ii,计算(am)i(am)i,查表,如果有值与之相等,则当时得到的im−jim−j是最小值。
#include<map>
#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
map<int,int>mp;
void read(int &x)
{
x=0; char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); }
}
int get_gcd(int a,int b) { return !b ? a : get_gcd(b,a%b); }
int Pow(int a,int b,int mod)
{
int res=1;
for(;b;a=1LL*a*a%mod,b>>=1)
if(b&1) res=1LL*res*a%mod;
return res;
}
int ex_BSGS(int A,int B,int C)
{
if(B==1) return 0;
int k=0,tmp=1,d;
while(1)
{
d=get_gcd(A,C);
if(d==1) break;
if(B%d) return -1;
B/=d; C/=d;
tmp=1LL*tmp*(A/d)%C;
k++;
if(tmp==B) return k;
}
mp.clear();
int mul=B;
mp[B]=0;
int m=ceil(sqrt(1.0*C));
for(int j=1;j<=m;++j)
{
mul=1LL*mul*A%C;
mp[mul]=j;
}
int am=Pow(A,m,C);
mul=tmp;
for(int j=1;j<=m;++j)
{
mul=1LL*mul*am%C;
if(mp.count(mul)) return j*m-mp[mul]+k;
}
return -1;
}
int main()
{
int A,C,B;
int ans;
while(1)
{
read(A); read(B); read(C);
if(!A) return 0;
ans=ex_BSGS(A,B,C);
if(ans==-1) puts("No Solution");
else cout<<ans<<'\n';
}
}
A=,B=
,P=
A,P 互素 ,
直接用BSGS 求 * A ^ x ≡ B mod P
所得结果再+k即可