求解离散对数——BSGS算法の板子

BSGS这玩意儿好像用的不是很多?好像除了POJ和HDU有两道题,然后就是SDOI的两个题了。。然后关于这玩意儿正确性的很多证明ATP都不是很懂。。这里只是解释一下它的实现过程。。

BSGS

BSGS算法(Baby Step Gaint Step)是用于求解离散对数(即 ALB(mod C) 中的最小的 L )的一种算法。它的实质是一种优化的枚举算法,但是它通过数学分析缩小了枚举的范围,一般在 O(n) O(nlogn) (如果使用STL的map作为哈希表)的时间复杂度内就能出解。

普通的BSGS算法要求 A,C 互质。这里先贴板子再解释。。。(其中powww(a,t,Mod)是计算快速幂 at % Mod 的过程)

void BSGS(LL A,LL B,LL C){
    LL m=ceil(sqrt(C)),k=B%C,am,x;
    bool end=false;
    if (B%C==0){
        printf("No Solution\n");
        return;
    }
    hash.clear();
    hash[k]=0;
    am=powww(A,m,C);
    for (int j=1;j<=m;j++){
        k=k*A%C;hash[k]=j;
    }
    x=1;
    for (int i=1;i<=m;i++){
        x=x*am%C;
        if (hash[x]!=0){
            printf("%I64d\n",((i*m-hash[x]+cnt)%C+C)%C);
            end=true;
            return;
        }
    }
    if (end==false)
      printf("No Solution\n");
}

对于要求的式子 ALB(mod C) ,首先我们设定一个值 M ,并且设 L=iMj 。那么原来的式子就能表示成 AiMjB 。显然我们可以把 Aj 移到等式的右侧变成 AiMBAj 。那么观察有多少个“本质不同”的 j ,这里的两个“本质不同”指的是它不能通过移项变成另外一个具有 AiMBAj 形式的式子。

比如说, j=1 j=M+1 这两个就是本质相同的,因为如果取 j=M+1 的话,式子就会变成 AiMBAM+1 ,把 AM 移到左边去就变成了 A(i1)MBA1 ,也就是说它和 j=1 是等价的。

那么我们可以发现,本质不同的 j 只能是 0..M1 这些取值,一共有 M 个。那么对应的 i 也一共只有 M 种情况。如果我们能够把 i,j 都求出来那么这个问题也就解决了。我们可以把这 M 种情况所算出来的 BAj 都用哈希表映射到 j 上。

接下来我们就要寻找能够让式子有解的 i 了。这里有一点就是要尽量保证 i 最小,因为 i 为答案贡献的值是 iM ,如果能够保证 i 比较小,就算 j 比较大也没有关系,因为 j 不会超过 M 。那么就从1到 CM (C是模数)枚举 i ,算出 AiM 以后在哈希表里查,如果能查到合法的 j ,那么 iMj 就是要求的最小解。如果枚举完了所有的i都没有找到合法的j,就说明这个式子无解。

一般地,我们令 M=C 。(这玩意儿好像有个证明?然而愚蠢的ATP并不会。。)

这里还有一个问题:话说回来上面提到过本质不同的 j 只能是 0..M1 ,那我上面枚举的时候明明枚举到 M 了啊。。这里实际上是STL的map的锅,因为map里面没有东西的元素都是0,那如果我又把某个元素赋值成0的话就没法区分它到底是有没有东西。。也就是说如果当前最小的 i 对应的 j 恰好等于0,它就会认为map里面没有东西然后给判过去,这是不应该的。。而解决办法就是增加一个 j=M ,这样的话就能正确判断出答案恰好是 M 的倍数的情况。

网上还有一种写法是设 L=iM+j ,正确性也是一样的,但是操作的时候需要用到逆元。

板子题:POJ2417(BZOJ3239)/BZOJ2242/BZOJ3122

扩展算法

上面说过,BSGS算法要求 A,C 互质,那么如果 A,C 不互质怎么办呢?那么就要用到BSGS的扩展算法了。扩展算法的原理就是通过提取公因数使得 A,C 互质,然后再用普通的BSGS求解。

继续贴板子:

void ex_BSGS(LL A,LL B,LL C){
    int t=ceil(log2(C));
    flag=false;
    for (int i=0;i<=t;i++)
      if (powww(A,i,C)==B){
         printf("%d\n",i);
         flag=true;
         break;
      }
    if (flag) continue;
    cnt=0;P=1;
    d=gcd(A,C);
    while (d!=1){
        ++cnt;
        if (B%d!=0){
            printf("No Solution\n");
            flag=true;break;
        }
        B/=d;C/=d;P*=d;
        d=gcd(A,C);
    }
    if (flag) continue;
    exgcd(inv,y,P,C);
    inv=((inv%C)+C)%C;
    inv1=powww(A,cnt,C)*inv%C;
    exgcd(inv,y,inv1,C);
    inv=((inv%C)+C)%C;
    B=B*inv%C;ans=0;
    BSGS(A,B,C);
}

首先要想使 A,C 互质,我们就要提取A和C的最大公约数。假设这个公约数是d,提取完了以后就变成了 AdAL1Bd(mod Cd) 。如果 B 不能整除 d ,说明这个式子是无解的;那么现在新的 C 变成了 Cd ,如果 A C 互质,就能直接用BSGS求解了,否则继续上面的过程,不断提取最大公因数。假设提取了 t 个最大公因数,它们的乘积为 tmp ,那么最后的式子就变成了 AttmpALtBtmp(mod Ctmp) 。这个时候 A Ctmp 是互质的,用逆元把 tmp 移到等式右边就可以用BSGS求解了。

上面一堆都是ATP口胡的。。。zyf2000那里有更加严谨和详细的证明。。
板子题:BZOJ1467/HDU2815

你可能感兴趣的:(烧脑的数论,板子们)