大步小步算法(Shank's Baby-Step-Giant-Step Algorithm)
解模方程 ,n为素数(扩展算法处理n不是素数)
似暴力枚举。只要检查x是否=0,1,2,3,..n-1.根据欧拉定理,大于n-1时a^x会循环。
先检查前m项(暂时先记住m = sqrt(n)),检查a^0,a^1,a^2,...a^m-1模n是不是b,每次a^i的值保存在e里
求出a^m的逆a^(-m)(先要学会扩展欧几里得和逆元)。
下面考虑m之后的项:a^m , a^(m+1) ,..a^(2m-1)。
不用逐一检查,因为若a^x
≡ b(x在这个范围内),那么因为a^x可以表示为ei * a^m,所以存在ei × a^m
≡ b
利用同余性质,得到ei
≡ b a^(-m).所以只要检查ei是不是与b a^(-m)同
余即可.
后面的检查,以此类推
我们把e放入STL的map<int, int> x里(打Hash太麻烦)。x[i] = j表示满足e = i这个条件的j,同时j取最小.
LL log_mod(LL a, LL b, LL n) {
LL m = (LL)sqrt(n+0.5), v, e = 1;
v = Inv(Qpow(a, m, n), n);
map<LL, LL> x;
x.clear();
x[1] = 0;
for(LL i=1; i<m; i++) {
e = mul_mod(e, a, n);
if(!x.count(e)) x[e] = i;
}
for(LL i=0; i<m; i++) { //在m个m里寻找
if(x.count(b)) return i*m + x[b];
b = mul_mod(b, v, n);
}
return -1e9;
}