对于A^X==B(MOD C)的情况,如果A与C不互质,那么普通的baby_step giant_step 便不能解决,AC给出了消因子的做法,并且
有证明
推论 2:
令 AA * A^b = B(mod C)
那么解存在的必要条件为: 可以得到至少一个可行解 A^b = X (mod C)
使上式成立
推论3
AA * A^b = B(mod C)
中解的个数为 (AA,C)
由推论3 不难想到对原始Baby Step Giant Step的改进
For I = 0 -> m
For any solution that AA * X = B (mod C)
If find X
Return I * m + j
而根据推论3,以上算法的复杂度实际在 (AA,C)很大的时候会退化到几乎O(C)
归结原因,是因为(AA,C)过大,而就是(A,C)过大 于是我们需要找到一中做法,可以将(A,C)减少,并不影响解 下面介绍一种“消因子”的做法 一开始D = 1 mod C 进行若干论的消因子,对于每次消因子 令 G = (A,C[i]) // C[i]表示经过i轮消因子以后的C的值 如果不存在 G | B[i] //B[i]表示经过i轮消因子以后的B的值 直接返回无解 否则 B[i+1] = B[i] / G C[i+1] = C[i] / G D = D * A / G 具体实现只需要用若干变量,细节参考代码 假设我们消了a'轮(假设最后得到的B,C分别为B',C') 那么有 D * A^b = B' (mod C') 于是可以得到算法 for i = 0 -> m 解 ( D* (A^m) ^i ) * X = B'(mod C') 由于 ( D* (A^m) ^i , C') = 1 (想想为什么?) 于是我们可以得到唯一解 之后的做法就是对于这个唯一解在Hash中查找 这样我们可以得到b的值,那么最小解就是a' + b !! 现在问题大约已近解决了,可是细心看来,其实还是有BUG的,那就是 对于 A^x = B(mod C) 如果x的最小解< a',那么会出错 而考虑到每次消因子最小消 2 故a'最大值为log(C) 于是我们可以暴力枚举0->log(C)的解,若得到了一个解,直接返回 否则必然有 解x > log(C) PS.以上算法基于Hash 表,如果使用map等平衡树维护,那么复杂度会更大
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define LL __int64 #define N 1000000 using namespace std; struct Node { LL idx; LL val; } baby[N]; bool cmp(Node n1,Node n2) { return n1.val!=n2.val?n1.val<n2.val:n1.idx<n2.idx; } LL gcd(LL a,LL b) { return b==0?a:gcd(b,a%b); } LL extend_gcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1; y=0; return a; } LL gcd=extend_gcd(b,a%b,x,y); LL t=x; x=y; y=t-a/b*y; return gcd; } LL inval(LL a,LL b,LL n) { LL e,x,y; extend_gcd(a,n,x,y); e=((LL)x*b)%n; return e<0?e+n:e; } LL PowMod(LL a,LL b,LL MOD) { LL ret=1%MOD,t=a%MOD; while(b) { if(b&1) ret=((LL)ret*t)%MOD; t=((LL)t*t)%MOD; b>>=1; } return ret; } LL BinSearch(int num,int m) { int low=0,high=m-1,mid; while(low<=high) { mid=(low+high)>>1; if(baby[mid].val==num) return baby[mid].idx; if(baby[mid].val<num) low=mid+1; else high=mid-1; } return -1; } LL BabyStep(LL A,LL B,LL C) { LL tmp,D=1%C; LL temp; for(LL i=0,tmp=1%C; i<100; i++,tmp=((LL)tmp*A)%C) if(tmp==B) return i; LL d=0; while((temp=gcd(A,C))!=1) { if(B%temp) return -1; d++; C/=temp; B/=temp; D=((A/temp)*D)%C; } LL m=(LL)ceil(sqrt((long double)C)); for(LL i=0,tmp=1%C; i<=m; i++,tmp=((LL)tmp*A)%C) { baby[i].idx=i; baby[i].val=tmp; } sort(baby,baby+m+1,cmp); int cnt=1; for(int i=1; i<=m; i++) if(baby[i].val!=baby[cnt-1].val) baby[cnt++]=baby[i]; LL am=PowMod(A,m,C); for(LL i=0; i<=m; i++,D=((LL)(D*am))%C) { LL tmp=inval(D,B,C); if(tmp>=0) { LL pos=BinSearch(tmp,cnt); if(pos!=-1) return i*m+pos+d; } } return -1; } int main() { LL A,B,C; while(scanf("%lld%lld%lld",&A,&C,&B)!=EOF) { if(B>=C) { puts("Orz,I can’t find D!"); continue; } LL ans=BabyStep(A,B,C); if(ans==-1) puts("Orz,I can’t find D!"); else printf("%lld\n",ans); } return 0; }