离散对数及其拓展 大步小步算法 BSGS
离散对数及其拓展
离散对数是在群而言的,其中是素数。即在在群内,是生成元,求关于的方程的解,并将解记作,离散对数指的就是这个.由于群的阶是,且是循环群,因为生成元的阶是,因而模相等的指数可以看做一样的数,的值不妨定为的元素,即模的数,即.
以上说的是数学上对离散对数的定义,但是,搞算法竞赛人说的离散对数和这个有点差异,差异在于没有要求是生成元。
数学上的离散对数的定义,解一定存在且解模意义下唯一,而我们平常说的离散对数由于没有要求是生成元,那么解可能不存在,解模意义下不唯一,例如群中不是生成元(因为),于是以为底的离散对数,模意义下解不唯一,模意义下解才唯一;而且当的时候没有解。
所以算法竞赛中说的离散对数一般是任意的,若有解求最小非负整数解,否则返回无解。
如何求离散对数的值?由于答案在内,最简单暴力的做法是枚举中的每个数,共个数,复杂度.
更高效的做法------Shank的大步小步算法
注意到是个群,所以会有很多很好的性质,例如每个元素都有逆元。
我们选取一个数s,然后任意指数都可以表示成.在群内,有------
每一个指数都可以和一个有序对建立一一对应关系,求就变成确定的问题了。注意上面的式子最后一行,左边最多有个取值,右边是固定的,而,因此右边最多有个取值。
于是变成了右边个数在左边个数中有没有相等的元素.
这便是Shank的大步小步算法(Shank’s Baby-Step-Giant-Step
Algorithm)。左边的指数是每一加1,是小步;右边指数每次的变化是,是大步。
为了求最小的,具体地我们要从小到大枚举,计算出右边的值,查询左边个数中是否有相等的数。若有,符合条件最小的与即构成最小的解;若无,则看下一个;若所有都看完了还没有,那么就说明无解。
为了加速查询,显然对左边的个数需要预先计算出来,并建立由值查询最小的索引"数组",如果直接用值做下标的话,空间需要,但是总共才个值,所以应该用map建立"数组"或者hash(这个写麻烦一点)。
计算左边和右边所有取值的总的复杂度是.插入及查询的时间复杂度使用map是,使用hash是。空间复杂度是索引数组大小。而,取附近的数,可以获得比较好的复杂度,如果使用map的话就是,如果是使用hash的话就是.
如果map会被卡就换成hash吧。
大步小步思路
略微思考便可以发现大步小步算法的精妙在于将原本需要的次计算的问题变成了左右各次的计算加次插入和查询问题。**这可以说是一种牺牲空间换时间的一种思路。**当我们去掉在这里代表的数含义的话,如果还可以有相同的变换,那么也可以如此降低复杂度。
另外,一点小优化,也可以换成的形式,然后将式子变换成.如此,就不用求逆元了。
会发现上面式子的变换只用到了群的性质,所以换成依旧可以如此做,只是阶不是由变成了,而是变成了,事实上只是是质数时的。当然,指数算到即可,当然,算到也无妨,只是多算了一点而已,不影响结果。
当然,这就要求必须是中的元素了,即,即互质。
拓展离散对数的思考与推导
离散对数的拓展,其实是想解的方程中最小的自然数解x,其中是一般数,不保证与互质。因此必须思考与不互质如何解的问题。
基本思路是,通过方程的等价变化,将暂时无法解决的问题化为可以解决的问题。
[如果只想看结论可以直接往下看下一小节的具体算法部分。]{.underline}
不妨考虑成形式的问题,首先将其视作关于的线性同余方程,线性同余方程有解等价于.显然关于的线性同余方程有解是原方程有解的必要条件,此必要条件成立时,显然等价于。新的方程和原方程形式上相同,但是模数变小了。如此,只要与模数不互质,就重复进行如此的变换,最终一定会终止于互质的情况(当然如果某一步等价变换的必要条件不满足则直接可以得出无解结论而提前终止了)。
假设最后终止于,与已经互质,则大步小步算法解之即可。
当指数在整个整数上域取值,这一系列的变换显然等价。
拓展离散对数的具体算法
解方程的最小自然数解。
-
方程等价变换
-
将原方程通过等价变换,始终保持着的形式。
-
令。
-
迭代过程,,迭代的条件是是整数。
-
迭代终止条件:
-
不满足迭代条件返回无解
-
,终止迭代,假设终止时的取值是.
-
-
-
对迭代终止时的方程解关于的线性同余方程。
-
若线性同余方程无解,返回无解。
-
否则,假设解得。
-
若,则都在群中,大步小步算法解的最小自然数解x,终止。
-
否则,返回无解。
-
当然,最后先解线性同余方程,再进一步求x不是必要的。为了减少代码量,也可以直接再迭代终止形式的方程的基础上将调到右边,然后大步小步思想解决。事实上,大多数时候为了程序的简洁性都是如此做的。
Code
struct mod_sys{
/*other mod_sys code*/
// 这些方法和成员必须添加上
set_mod,to_std and mod are needed
// 群Z_{p}^{*}下的离散对数log_a{b} 预设p=mod是素数
// 使用大步小步算法
// a^x = b (mod p)
// 不要求a是生成元,但a mod p != 0,b mod p != 0
// 由于时间复杂度 p^0.5*ln(p) 空间复杂度p^0.5
// 故p^0.5肯定在int范围内,而且应该会更小一些
// 预设p^2不爆ll ,否则乘法需要换成quick_mlt版本
// 使用unordered_map作为查询数组
// 返回是否有解,有解的情况下,x储存最小非负整数解
bool log_p(ll a, ll b, ll &x) {
a = to_std(a); b = to_std(b);
unordered_map<ll,ll>val_to_r;
ll s = (ll)ceil(sqrt((long long)mod));
// x = ks-r r in [1,s] k in [1,(mod-2)/s+1]
// (a^s)^k=b*a^r
ll ar = 1,bar;
for (ll r = 1; r <= s; ++r) {
ar = (ar*a)%mod;
bar = (b*ar)%mod;
val_to_r[bar] = r; // 相同的k,查询应该返回最大的r,正序枚举r
}
// 循环结束,ar就是a^s
ll &as = ar;
ll ask = 1; // (a^s)^k
int t = (mod-2)/s+1;
for (int k = 1; k <= t; ++k) {
ask = (ask*as)%mod;
auto it = val_to_r.find(ask);
if (it != val_to_r.end()) {
x = k*s-it->second;
return true;
}
}
return false;
}
// n=mod>=1,无其它特殊限制
// (a,n)=1,(b,n)=1
// a^x \equiv b (%n)
// 返回是否有解
// x存储最小自然数解
bool log_n_easy(ll a,ll b, ll &x) {
// x=ks-r in [0,phi_mod)
// s \in [1,s], k in [1,(phi_mod-1)/s+1]
ll phi_mod = phi(mod);
a = to_std(a); b = to_std(b);
unordered_map<ll,ll>val_to_r;
ll s = (ll)ceil(sqrt((long long)phi_mod));
ll ar = 1,bar; // a^r, b*a^r
for (ll r = 1; r <= s; ++r) {
ar = (ar*a)%mod;
bar = (b*ar)%mod;
val_to_r[bar] = r; // 相同的k,查询应该返回最大的r,正序枚举r
}
// 循环结束,ar就是a^s
ll &as = ar;
ll ask = 1; // (a^s)^k
// ask = bar
int t = (phi_mod-1)/s+1;
for (int k = 1; k <= t; ++k) {
ask = (ask*as)%mod;
auto it = val_to_r.find(ask);
if (it != val_to_r.end()) {
x = k*s-it->second;
return true;
}
}
return false;
}
// a^x = b (%mod) a,b,mod>=1没有特殊地限制
// 返回是否有解,x存储最小自然数解
bool log_n(ll a,ll b, ll &x) {
ll q = 1,d,c=0;
ll &n = mod;
a = to_std(a); b = to_std(b);
while (true) {
if (q == b) return c;
d = __gcd(a,n);
if (1 == d) break;
if (b%d) return false;
b /= d; n /= d;
q = a/d*(q%n)%n;
a %= n;
++c;
}
// qa^{x-c} = b (%n) <===>
// 先解线性同余方程的步骤去掉,之后给log_n_easy添加一个参数q即可
ll B,N;
if (!linear_congruence_equation(q,b,n,B,N))
return false;
if (__gcd(B,N) != 1) return false;
mod = N;
bool t = log_n_easy(a,B,x);
x += c;
return t;
}
};

浙公网安备 33010602011771号