• BSGS ! x


    一.引入:

    若存在一个式子a^b ≡ c (mod p) (p ≡ 1000000007,且0<a,b,c<p)

       已知a,b,求c.

         这不就是快速幂嘛!

      已知a,c,求b.

        这就是我们需要研究的问题!用到了BSGS!

    题目链接:poj 2417 bsgs  

    二.概念

    BSGS:

    又名大步小步算法.具体的我也不清楚啦~

    那么发明来做什么事情呢?

    如上所述:

      就是用来求解a^x ≡ b (mod p)这样的式子

        PS:已知a,b,p

            求最小x

    三.做法

    首先,我们将x用i*m-j来表示,其中我们的m=seil(sqrt(p)),(seil为向上取整)

    然后我们用i*m-j代替掉x,所以原式就变成了这个样子

            a^(i * m-j) ≡ b (mod p)

    又因为a^(i * m-j)  =>  a^(i * m)/a^j

    所以原式又变了模样:

            a^(i * m)/a^j ≡ b (mod p)

    又因为在"≡"(同余号)的两侧同时乘以一个相同的数依旧是成立的,如果不明白可以手动模拟一下,

    给出个栗子~

      3 ≡ 10 (mod 7),当两边同乘以5时式子变成: 15 ≡ 50 (mod 7)

      因为15%7==1&&50%7==1,所以原式依旧成立

    所以将式子两边同时乘以a^j

    那么式子就又发生了改变:

            a^(i * m) ≡ b * a^j (mod p)

        = (a^m)^i ≡ b * a^j (mod p)

    所以问题就简单多了!

    因为a^m是常数,b是常数,p也是常数,所以只有 i 跟 j 是未知的,暴力枚举!

    但是有一点是我们不能够忘记的:

    m的取值范围:1 —— p(p是当b==0时),所以

    首先将 j 从1 到 p-1 进行枚举一遍,求出b * a^j 的值丢到hash(大佬是用map做的)里面咯(讲真hash不会用...怪我咯?)

      思路是这样的:(其实就是hash啦~)

            (b*a^j)%p(如果p太小换成另外一个比较大的质数)作为下标,里面存着当前计算出来的数,以及当前的 j (可以用vector~) 

    然后,枚举 j ,我们大可以设a^m==c

    将 i 从 1 到p-1枚举一遍,如果求出的数在hash里面找到了,则说明当前的数就是 i (能够成立的) 最小值,跳出循环,用当前的i,以及记录下来的 j 计算对应的 i 求出 x 

    则x就是最小的解

    C++代码实现:

    (map方法:)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #include<cmath>
    
    using namespace std;
    typedef long long LL;
    
    LL a,b,c;
    map<LL,LL> mp;
    
    LL qsm(LL m)
    {
        LL n = a;
    
        if(m == 0) return 1;
    
        LL t = qsm(m/2);
    
        t = 1LL*t*t%c;
        if(m&1) t = 1LL*t*n%c;
    
        return t;
    }
    
    int main()
    {
        //a^im=b*a^j(mod c)
        while (scanf("%lld%lld%lld",&c,&a,&b)!=EOF)
        {
            mp.clear();   //清空 
            if (a%c==0)   //判断a,c 是否互质,因为c 是质数,所以直接判断是否整除即可
            {
                printf("no solution
    ");
                continue;
            }
            LL m=ceil(sqrt(c));
            LL ans;
            for (LL j=0; j<=m; j++)
            {
                if (j==0)
                {
                    //当j=0时,a^j=1, b*a^j=b
                    ans=b%c;
                    mp[ans]=j;
                    continue;
                }
                ans=(ans*a)%c;
                //括号里的ans指a^(j-1)*b,(a^(j-1)*b)*a=(a^j)*b
                mp[ans]=j;//在((a^j)*b)%c的位置记录下j
            }
            LL t=qsm(m);//t=a^m
            ans=1;
            bool p=false;
            for (LL i=1; i<=m; i++)
            {
                ans=(ans*t)%c;//括号里的ans指的是((a^m)^(i-1))*(a^m)=(a^m)^i=a^(im)
                if (mp[ans])
                {
                    LL t=i*m-mp[ans];//t=x,因为我们设的x=i*m-j
                    printf("%lld
    ",t);
                    p=true;
                    break;
                }
            }
            if (!p)
                printf("no solution
    ");
        }
    }
    View Code

    End.

    如果运气好也是错,那我倒愿意错上加错!

    ❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀❀

  • 相关阅读:
    mybatis中#{}与${}的差别(如何防止sql注入)
    使用Eclipse打开已有工程
    IntelliJ IDEA添加jar包
    【转】mysql 中int类型字段unsigned和signed的区别
    【转】MySQL int转换成varchar引发的慢查询
    数字类型转换函数
    【转】MySQL datetime数据类型设置当前时间为默认值
    【转】拍拍网2015年mysql最新开发规范
    【转】赶集网mysql开发36军规
    【转】再议数据库军规
  • 原文地址:https://www.cnblogs.com/zxqxwnngztxx/p/6880136.html
Copyright © 2020-2023  润新知