本文原载于: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. github-SSH模式如何配置秘钥clone远程仓库以及分支切换

    一.ssh模式clone 恕我无知,之前使用git命令都是https模式,该模式每次push都需要输入账号和密码,而且速度会根据的网速的快慢而定. 近日电脑重装了系统,在用SSH模式clone远程仓库 ...

  2. php soapclient 超时 设置

    用php的soapclient,默认是60秒.可在php.ini里配置, 重启APache 或者在PHP代码里做设置 ini_set('default_socket_timeout', 300);// ...

  3. SoapUI 访问代理设置

    SoapUI 访问代理设置 by:授客 QQ:1033553122 问题描述: 运行SoapUI时,发现接口访问不通,如下图,提示"Connection to http://127.0.0. ...

  4. 最全的android学习资料

    一.开发环境搭建 (已完成) 负责人:kris 状态:已完成 所整理标签为:搭建 SDK JDK NDK Eclipse ADT 模拟器 AVD 调试器(DEBUG) DDMS 测试 日志 Logca ...

  5. loadrunner11迭代录制注册账号

    1.创建一个新的web脚本 2.我们就以loadrunner自带的WebTours为例子 3.点击确定后进入Web Tours主页,点击sign up now进行注册 4.输入用户名:test,密码: ...

  6. 【JS单元测试】Qunit 和 jsCoverage使用方法

          近日在网上浏览过很多有关js单元测试相关的文档,工具,但是,针对Qunit 和 jsCoverage使用方法,缺少详细说明,对于初入前端的人来说,很难明白其中的意思,特此整理这篇文章,希望 ...

  7. 编写一个BAT脚本协助运维人员遇到问题时候调测数据库是否有效连接成功的操作攻略

    简单摘要: 1.内网系统出现故障需要排查 2.运维人员不熟悉数据库操作,没法通过连接数据库和执行SQL语句的方式排查数据库及数据是否正常 3.解决方案:编写一个bat脚本,运维人员双击运行即可.   ...

  8. 个人技术博客Alpha----Android Studio学习

    项目联系: 本次项目我主要负责Android studio的后端,以及游戏文案游戏策划,结果后来事情太散了,Android studio学的不咋地,文案写完还有帮着写一写数据库的插入语句,然后就是跟队 ...

  9. WebService Client Generation Error with JDK8

    java.lang.AssertionError: org.xml.sax.SAXParseException; systemId: jar:file:/path/to/glassfish/modul ...

  10. File类_常见的方法(获取目录中指定规则的内容)_listFiles

    import java.io.File; public class ListDemo { public static void main(String[] args) { ListDemo(); } ...