首先讲一下基础的baby-step-giant-step,解决的问题模型是求A^x=B(mod C)的解,其中0<=x<C,C为素数。
x=i*m+j (0<=i<m,0<=j<m,m=Ceil(sqrt(C))),问题转变为(A^i)^m*A^j=B(mod C),进一步转换为(A^m)^i*A^j=B(mod C)
求出(A^i)modC的值并储存到hash中(i from 0 to m)
枚举i(from 0 to m),根据扩展欧几里得得到一个解(由于C是素数,在0-C中只存在一个解),然后在hash表里查找是否存在这个解,如果存在则对应一个j,i*m+j就是问题的最小非负解了。
edit: 发现bug,应该对于hash去一下重,这样才能保证二分的结果最小。 2013/3/22
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long llong;
struct baby
{
llong data;
int index;
};
bool cmp(baby a,baby b)
{
return a.data<b.data;
}
baby hash[1<<17];
llong ex_gcd(llong a,llong b,llong& x,llong& y)
{
if(b==0)
{
x=1,y=0;
return a;
}
else
{
llong ans,t;
ans=ex_gcd(b,a%b,x,y);
t=x;
x=y;
y=t-a/b*y;
return ans;
}
}
int bi_search(llong x,int m)
{
int l,r,mid;
l=0,r=m,mid=(l+r)>>1;
while(l<r)
{
if(hash[mid].data==x)
return hash[mid].index;
else if(hash[mid].data<x)
l=mid+1;
else
r=mid;
mid=(l+r)>>1;
}
return -1;
}
llong quickpow(llong a,llong b,llong c)
{
llong ans=1;
while(b)
{
if(b&1)
ans=(ans*a)%c;
a=(a*a)%c;
b>>=1;
}
return ans;
}
llong bsgs(llong a,llong b,llong c)
{
llong m=llong(sqrt(double(c)))+1;
llong i,j,t,AA,gcd,x,y,tmp;
t=1,hash[0].index=0,hash[0].data=1;
for(i=1;i<m;++i)
{
t=(t*a)%c;
hash[i].data=t;
hash[i].index=i;
}
sort(hash,hash+m,cmp);
AA=quickpow(a,m,c);
t=1;
for(i=0;i<m;++i)
{
ex_gcd(t,c,x,y);
x=(x%c+c)%c;
x=(x*b)%c;
// for(j=0;j<m;++j)
// if(hash[j]==x)
// return i*m+j; //超时
j=bi_search(x,m);
if(j!=-1)
return i*m+j;
t=(t*AA)%c;
}
return -1;
}
int main()
{
llong p,g,y,ans;
while(scanf("%I64d %I64d %I64d",&p,&g,&y)!=EOF)
{
ans=bsgs(g,y,p);
if(ans==-1)
printf("ERROR\n");
else
printf("%I64d\n",ans);
}
return 0;
}