BSGS演算法解析
阿新 • • 發佈:2020-08-05
前置芝士:
1.快速冪(用於求一個數的冪次方)
2.STL裡的map(快速查詢)
詳解
BSGS 演算法適用於解決高次同餘方程 \(a^x\equiv b (mod p)\)
由費馬小定理可得 x <= p-1
我們設 \(m = sqrt(p)\) 至於為什麼寫,下文會講到。
那麼\(x\)就可以用 \(m\) 表示出來。
即 x = \(k \times m - j\)
移項可得 \(a^t \equiv b\times a^j\) 其中 t = \(k \times m\)
這也就是我們為什麼把\(x\)用 \(k \times m - j\)來表示。
因為改為加\(j\)後,移項後要求逆元,就會變得很麻煩。
這樣,我們就可以列舉每個\(k\)和\(j\),來判斷左右兩邊得值是否相等就行了。
首先,我們可以列舉j 將 \(b\times a^j\)放入map中。
然後,從小到大列舉\(k\),在雜湊表中,找到最大的\(j\)滿足 \(a^t \equiv b\times a^j\) 其中 t = \(k \times m\)
若存在\(k\times m -j\)就是方程的解
關於上文中,為什麼要設\(m = sqrt(p)\)
是為了保證BSGS的複雜度,是左右兩邊的數儘可能的均勻。
例題
模板題,水過去了
#include<iostream> #include<cstdio> #include<algorithm> #include<map> #include<cmath> using namespace std; #define LL long long int a,b,p; LL ksm(LL a, LL b) { LL res = 1; for(; b; b >>= 1) { if(b & 1) res = res * a % p; a = a * a % p; } return res; } int BSGS(int a,int b,int p) { map<LL,int> hash; hash.clear(); int m = (int)sqrt(p); for(int i = 0; i <= m; i++) { LL val = ksm(a,i) * b % p;//b * a ^ i hash[val] = i;//放入map中 } a = ksm(a,m);//a ^ m for(int i = 0; i <= m; i++) { LL val = ksm(a,i);//(a^m)^i int j = hash.find(val) == hash.end() ? -1 : hash[val];//如果沒有j就為-1 if(j >= 0 && i * m - j >= 0) return i * m - j;//找到一組解 } return -1; } int main() { scanf("%d%d%d",&p,&a,&b); LL ans = BSGS(a,b,p); if(ans == -1) cout<<"no solution"<<endl; else printf("%lld\n",ans); return 0; }
一道很多演算法綜合在一起的模板題,水過去了。
但我還沒寫,先把坑占上