BSGS-Junior·大步小步算法
本文原载于: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·大步小步算法的更多相关文章
- [模板]大步小步算法——BSGS算法
大步小步算法用于解决:已知A, B, C,求X使得 A^x = B (mod C) 成立. 我们令x = im - j | m = ceil(sqrt(C)), i = [1, m], j = [0, ...
- 离散对数及其拓展 大步小步算法 BSGS
离散对数及其拓展 离散对数是在群Zp∗Z_{p}^{*}Zp∗而言的,其中ppp是素数.即在在群Zp∗Z_{p}^{*}Zp∗内,aaa是生成元,求关于xxx的方程ax=ba^x=bax=b的解, ...
- 离散对数&&大步小步算法及扩展
bsgs algorithm ax≡b(mod n) 大步小步算法,这个算法有一定的局限性,只有当gcd(a,m)=1时才可以用 原理 此处讨论n为素数的时候. ax≡b(mod n)(n为素数) 由 ...
- 【题解】Matrix BZOJ 4128 矩阵求逆 离散对数 大步小步算法
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4128 大水题一道 使用大步小步算法,把数字的运算换成矩阵的运算就好了 矩阵求逆?这么基础的线 ...
- 大步小步算法模板题, poj2417
大步小步模板 (hash稍微有一点麻烦, poj不支持C++11略坑) #include <iostream> #include <vector> #include <c ...
- [BSGS]大步小步算法
问题 BSGS被用于求解离散对数,即同余方程: \[ A^x\equiv B\pmod{P} \] 求\(x\)的最小非负整数解. 保证\(A\perp P\)(互质). 分析 首先,我们根据费马小定 ...
- BSGS算法(大步小步算法)
计算\(y^x ≡ z \ mod\ p\) 中 \(x\) 的解. 这个模板是最小化了\(x\) , 无解输出\(No \ Solution!\) map<ll,ll>data; ll ...
- 洛谷 - P4861 - 按钮 - 扩展大步小步算法
https://www.luogu.org/problemnew/show/P4861 把好像把一开始b==1的特判去掉就可以AC了. #include<bits/stdc++.h> us ...
- UVA 11916 Emoogle Grid 离散对数 大步小步算法
LRJ白书上的题 #include <stdio.h> #include <iostream> #include <vector> #include <mat ...
随机推荐
- JavaScript有这几种测试
译者按: 也许你讨厌测试,但是你不得不面对它,所以至少区分一下单元测试.集成测试与功能测试?对吧... 原文: What are Unit Testing, Integration Testing a ...
- map标签
map和area 标签配合img标签制作分区超链接效果 http://www.w3school.com.cn/tiy/t.asp?f=html_areamap
- VUE组件 之 倒计时(防刷新)
思路: 一.效果图: 二.CSS代码 .box{ width: 300px; height: 100px; line-height: 100px; margin: 100px auto; backgr ...
- 【转】MySQL:日期函数、时间函数总结(MySQL 5.X)
转自:http://www.cnblogs.com/she27/articles/1377089.html 一.MySQL 获得当前日期时间 函数1.1 获得当前日期+时间(date + time)函 ...
- MySQL InnoDB表和索引之聚簇索引与第二索引
MySQL InnoDB表和索引之聚簇索引与第二索引 By:授客QQ:1033553122 每个InnoDB表都有一个称之为聚簇索引(clustered index)的特殊索引,存储记录行数据.通常, ...
- JavaScript长时间未操作自动退出登录
主要是通过mouseover 来监听有没有进行当前页面操作,通过未操作时间和设定退出的时间做比较,从而退出登录. var oldTime = new Date().getTime(); var new ...
- 你不可不知的Java引用类型之——弱引用
定义 弱引用是使用WeakReference创建的引用,弱引用也是用来描述非必需对象的,它是比软引用更弱的引用类型.在发生GC时,只要发现弱引用,不管系统堆空间是否足够,都会将对象进行回收. 说明 弱 ...
- (网页)JS中的小技巧,但十分的实用!
转自CSDN: 1.document.write(”"); 输出语句2.JS中的注释为//3.传统的HTML文档顺序是:document->html->(head,body)4. ...
- 1.Spring MVC详解
目录 1.SpringMVC 详细介绍 2.SpringMVC 处理请求流程 3.配置前端控制器 4.配置处理器适配器 5.编写 Handler 6.配置处理器映射器 7.配置视图解析器 8.Disp ...
- PowerDesin把name复制到Comment,把Comment复制到Name
PowerDesin把name复制到Comment,把Comment复制到Name的方法: PowerDesigner->Tools->Execute Commands->Edit/ ...