• 离散对数


           首先回忆一下初等代数里的对数。如果$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

  • 相关阅读:
    charles使用
    断言
    JDBC Request
    HTTP请求建立一个测试计划
    利用badboy进行脚本录制
    接口测试用例
    Monkey常用命令
    charles安装与使用
    celery配置与基本使用
    图片验证码接口
  • 原文地址:https://www.cnblogs.com/lfri/p/10455248.html
Copyright © 2020-2023  润新知