本文章内容参考自这里
定义
BSGS算法中文名叫”大步小步算法”,用来求解如下同余方程x的最小正整数解:
//这里p为素数, a、b、p已知,且0<=a,b< p(如果a,b大于p,可将他们对p取模)。
//显然若a为p的一个原根,则x即为b关于a的离散对数(mod p),故此算法可求离散对数。
算法原理
将x重写为x=i?m+jx=i?m+j
其中 m=?p–√?,i=x/m,j=x%m.m=?p?,i=x/m,j=x%m.
显然0≤i<m,0≤j<m.0≤i<m,0≤j<m.
那么我们就可以分别枚举i和j,以找到符合条件的x。
这里采用的方法是,先枚举j,将其所有的结果放入一个哈希表中,然后再枚举i,看能不能找到一个满足条件的j。
算法复杂度分析
这种做法毋庸置疑,肯定是对的。但我们得思考为什么这样做。
我们可以结合幂的性质来思考,ax+y=ax?ayax+y=ax?ay,幂的和的关系会转换成值的积,而积我们往往只会枚举sqrt(p),所以我们可以使j< sqrt(p),再使前面由?p–√??i?p??i,从而减少复杂度。所以这样做的关键在于幂的和可以变成两个数的乘积(思想很重要)。
步骤
1.令m=?p–√?m=?p?
2.枚举j,j的范围是[0,…,m)。
将ajmodpajmodp作为键存入hash表,其值为j。
3.枚举i,i的范围是[0,…m)。
设A=(am)iA=(am)i 是已知(代入i),设未知数j,满足A?(aj)≡B(modp)A?(aj)≡B(modp),那么j即为第二步枚举的j,而ajaj即为第二步放入hash表内的值,由扩展欧几里得可求出ajaj的值。在hash表里找ajaj的值,如果找到了则记录j的值,跳出此循环。
4.最后的结果即为i?m+ji?m+j。
实现
/*==================================================*\ | Baby-Step-Giant-Step 大步小步算法 | 求 a^x === b (mod p) 中的 x值 -- 此处p仅为素数 | 实际运用中用自己的hash表代替map可防TLE \*==================================================*/
LL BSGS(LL a, LL b, LL p) {a %= p; b %= p;map<LL, LL> h;LL m = ceil(sqrt(p)), x, y, d, t = 1, v = 1;for(LL i = 0; i < m; ++i) {if(h.count(t)) h[t] = min(h[t], i);else h[t] = i;t = (t*a) % p;}for(LL i = 0; i < m; ++i) {d = extgcd(v, p, x, y);x = (x* b/d % p + p) % (p);if(h.count(x)) return i*m + h[x];v = (v*t) % p;}return -1;
}