例题 poj 2417bsgs http://poj.org/problem?id=2417
这是一道bsgs题目,用bsgs算法,又称大小步(baby step giant step)算法,或者拔(b)山(s)盖(g)世(s)算法,或者北(b)上(s)广(g)深(s)算法。。。
题目大意就是
给定a,b,p,求最小的非负整数x,满足 ax ≡ b(mod p)
先令 x = i*m-j,其中 m=ceil(sqrt(p)),ceil是向上取整。
这样原式就变为 ai*m-j = b (mod p),
移项就变成了 ai*m = b*aj (mod p)
枚举j (范围0-m) ,将 b*aj 存入hash表。
枚举i (范围1-m) ,从hash表中寻找第一个满足ai*m = b*aj (mod p)。
此时 x = i*m-j 就是所要求的。
那么为什么只计算到 m=ceil(sqrt(q)) 就可以确定答案呢?
因为 x = i*m-j , 所以x 的最大值不会超过p
a(k mod p-1) = ak (mod p) 证明这个公式,(需要用到费马小定理)
k mod p-1 就是 k-m(p-1) ,原式就变成了 ak-m(p-1) ≡ ak (mod p)
再变一步 ak / am(p-1) ≡ ak (mod p)
这时让 am(p-1) ≡ 1 (mod p) 就行了。
由费马小定理知: 当p为质数且 (a,p) = 1 时 ap-1 ≡ 1 (mod p)
所以推出 p 为质数 且 (a,p)=1 这个条件, 所以 a(k mod p-1) ≡ a k (mod p)
所以:如果枚举 x 的话枚举到 p 即可。
所以使 im−j<=p , 即 m=⌈√p⌉ , i,j 最大值也为m。
这是代码,结合上面的看
1 #include<cstdio> 2 #include<algorithm> 3 #include<map> 4 #include<cmath> 5 6 using namespace std; 7 typedef long long ll; 8 9 map<ll,int>mp; 10 ll p,a,b; 11 ll n,m,now,ans,t; 12 bool flag; 13 14 ll fast_pow(ll x) 15 { 16 ll sum = 1; 17 ll aa = a; 18 while (x>0) 19 { 20 if (x&1) 21 sum = (sum*aa)%p; 22 x = x>>1; 23 aa = (aa*aa)%p; 24 } 25 return sum; 26 } 27 int main() 28 { 29 while(scanf("%lld%lld%lld",&p,&a,&b)!=EOF) 30 { 31 if(a%p==0) 32 { 33 printf("no solution "); 34 continue; 35 } 36 mp.clear(); 37 m = ceil(sqrt(p)); 38 flag = false ; 39 now = b%p; //b*a^j 当j==0时 40 mp[now] = 0; 41 for(int i=1;i<=m;++i) 42 { 43 now = (now*a)%p; 44 mp[now] = i; 45 } 46 t = fast_pow(m); 47 now = 1; 48 for(int i=1;i<=m;++i) //枚举 (a^m)^i 49 { 50 now = (now*t)%p; 51 if(mp[now]) 52 { 53 flag = true; 54 ans = i*m-mp[now]; 55 printf("%lld ",(ans%p+p)%p); //printf("%lld ",(ans%p+p)%p); 56 break; 57 } 58 } 59 if(!flag) printf("no solution "); 60 } 61 return 0; 62 }