BSGS(Baby Step Gaint Step)算法详解
BSGS算法是用于求解形如Ax≡B(modP)A^x\equiv B(\mod P)Ax≡B(modP)的高次同余方程,其中A,B,PA,B,PA,B,P为已知,且要求gcd?(A,P)=1\gcd(A,P)=1gcd(A,P)=1是一个质数。
前置知识
- 哈希
算法分析
先证明一个结论:若原方程有解,则x∈[0,P?1)x\in[0,P-1)x∈[0,P?1)一定有解。
因为gcd?(A,P)=1\gcd(A,P)=1gcd(A,P)=1,由费马小定理得AP?1≡1(modP)A^{P-1}\equiv1(\mod P)AP?1≡1(modP)。
则有Ax+k(P?1)≡Ax(modP)A^{x+k(P-1)}\equiv A^x(\mod P)Ax+k(P?1)≡Ax(modP),其中kkk为整数。
换句话说就是出现了循环。这样一来,这个方程的最小解就一定在[0,P?1)[0,P-1)[0,P?1)中了。
我们设x=am?bx = am - bx=am?b,其中mmm为任意一个正数,且有a∈[0,m)a\in[0,m)a∈[0,m)。
那么变一下形就发现Aam≡B?Ab(modP)A^{am}\equiv B\cdot A^{b}(\mod P)Aam≡B?Ab(modP)。
我们暴力算出右边的B?AbmodPB\cdot A^b\mod PB?AbmodP的所有可能的值,并存起来。
然后再枚举左边的aaa,并查询在右边有没有相等的值。
所以这个算法的复杂度为O(max?(m,Pm))O(\max(m,\frac{P}{m}))O(max(m,mP?))。可以证明当m=Pm=\sqrt Pm=P?时,算法的复杂度达到下界,为O(P)O(\sqrt P)O(P?)。
就这么简单QAQ…
当然为了查询方便,我们写一个哈希或者直接用map
存下右边所有的值即可。
这里建议用哈希,因为用map
的话会多出一个log?\loglog。
模版
struct HashTable {
struct HashNode {
int u, v;HashNode *nxt;};HashNode pool[HashMod * 20 + 5];HashNode *hcnt, *head[HashMod + 5];void clear() {
hcnt = &pool[0], memset(head, 0, sizeof head);}void addnode(int u, int v, int w) {
HashNode *p = ++hcnt;p->u = w, p->v = v;p->nxt = head[u], head[u] = p;}void hash(int x, int k) {
int s = x % HashMod;addnode(s, k, x);}int query(int x) {
int s = x % HashMod;for(HashNode *p = head[s]; p != NULL; p = p->nxt)if(p->u == x) return p->v;return -1;}
};
HashTable hashtab;
ll BabyStepGaintStep(ll y, ll z, ll p) {
if(y % p == 0) return -1;y %= p, z %= p;if(z == 1) return 0;hashtab.clear();ll m = (ll) sqrt(p) + 0.5;for(ll i = 0, t = z; i < m; i++, t = t * y % p)hashtab.hash(t, i);for(ll i = 1, tmp = QuickPow(y, m, p), t = tmp; i <= m + 1; i++, t = t * tmp % p) {
ll k = hashtab.query(t);if(k == -1) continue;return i * m - k;}return -1;
}