BSGS算法_Baby steps giant steps算法(無擴展)詳解


Baby Steps-Varsity

Giant Step-Astronauts(May'n・椎名慶治)

閱讀時可以聽聽這兩首歌,加深對這個算法的理解。(Baby steps少女時代翻唱過,這個原唱反而不是很有名……Giant Step就比較碉,是一個假面騎士片的插曲,由超碉的May'n和一個人建立的臨時組合唱的,怕不怕)

 

這個主要是用來解決這個題:

A^x=B(mod C)(C是質數),都是整數,已知A、B、C求x。

我在網上看了好多介紹,覺得他們寫得都不夠碉,我看不懂…於是我也來寫一發。

先把x=i*m+j,其中m=ceil(sqrt(C)),(ceil是向上取整)。

這樣原式就變為A^(i*m+j)=B(mod C),

再變為A^j=B*A^(-m*i) (mod C)

先循環j=0~(C-1),把(A^j,j)加入hash表中,這個就是Baby Steps(現在可以播放Baby Steps-Varsity了)。

下面我們要做的是枚舉等號右邊,從hash表中找看看有沒有,有的話就得到了一組i j,x=i*m+j,得到的這個就是正確解。

所以,接下來要解決的就是枚舉B*A^(-m*i) (mod C)這一步(這就是Giant Step,快放Giant Step-Astronauts(May'n・椎名慶治))。

A^(-m*i)相當於1/(A^(m*i)),里面有除法,在mod里不能直接用除法,這時候我們就要求逆元

/*百度百科:

若ax≡1 mod f, 則稱a關於模f的乘法逆元為x。也可表示為ax≡1(mod f)。
當a與f互素時,a關於模f的乘法逆元有唯一解。如果不互素,則無解。如果f為素數,則從1到f-1的任意數都與f互素,即在1到f-1之間都恰好有一個關於模f的乘法逆元。
*/
 
然后我們用超碉的 exgcd求逆元,exgcd(擴展歐幾里德算法)就是在求AB的最大公約數z的同時,求出整數x和y,使xA+yB=z。算法實現就是gcd加幾個語句。
然后我們再來看一下exgcd怎么求逆元:

對xA+yB=z,

變成這樣xA = z - yB,取B=C(C就是我們要mod的那個)

推導出 xA % C = z %C

只要  z%C==1 時,就可以求出A的逆元x

但用exgcd求完,x可能是負數,還需要這樣一下:x=(x%C+C)%C

//--exgcd介紹完畢--

再看我們的題目,

exgcd(A^(m*i) , C)=z,當C是質數的時候z肯定為1,這樣exgcd求得的x就是逆元了。

因為x就是A^(m*i)的逆元,P/(A^(m*i))=P*x,所以

B*A^(-m*i) = B/(A^(m*i)) = B*x(mod C)

這樣我們的式子A^j=B*A^(-m*i) (mod C)的等號右邊就有了,就是B*x,就問你怕不怕!

枚舉i,求出右邊在hash里找,找到了就返回,無敵!


例題:

http://poj.org/problem?id=2417

代碼:

 1 #include<cstdio>
 2 #include<cmath>
 3 #include<iostream>
 4 #include<cstring>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<map>
 8 #include<set>
 9 using namespace std;
10 #define ll __int64
11 #define usint unsigned int
12 #define RE  freopen("1.in","r",stdin)
13 
14 class hash {
15 public:
16     hash() {
17         memset(a,0xff,sizeof(a));
18     }
19     int locate(ll x) {
20         ll l=x%MOD;
21         while(a[l]!=x&&a[l]!=-1) l=(l+1)%MOD;
22         return l;
23     }
24     void insert(ll x,ll va) {
25         ll l=locate(x);
26         if(a[l]==-1) {
27             a[l]=x;
28             v[l]=va;
29         }
30     }
31     ll get(ll x) {
32         ll l=locate(x);
33         return a[l]==x?v[l]:-1;
34     }
35     void clear() {
36         memset(a,0xff,sizeof(a));
37     }
38 private:
39     static const ll MOD=100007;
40     ll a[MOD+100],v[MOD+100];
41 } S;
42 
43 ll exgcd(ll a,ll b,ll &x,ll &y) {
44     ll t,ret;
45     if (!b) {
46         x=1,y=0;
47         return a;
48     }
49     ret=exgcd(b,a%b,x,y);
50     t=x,x=y,y=t-a/b*y;
51     return ret;
52 }
53 
54 int main() {///A^x=B(modC)    A^j=B*A^(-m*i)(mod C)
55     ll C,A,B;
56     ll m,i,t,D,ans,x,y;
57     while(scanf("%lld%lld%lld",&C,&A,&B)!=EOF) {
58         S.clear();
59         m=ceil(sqrt((double)C));
60         t=1;
61         for(i=0; i<m; i++) { /**One, two, baby steps.Three, four, baby steps.Five, six, baby steps.**/
62             S.insert(t,i);
63             t=t*A%C;
64         }
65         D=1;///此時t=A^m
66         ans=-1;
67         for(i=0; i<m; i++) { /**一歩 Giant Step , 君にとってLittle だとしても**/
68             exgcd(D,C,x,y);///exgcd求逆元,得到x=D^(-i*m)
69             x=((x*B)%C+C)%C;///B*x=B*D^(-i*m)
70             y=S.get(x);
71             //printf("%lld,%lld\n",x,y);
72             if(y!=-1) {
73                 ans=i*m+y;
74                 break;
75             }
76             D=(D*t)%C;///D=t^i,(t=A^m)
77         }
78         if(ans==-1) printf("no solution\n");
79         else printf("%lld\n",ans);
80     }
81     return 0;
82 }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM