关于离散对数,请移步至此 => 离散对数:这个好难。。。
Baby Step Giant Step 中文名叫”大步小步算法”,用来求解如下同余方程x的最小正整数解:
ax≡b(modp)其中0<=x<p a x ≡ b ( mod p ) 其 中 0 <= x < p
//这里 p p 为素数, a、b、p a 、 b 、 p 已知,且 0<=a,b<p 0 <= a , b < p (如果a, b 大于p,可将它们对p取模)。
//显然若a为p的一个原根,则x即为b关于a的离散对数(mod p), 故此算法可求离散对数。
原理 将x重写为 x=i∗m+j x = 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,此时对每个i都看一下能不能在哈希表里找到一个满足条件的j,这样复杂度就降下来了, 有一种分块的思想。
专业解释详见 =>维基百科Baby Step Giant Step
思想 分块 哈希 中途相遇
方法 hash_table 扩展gcd
/*==================================================*\
| 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 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;
}
在上述BSGS算法的基础上, 可进一步讨论p为合数的情况,作出如下改动即可:
- 从0开始在小范围内枚举结果,万一找到了呢。
- 此时 (a,p)≠1 ( a , p ) ≠ 1 , 所以要拿出来一些a和p进行约分, 直到 (a,p)=1 ( a , p ) = 1 , 约分过程在后面给出(此处为重点!!!)。
- 约分后 (a,p)=1 ( a , p ) = 1 , 可按照基本BSGS算法处理后续步骤。
约分过程如下
初始方程为
ax≡b(modp) a x ≡ b ( mod p )
设 b′=b,p′=p b ′ = b , p ′ = p , 初始化 v=1 v = 1 ,每次约分拿出一个a, 令d=gcd(a,p′) d = g c d ( a , p ′ )
如果 d=1 d = 1 ,则约分过程结束, 约分完成。
否则的话,如果 d d 不能整除 b′ b ′ ,说明无法完成约分, x x 无解,整个算法结束。
再否则的话,更新 b′,p′,v b ′ , p ′ , v 为b′=b′d,p′=p′d,v=v∗ad b ′ = b ′ d , p ′ = p ′ d , v = v ∗ a d
然后继续循环进行下一轮约分。
其中 v v 即为 a a 每次约分剩下的没有被约掉的因子 的乘积。
约分完成后, 假设约分了cnt次,则程变为v∗ax−cnt≡b′(modp′) v ∗ a x − c n t ≡ b ′ ( mod p ′ )
此时 (a,p′)=1 ( a , p ′ ) = 1 ,显然我们可用基本的BSGS算法求得 x−cnt x − c n t 的值,算法至此结束。
具体可参考下方的实现理解。
实现
/*==================================================*\
| 扩展BSGS
| 求 a^x === b (mod p) 中的 x值 -- 此处p可为合数
| 实际运用中用自己的hash表代替map可防TLE
| 未解之谜: a, b, p 分别为1, 1, 1时, 结果x为1而非0
\*==================================================*/
LL exBSGS(LL a, LL b, LL p) {
a %= p; b %= p;
LL ret = 1;
for(LL i = 0; i <= 50; ++i) {
if(ret == b) return i;
ret = (ret*a) % p;
}//枚举比较小的i
LL x,y,d, v = 1, cnt = 0;
while((d = gcd(a, p)) != 1) {
if(b % d) return -1;
b /= d, p /= d;
v = (v * (a/d)) % p;
++cnt;
}//约分直到(a, p) == 1
map h;
LL m = ceil(sqrt(p)), t = 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] + cnt;
v = (v*t) % p;
}
return -1;
}
求离散对数
Discrete Logging POJ - 2417
Power Modulo Inverted SPOJ - MOD