本文原载于:http://www.orchidany.cf/2019/02/06/BSGS-junior/#more

\(\rm{0x01}\) \(\mathcal{Preface}\)

\(\rm{BSGS}(Baby~Step~Giant~Step)\), 大步小步法。当然也会被叫做拔山盖世北上广深算法……咳,这并不重要。形式化地讲, \(\rm{BSGS}\)算法主要用来解决以下问题 :

给定质数\(p\), 整数\(a, b\), \((a, p)=1\).求最小的非负整数\(x\), 使得\(a^x≡ b~(\mod p)\)

而首先我们知道的,是由欧拉定理\(a ^{\varphi(p)} ≡ 1 ~(\mod p)​\),并且我们还知道\(a^0=1≡1 ~(\mod p)​\),所以我们可以得出一个断言:

如果方程\(a^x≡ b~(\mod p)​\)有最小非负整数解,那么最小非负整数解一定在\([0, \varphi(p))​\)中 \(\qquad \qquad(1) ​\)

此处肉眼可以看出其循环节为\(\varphi(p)\),不再证明。

之后我们将以此为基础进行类似分块的操作——

\(\rm{0x02~~Baby~Step~Giant~Step}\)

首先我们记\(n=\sqrt {\varphi(p)}\),那么\(\forall x \in [0, \varphi(p))\), \(x = i\times m+j\), \(i \leq \lfloor \frac{p−1-m}{m} \rfloor,~~ 0≤j <m\) 。那么对于原方程我们可以把其改为:$$a^{i\cdot n+j}≡ b~(\mod p)$$移一下项就可以变成$$a^j ≡b \cdot a^{-i\cdot n} (\mod p)$$那么现在我们的策略是算出所有\(a^j\)来,在\(\mod p\) 意义下观察是否有一个\(i\)使得\(a^j ≡b \cdot a^{-i\cdot n} (\mod p)\)。我们称左边枚举\(a^j\)叫做小步\((\rm{Baby~Step})\), 称右边枚举\(b \cdot a^{-i\cdot n}\)叫做大步\(~(\rm{Giant~Step})\)

那么其实算法流程很明晰了,我们只需要循环两次、第一次记录的\(a^j\)用哈希表(\(STL\)自带\(unordered\)_ \(map\))记录一下即可。

inline LL expow(LL a, LL b, LL p){
LL res = 1 ;
while (b){
if (b & 1)
(res *= a) %= p ;
(a *= a) %= p, b >>= 1 ;
}
return res % p ;
}
inline void bsgs(LL x, LL y, LL p){
P = ceil(sqrt(p)), Hash.clear(), Q = expow(x, -P + 2 *(p - 1), p) ;
//a ^ (p-1) = 1 (mod p) => Q = a^(-P) = a ^(-P + p -1) ;
for (LL i = 1, j = 0 ; j < P ; ++ j, (i *= x) %= p)
if (!Hash.count(i)) Hash[i] = j ; // Push them into hash_table
for (LL i = y, j = 0 ; j <= P ; ++ j, (i *= Q) %= p)
if (Hash.count(i)){ cout << Hash[i] + j * P << endl ; return ; }
cout << "-1" << endl ;
}

其中细节还是有的:

  • 计算sqrt时要上取整

  • 我们在求\(a^{-i\cdot n}​\)时用的底变量需要由费马小定理求快速幂得出。但是此时指数上可能为负数,所以我们选择加上一个模数,不影响结果。

  • 两次循环枚举的边界要注意有的是\(\leq\)有的是\(<\)

  • 算法还没开始时,要判断本身\(a\)是否可以被\(P\)整除。如果不特判这种情况的话,我们上面代码中的Q就会=0,从而在下面的第二个循环处出错——我们的hash[i]j不能同时为\(0\),从而输出错误的答案。

\(\rm{0x03}\) 例题

\(T1~\)\(LuoguP4028\)

裸题,但是有很多坑……或者说上面列举的细节都涵盖了qaq

#include <cmath>
#include <cstdio>
#include <iostream>
#include<tr1/unordered_map> #define LL long long using namespace std ;
using namespace tr1 ; int T ;
LL A, B, M, P, Q ; unordered_map <LL, LL> Hash ; inline LL expow(LL a, LL b, LL p){
LL res = 1 ;
while (b){
if (b & 1)
(res *= a) %= p ;
(a *= a) %= p, b >>= 1 ;
}
return res % p ;
}
inline void bsgs(LL x, LL y, LL p){
P = ceil(sqrt(p)), Hash.clear(), Q = expow(x, -P + 2 *(p - 1), p) ;
//a ^ (p-1) = 1 (mod p) => Q = a^(-P) = a ^(-P + p -1) ;
for (LL i = 1, j = 0 ; j < P ; ++ j, (i *= x) %= p)
if (!Hash.count(i)) Hash[i] = j ; // Push them into hash_table
for (LL i = y, j = 0 ; j <= P ; ++ j, (i *= Q) %= p)
if (Hash.count(i)){ cout << Hash[i] + j * P << endl ; return ; }
cout << "Couldn't Produce!" << endl ;
}
inline LL qr(){
LL res = 0 ; char c = getchar() ; while (!isdigit(c)) c = getchar() ;
while (isdigit(c)) res = (res << 1) + (res << 3) + c - 48, c = getchar() ;
return res ;
}
int main(){
cin >> T ;
while (T --){
M = qr(), A = qr(), B = qr() ;
if ((!(A % M == 0 && B))) bsgs(A, B, M) ;
else cout << "Couldn't Produce!" << endl ;
}
return 0 ;
}

