一個經典的算法——BSGS算法
BSGS全名為Baby-Step-Giant-Step,即大小步法,有人戲稱之為北上廣深算法。可以快速求解離散對數問題,形如,C是素數。
BSGS算法使用了一個重要的引理:
重要的引理
如果
,那么
。
證明這個引理需要用到歐拉定理
歐拉定理
如果
,那么
。
證明(十分精彩的證明):
將小於C且與C互質的數順序列出:
。
令
。
那么數列m有如下兩個性質:
1)這些數中的任意兩個都不模C同余。
假設存在兩個數
使得
,則
。
,矛盾。
因此,這
個數有
種余數。
2)這些數模C的余數都和C互質。
假設其中一個數模C的余數與C有公因子r,則
,矛盾。
因此,這些數模C的余數包含於x。
由這兩個性質可得:
QED.
使用歐拉定理,得
。
QED.
證明完這個引理,BSGS的步驟就很簡單了。只需要考慮的情況,其他情況周期出現。(當然,
並不一定是最小正周期)對答案分塊,令
,則答案x可以寫作
的形式,即
。對於每一個i,至多有一個j對應使等式成立,預處理將
對應的j哈希掉,總復雜度
。
1 void bsgs(int u, int v) { 2 if(!u && !v) return 2; 3 else if(!u) return -1; 4 int m = ceil(sqrt(mod)); hash.clear(); 5 hash[1] = m; int p = u, q = v, iv = inv(pow(u, m)); 6 for(int i = 1; i < m; i++, p = 1ll * p * u % mod) hash[p] = i; 7 for(int i = 0; i <= m; i++) { 8 if(hash[q]) return i * m + hash[q] % m + 1; 9 q = 1ll * q * iv % mod; 10 } 11 puts("-1"); 12 }
更泛用的算法——擴展BSGS算法
BSGS非常神奇,但只能解決A和C互質的情況。擴展BSGS基於BSGS算法,但可以解決A和C不互質的情況。
令,則
。若B不是d的倍數且非1則顯然無解。
一個簡單的同余性質
原方程可以化為。令
,則可遞歸處理直到A和C互質,使用BSGS算法解決即可。
需要注意的是,原方程最后被化為了的形式。BSGS只能求解
的情況,所以需要枚舉0-num-1。
1 int exbsgs(int a, int b, int p) { 2 a %= p; b %= p; 3 if(b == 1) return 0; int cnt = 0; ll t = 1; 4 if(a == 0) return b > 1 ? -1 : b == 0 && p > 1; 5 for(int g = gcd(a, p); g != 1; g = gcd(a, p)) { 6 if(b % g) return -1; 7 p /= g; b /= g; t = 1ll * t * (1ll * a / g % p) % p; 8 cnt++; if(b == t) return cnt; 9 } 10 hash.clear(); int m = ceil(sqrt(p)); 11 ll u = b; for(int i = 0; i < m; i++, u = 1ll * u * a % p) hash[u] = i; 12 ll v = t, iv = pow(a, m, p); 13 for(int i = 0; i <= m; i++) { 14 v = 1ll * v * iv % p; 15 if(hash.count(v)) return (i + 1) * m - hash[v] + cnt; 16 } 17 return -1; 18 }