离散对数

       首先回忆一下初等代数里的对数。如果$a^x=b$,就是$x = log_ab$,即x是以a为底b的对数。在模算术中,也有类似的概念,但要比初等代数里的复杂一些。简单起见,这里只考虑一种最简单的情况,即当n为素数时,解模方程$a^x equiv b(mod n)$。因为n为素数,只要a不为0,一定存在逆$a^{-1}$。

       根据欧拉定理,只需检查$x=1,2, cdots,n-1$是不是解即可,因为$a^{n-1} equiv 1(mod n)$,当x超过n-1时$a^x$就开始循环了。我们先检查前m项(m的取值稍后叙述),即$a^0,a^1, cdots, a^{m-1}$模n的值是否为b,并把$a^i mod n$保存在$e_i$里,求出$a^m$的逆$a^{-m}$.

       下面考虑$a^m,a^{m+1},cdots,a^{2m-1}$.这次不用一一检查,因为如果它们中有解,则相当于存在$i$,使得$e_i imes a^mequiv b(mod n)$,两边左乘$a^{-m}$得${b}' equiv a^{-m}b(mod n)$。这样只需检查是否真存在$e_i$等于这个${b}'$即可。为了方便,可以把$e_i$保存在一个$STL$集合中,但考虑还需要得到具体的下标$i$,我们用$map<int,int>x$,其中$x[j]$表示满足$e_i = j$的最小的$i$(因为可能有多个$e_i$的值相同)。在下面的代码中,${b}'$采用递推计算,覆盖了输入参数$b$。

//求解模方程a^x=b(mod n)。n为素数,无解返回-1
int log_mod(int a, int b, int n)
{
    int m, v, e = 1, i;
    m = (int)sqrt(n + 0.5);
    v = inv(pow_mod(a, m, n), n);
    map<int, int>x;
    x[1] = 0;
    for (i = 1; i < m; i++)
    {
        e = mul_mod(e, a, n);
        if (!x.count(e))  x[e] = i;
    }
    for (i = 0; i < m; i++)    //m*i < n,当m=√n,i < m
    {
        if (x.count(b))  return i * m + x[b];
        b = mul_mod(b, v, n);
    }
    return -1;
}

        上面的代码中,$m=n^{1/2}$,这是为什么呢?因为计算e数组需要$O(mlogm)$时间,以后每一轮需要$O(logm)$时间,一共$O(n/m)$轮,因此总的时间复杂度为$O((m+n/m)logm)$,当$m$和$n/m$接近,即$m=n^{1/2}$时总时间最短,为$O(n^{1/2}logn)$。这个算法称为Shank的大步小步算法$({Shank}'s Baby-Step-Giant-Step Algorithm)$.

        当$n$不是素数时,还可以把这个算法加以扩展。

易只,BSGS有两种形式:

$$a^{km+t}equiv(mod p), a^{km-t}equiv b(mod p)$$

第一种形式是经典的BSGS,并可以应用到EXBSGS中,而第二种形式的优点在于不需要求逆元,比如 $a, b$ 是矩阵时。

按照BSGS的思路,可化成 $a^{km}equiv b*a^t(mod p)$,

于是我们便可以把 $b*a^t(mod p)$ 存到 map 中,然后枚举 $k$ 的取值来查询。

map<int,int>mp;
int bsgs(int a, int b, int p){    //a^x = b (mod P),(a,p)=1,返回x,x>=1; 无解返回-1
    int m=sqrt(p)+1;mp.clear();
    for(register int i=0,res=b;i<m;++i,res=1ll*res*a%p)mp[res]=i;
    for(register int i=1,tmp=qpow(a,m,p),res=tmp;i<=m+1;++i,res=1ll*res*tmp%p)
        if(mp.count(res))return i*m-mp[res];
    return -1;
}

参考链接:

1. 思路:https://www.cnblogs.com/GXZlegend/p/7055000.html

2. 代码:https://www.cnblogs.com/bztMinamoto/p/10348641.html

原文地址:https://www.cnblogs.com/lfri/p/10455248.html