\(T2~\) \(TJOI2007~Cute~Prime​\)

最裸最裸的、无特判的题……可以水一下双倍经验。

\(\mathfrak{writter: pks}\)

BSGS-Junior·大步小步算法的更多相关文章

  1. [模板]大步小步算法——BSGS算法

    大步小步算法用于解决:已知A, B, C,求X使得 A^x = B (mod C) 成立. 我们令x = im - j | m = ceil(sqrt(C)), i = [1, m], j = [0, ...

  2. 离散对数及其拓展 大步小步算法 BSGS

    离散对数及其拓展 离散对数是在群Zp∗Z_{p}^{*}Zp∗​而言的,其中ppp是素数.即在在群Zp∗Z_{p}^{*}Zp∗​内,aaa是生成元,求关于xxx的方程ax=ba^x=bax=b的解, ...

  3. 离散对数&&大步小步算法及扩展

    bsgs algorithm ax≡b(mod n) 大步小步算法,这个算法有一定的局限性,只有当gcd(a,m)=1时才可以用 原理 此处讨论n为素数的时候. ax≡b(mod n)(n为素数) 由 ...

  4. 【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 大水题一道 使用大步小步算法,把数字的运算换成矩阵的运算就好了 矩阵求逆?这么基础的线 ...

  5. 大步小步算法模板题, poj2417

    大步小步模板 (hash稍微有一点麻烦, poj不支持C++11略坑) #include <iostream> #include <vector> #include <c ...

  6. [BSGS]大步小步算法

    问题 BSGS被用于求解离散对数,即同余方程: \[ A^x\equiv B\pmod{P} \] 求\(x\)的最小非负整数解. 保证\(A\perp P\)(互质). 分析 首先,我们根据费马小定 ...

  7. BSGS算法(大步小步算法)

    计算\(y^x ≡ z \ mod\ p\) 中 \(x\) 的解. 这个模板是最小化了\(x\) , 无解输出\(No \ Solution!\) map<ll,ll>data; ll ...

  8. 洛谷 - P4861 - 按钮 - 扩展大步小步算法

    https://www.luogu.org/problemnew/show/P4861 把好像把一开始b==1的特判去掉就可以AC了. #include<bits/stdc++.h> us ...

  9. UVA 11916 Emoogle Grid 离散对数 大步小步算法

    LRJ白书上的题 #include <stdio.h> #include <iostream> #include <vector> #include <mat ...

随机推荐

  1. Servlet—Cookie(显示用户上次访问时间、显示商品浏览历史)

    1 . 什么是会话? 会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话. 1.1 会话过程中要解决的一些问题? 每个用户在使用浏览器 ...

  2. 前端的业余设计-about my 毕业季

    ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ...

  3. H5调拨打电话界面

    <a href=”tel:15771791266 ”>拨打电话</a> 切记不要用js调用  直接用a标签 苹果安卓塞班都能调起来

  4. ASP.NET MVC4分页Site.CSS

    ASP.NET MVC 4 的基础分页的CSS样式: body { font-family: Calibri,Georgia,"Times New Roman"; margin:; ...

  5. Python sys 模块

    import sys # 把命令行参数返回一个 List,第一个元素是程序本身的路径 print(sys.argv) # 命令行运行 python3 sys_model.py klvchen hell ...

  6. loadrunner 脚本开发-字符串编码转换

    字符串编码转换 by:授客 QQ:1033553122   相关函数 lr_convert_string_encoding函数 功能:字符串编码转换 原型: int lr_convert_string ...

  7. 利用python中的gensim模块训练和测试word2vec

    word2vec的基础知识介绍参考上一篇博客和列举的参考资料. 首先利用安装gensim模块,相关依赖如下,注意版本要一致: Python >= 2.7 (tested with version ...

  8. 为Hexo Next主题添加分享及打赏功能

    博客地址:往事亦如风的博客 要想先看打赏和分享功能效果,请移步我的博客 打赏功能 因为next主题自带打赏功能,所以我们只需要在next的主题配置文件中找到如下代码,image文件夹是我在blog/s ...

  9. layer层、modal模拟窗 单独测试页面

    layer_test.jsp <%@ page language="java" import="java.util.*" pageEncoding=&qu ...

  10. xunit-ICollectionFixture

      https://github.com/dmetzgar/dotnetcoreinaction