一个经典的算法——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 }