Baby-Step-Giant-Step
BSGS算法用于解决形如: A ^ x ≡ B ( mod C ) 的问题。
学这个算法前需要具备以下知识:快速幂取模、扩展欧几里得、同余知识、哈希表(也可以用map,不过更耗时)..
一. 普通的Baby-Step-Giant-Step
对于普通的BSGS,使用时有个限制条件就是:C只能是一个素数。当C 不是素数的时候,便要用到扩展BSGS,我们先来看普通的…
做法:首先令 x = a * m + b ; m = ceil ( sqrt ( C ) ) ; [ceil: 上取整] 0 <= a < m , 0 <= b < m ;
Then 原式等价于 ( ( A ^ a ) ^ m ) * A ^ b ≡ B (mod C );
Then 我们枚举a ,从0 到 m ,对每个a ,算出 A^a ,放到哈希表或map里(用哈希更高效…这里先用map) ,如 h=A^a; mp[h]=a;然后我们用 D 来表示 ( ( A ^ a ) ^ m ) ,用Y来表示 A^B 原式便可化为 : D * Y ≡ B (mod C);
Then 我们再次枚举 a 从 0 到 m ,可以逐个算出 D 的值,在有了D值得基础上,运用扩展欧几里得可以算出 Y ,现在查找原本记录的表,查看这个Y 值是否存在,如果存在便可返回 a^m + b ;
如果找完0 到 m ,依然没有发现可行的解,表示无解,退出。
1 #include<stdio.h> 2 #include<math.h> 3 #include<map> 4 using namespace std; 5 /* 6 Baby-Step-Giant-Step: 用于解决形如: A^x = B (mod C) 的问题 7 <><> 普通的解法只能解决C是素数的情况 8 首先,令 x= a*m + b ; -- m= Ceil(sqrt(c)); -- Ceil 表示上取整 9 then 原式等价于 ( (A^a )^m )*(A^b)=B (mod c); 10 then 那么我们可以枚举 a: 0-m ,记录到 (a,A^a) ,用H 来表示: (A^a)^m ; 11 那么 原式等价于 H * A^b = B (mod c) ; 12 then 求出 A^m ; 枚举 a: 0-m ,对于每个值判断有没有 A^b 存在,如果有便退出... 13 */ 14 // 这个代码不敢保证正确无误,因为没有找到题来A,不过思路应该是没错 15 //了解了普通的BSGS,可以去看下面那份代码,注释得比较详细... 16 #define EPS 0.0000001 17 typedef long long LL; 18 LL pow(LL a,LL b,LL c) //快速幂取模 19 { 20 LL ans=1; 21 while(b>0) 22 { 23 if(b&1) 24 { 25 ans=ans*a%c; 26 } 27 a=a*a%c; 28 b>>=1; 29 } 30 return ans; 31 } 32 void exGcd(LL a,LL b,LL &d,LL &x,LL &y) 33 { 34 if(b==0) 35 { 36 d=a; 37 x=1; 38 y=0; 39 return ; 40 } 41 exGcd(b,a%b,d,x,y); 42 LL t=x; 43 x=y; 44 y=t-a/b*y; 45 } 46 LL BSGS(LL A,LL B,LL C) 47 { 48 map<LL,int> mp; 49 // LL m=(LL)ceil(sqrt(C)); 据英明神武的某朱说Gcc不支持这个ceil 50 LL m=(LL)(sqrt(C)+1-EPS); 51 for(int i=m-1;i>=0;i--) 52 { 53 LL h=pow(A,i,C); 54 mp[h]=i; 55 } 56 LL h=pow(A,m,C); 57 LL d,x,y; 58 for(int i=0;i<m;i++) 59 { 60 LL k=pow(h,i,C); 61 exGcd(k,C,d,x,y); 62 if(B%d!=0) continue; 63 x=((x*B/d%C)+C)%C; 64 if(mp.find(x)!=mp.end()) return mp[x]+i*m; 65 } 66 return -1; 67 } 68 int main() 69 { 70 LL A,B,C; 71 while(scanf("%I64d%I64d%I64d",&A,&B,&C)!=EOF) 72 { 73 printf("%I64d\n",BSGS(A,B,C)); 74 } 75 return 0; 76 }