title : 离散对数与BSGS
date : 2021-8-12
tags : ACM,数论
author Linno


对与m互质的整数a,我们记满足an≡1modma^n\equiv 1\mod man≡1modm​的最小正整数n为a模m的阶,记为δm(a)\delta_m(a)δm​(a)。

引理

①若m>1并且gcd(a,m)=1,又满足an≡1modm,那么δm(a)∣n②由欧拉定理和定理一得:δm(a)∣ϕ(m)①若m>1并且gcd(a,m)=1,又满足a^n\equiv 1\mod m,那么\delta_m(a)|n\\ ②由欧拉定理和定理一得:\delta_m(a)|\phi(m) ①若m>1并且gcd(a,m)=1,又满足an≡1modm,那么δm​(a)∣n②由欧拉定理和定理一得:δm​(a)∣ϕ(m)

欧拉定理

若a与m互质,则aϕ(m)≡1modm若a与m互质,则a^{\phi(m)}\equiv1\mod m若a与m互质,则aϕ(m)≡1modm

原根

若存在与m互质的整数g,并且g模m的阶为ϕ(m)\phi(m)ϕ(m),那么我们称模m有原根,并称g为模m的一个原根。
若一个数能分解为x=r∗s,(r,s)=1,且r,s>2,那么x必定无原根若满足x=pk或者x=2∗pk的形式,x才有可能有原根若一个数能分解为x=r*s,(r,s)=1,且r,s>2,那么x必定无原根\\ 若满足x=p^k或者x=2*p^k的形式,x才有可能有原根 若一个数能分解为x=r∗s,(r,s)=1,且r,s>2,那么x必定无原根若满足x=pk或者x=2∗pk的形式,x才有可能有原根

相关定理

①一个正整数m有原根的充要条件是m=2,4,pe,2pe,其中,p为奇素数,e为正整数②每一个素数p都有ϕ(p−1)个原根,事实上,每一个正整数m都有ϕ(ϕ(m))个原根。③若g是m的一个原根,则g,g2,...,gϕ(m),各数对m取模的非负最小剩余就算小于m且与m互质的ϕ(m)个数的一个排列。①一个正整数m有原根的充要条件是m=2,4,p^e,2p^e,其中,p为奇素数,e为正整数\\ ②每一个素数p都有\phi(p-1)个原根,事实上,每一个正整数m都有\phi(\phi(m))个原根。\\ ③若g是m的一个原根,则g,g^2,...,g^{\phi(m)},\\各数对m取模的非负最小剩余就算小于m且与m互质的\phi(m)个数的一个排列。 ①一个正整数m有原根的充要条件是m=2,4,pe,2pe,其中,p为奇素数,e为正整数②每一个素数p都有ϕ(p−1)个原根,事实上,每一个正整数m都有ϕ(ϕ(m))个原根。③若g是m的一个原根,则g,g2,...,gϕ(m),各数对m取模的非负最小剩余就算小于m且与m互质的ϕ(m)个数的一个排列。

原根的求法

(1)首先求ϕ(m)的素幂分解式:ϕ(m)=p1e1∗p2e2∗...∗pkek然后枚举g,若恒满足gϕ(m)pi≠1modm,其中i=1,2,...,k则g是m的一个原根(1)首先求\phi(m)的素幂分解式:\phi(m)=p_1^{e_1}*p_2^{e_2}*...*p_k^{e_k}\\ 然后枚举g,若恒满足g^{\frac{\phi(m)}{pi}}\neq1\mod m,其中i=1,2,...,k\\则g是m的一个原根 (1)首先求ϕ(m)的素幂分解式:ϕ(m)=p1e1​​∗p2e2​​∗...∗pkek​​然后枚举g,若恒满足gpiϕ(m)​​=1modm,其中i=1,2,...,k则g是m的一个原根

离散对数

离散对数是一种再整数中基于同余运算和原根的对数运算。
当模m有原根时,设G为模m的一个原根,则当:x≡Gkmodk时,logG(x)≡kmodϕ(m)此处的logG(x)是x以整数G为底模ϕ(m)的离散对数值。当模m有原根时,设G为模m的一个原根,\\ 则当:x\equiv G^k \mod k时,log_G(x)\equiv k \mod \phi(m)\\ 此处的log_G(x)是x以整数G为底模\phi(m)的离散对数值。 当模m有原根时,设G为模m的一个原根,则当:x≡Gkmodk时,logG​(x)≡kmodϕ(m)此处的logG​(x)是x以整数G为底模ϕ(m)的离散对数值。

BSGS

Baby-Step-Giant-Step及其拓展算法(Extended BSGS)是用来求解Ax≡BmodC(0≤x<C)A^x\equiv B\mod C(0\le x<C)Ax≡BmodC(0≤x<C)​​类型问题(高次同余方程)的算法,以空间换时间,是对穷举法的一个改进。

穷举法

由费马小定理得 如果方程有解,那么一定在循环节[0,C-1]之中(费马小定理),只需要在循环节中枚举x就可以求出方程的解。(p很大的时候就会爆)

基础BSGS

只能解决C为素数的情况。
设m=C上取整,x=i∗m+j,那么Ax=(Am)i∗Aj,0≤i<m,0≤j<m然后可以枚举i,这是O(C)级别的枚举对于一个枚举出来的i,令D=(Am)i。现在问题转化为求D∗Aj≡BmodC,如果把Aj当作一个整体,套上拓展欧几里得算法就可以解出来了。(而且因为C是质数,A是C的倍数的情况容易特判,除此之外必有GCD(D,C)=1,所以一定有解。设m=\sqrt C上取整,x=i*m+j,那么A^x=(A^m)^i*A^j,0\le i<m,0\le j<m\\ 然后可以枚举i,这是O(\sqrt C)级别的枚举\\ 对于一个枚举出来的i,令D=(A^m)^i。现在问题转化为求D*A^j\equiv B\mod C,\\如果把A^j当作一个整体,套上拓展欧几里得算法就可以解出来了。\\(而且因为C是质数,A是C的倍数的情况容易特判,\\除此之外必有GCD(D,C)=1,所以一定有解。 设m=C​上取整,x=i∗m+j,那么Ax=(Am)i∗Aj,0≤i<m,0≤j<m然后可以枚举i,这是O(C​)级别的枚举对于一个枚举出来的i,令D=(Am)i。现在问题转化为求D∗Aj≡BmodC,如果把Aj当作一个整体,套上拓展欧几里得算法就可以解出来了。(而且因为C是质数,A是C的倍数的情况容易特判,除此之外必有GCD(D,C)=1,所以一定有解。
求出来AjA^jAj,现在的问题是我怎么知道j是多少?先用O(C)O(\sqrt C)O(C​)的时间,将A^j全部存进hash表里面,然后只要查表就在O(1)的时间内知道j是多少了。

luoguP2485 [SDOI2011]计算器
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;ll fpow(ll a,ll b,ll mod){ll res=1;while(b){if(b&1) res=res*a%mod;a=a*a%mod;b>>=1; }return res;
}ll exgcd(ll a,ll b,ll &x,ll &y){if(b==0){x=1,y=0;return a;}ll gcd=exgcd(b,a%b,y,x);y-=a/b*x;return gcd;
}void bsgs(ll y,ll z,ll p){z%=p;if(y%p==0){if(z){cout<<"Orz, I cannot find x!"<<endl;return;   }else{if(p==1){cout<<0<<endl;return;}else{cout<<1<<endl;return;} }}ll m=ceil(sqrt(p));map<ll,ll>mp;ll now=z%p,f=fpow(y,m,p);mp[now]=0;for(int j=1;j<=m;j++){now=now*y%p;mp[now]=j;}now=1;for(int i=1;i<=m;i++){now=now*f%p;if(mp[now]){cout<<((i*m-mp[now])%p+p)%p<<endl;return;}}cout<<"Orz, I cannot find x!"<<endl;
}int t,k,y,z,p;signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);cin>>t>>k;if(k==1){for(int i=1;i<=t;i++){cin>>y>>z>>p;   cout<<fpow(y,z,p)%p<<endl; }}else if(k==2){for(int i=1;i<=t;i++){cin>>y>>z>>p;ll x,d;ll gcd=exgcd(y,p,x,d);if(z%gcd) cout<<"Orz, I cannot find x!"<<endl;else{ll tmp=p/gcd;while(x<0) x+=tmp;cout<<((x*z/gcd)%tmp+tmp)%tmp<<endl;}}}else if(k==3){for(int i=1;i<=t;i++){cin>>y>>z>>p;bsgs(y,z,p);}}return 0;
}
拓展BSGS

不要求C为素数,开始前先执行消除因子。

$a\equiv b\mod p 可转化为可转化为可转化为\frac{a}{c}\equiv\frac{b}{c}\mod \frac{p}{c}$

那么$a^x\equiv b\mod p 可转化为可转化为可转化为a{x-k}\frac{ak}{\prod _{i-1}^kd_i}\equiv\frac{b}{\prod _{i-1}^kd_i}\mod \frac{p}{\prod _{i-1}^kd_i}$

步骤

(1)若b==1 那么x=0,算法结束;

(2)若gcd(a,p)不能整除b,则无解,算法结束;

(3)若gcd(a,p)!=1,令d=gcd(a,p),若d不能整除b,则无解,算法结束;

(4)持续步骤三直到gcd(A,p∏i−1kdi)=1gcd(A,\frac{p}{\prod _{i-1}^kd_i})=1gcd(A,∏i−1k​di​p​)=1,我们就可以直接用普通BSGS求出结果,最后加上k即可。

luoguP4195 【模板】扩展 BSGS/exBSGS
#include<bits/stdc++.h>
#define LL long long
using namespace std;
int read(){ int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
unordered_map<int,int>mp;
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int BSGS(int a,int n,int p,int ad){mp.clear();int m=ceil(sqrt(p)),s=1;for(int i=0;i<m;i++,s=1ll*s*a%p) mp[1ll*s*n%p]=i;for(int i=0,tmp=s,s=ad;i<=m;i++,s=1ll*s*tmp%p)if(mp[s]) if(1ll*i*m-mp[s]>=0) return 1ll*i*m-mp[s];return -1;
}
inline int exBSGS(int a,int n,int p){a%=p;n%=p;if(n==1||p==1) return 0;int cnt=0;int d,ad=1;while((d=gcd(a,p))^1){if(n%d) return -1;cnt++;n/=d;p/=d;ad=(1ll*ad*a/d)%p;if(ad==n) return cnt;}LL res=BSGS(a,n,p,ad);if(res==-1) return -1;return res+cnt;
}
signed main(){int a=read(),p=read(),n=read(),ans;while(a||p||n){ans=exBSGS(a,n,p);if(~ans) printf("%d\n",ans);else puts("No Solution");a=read();p=read();n=read();}return 0;
}

参考资料

https://www.cnblogs.com/cytus/p/9296661.html

https://www.bilibili.com/video/BV14A411h7oD

https://www.bilibili.com/video/BV17f4y1v7D5

【算法竞赛学习笔记】离散对数与BSGS-数学提升计划相关推荐

  1. 【算法竞赛学习笔记】佩尔方程-数学提升计划

    title : 佩尔方程 date : 2021-10-31 tags : ACM,数学 author : Linno 佩尔方程 形如x2−dy2=1(d>1且d不为完全平方数)x^2-dy^2 ...

  2. 【算法竞赛学习笔记】pb_ds-超好懂的数据结构

    title : pb_ds date : 2021-8-21 tags : ACM,数据结构 author : Linno 简介 pb_ds库全称Policy-Based Data Structure ...

  3. 【算法竞赛学习笔记】快速傅里叶变换FFT-数学提高计划

    tilte : 快速傅里叶变换FFT学习笔记 tags : ACM,数论 date : 2021-7-18 简介 FFT(Fast Fourier Transformation),中文名快速傅里叶变换 ...

  4. 学习笔记之12个月提升计划

    Java世界博大精深,有太多的东西要学.如果一头扎进去,很可能会淹没在Java技术的海洋里.于是,最近一直在思考列一个提纲,作为高级工程师到资深.再到架构之路的路标. 学习笔记一栏,即为本计划的博客记 ...

  5. 【算法竞赛学习笔记】KD-Tree

    title : KD-Tree date : 2022-4-7 tags : ACM,数据结构 author : Linno K-D tree K-D树是在k维欧几里得空间中组织点的数据结构.在算法竞 ...

  6. 【算法竞赛学习笔记】莫队算法-超优雅的暴力算法

    title : 莫队算法 tags : ACM,暴力 date : 2021-10-30 author : Linno 普通莫队 常用操作:分块/排序/卡常/离散化等,直接上板子. luoguP270 ...

  7. 【算法竞赛学习笔记】状压DP

    title : 状压DP date : 2022-3-5 tags : ACM,图论,动态规划 author : Linno 状压DP 状态压缩,是利用二进制数的性质对问题进行优化的一种算法,经常与搜 ...

  8. 【算法竞赛学习笔记】Link-Cut-Tree基础-超好懂的数据结构

    titile : Link-Cut-Tree time : 2021-7-21 tags : ACM,数据结构 author : Linno LCT Link-Cut-Tree,中文名为动态树,是一种 ...

  9. 【算法竞赛学习笔记】超好懂的斯坦纳树详解!!!

    title : 斯坦纳树 tags : ACM 图论 date : 2021-6-26 author : Linno 什么是斯坦纳树 给定 n 个点 A1,A2,⋯,An试求连接此n个点,总长最短的直 ...

最新文章

  1. Xilinx 在文档中所用的 LC(logic cells) 与 LUT之间的换算关系
  2. linux中sqlplus不能用_装修中不能用海沙,但是海沙已经偷偷走进了你的家
  3. npoi 所有列调整为一页_必能用到,一页PPT中想放超多图片怎么办?
  4. mosca mysql_nodejs 完成mqtt服务端
  5. java class private_Java class 中public、protected 、friendly、private的区别
  6. 2020-2021家居行业年度盘点与趋势洞察
  7. uefi引导linux_使用UEFI双重引导Windows和Linux
  8. 数仓ETL系统:给强大的“心脏”配上“超级流水线”
  9. 2018第一场多校 -补题
  10. 序列生成_Excel中最快速地生成数字序列的方法
  11. 利用T-SQL动态定义重复SQL Server数据库表行
  12. latex数学公式符号 + markdown操作(图片的缩放、居中等)
  13. Another app is currently holding the yum lock; waiting for it to exit
  14. java教程:十分钟理解Java中的弱引用
  15. H264---H.264/AVC 的各大主流编解码器JM、x264、Xvid、T264、ffmpeg、ffdshow + JMHM源码分析
  16. 阿根廷探戈----中英文对照
  17. PHP实现身份证认证和银行卡认证
  18. Java中的equals和==比较
  19. 知识竞赛答题小程序的管理后台搭建教程
  20. 路由器mw320虚拟服务器,新版水星MW320R路由器怎么设置_新版水星MW320R路由器设置教程-192路由网...

热门文章

  1. 面向全局搜索的自适应领导者樽海鞘群算法-附代码
  2. 版本控制工具之TFS的使用
  3. 使用Canvas绘制不完美/不规则的圆形
  4. 文本分类算法TextCNN
  5. 13、微信小程序:上传图片到服务器
  6. 批量html转换word,用Word宏来实现批量将HTML转换成DOC
  7. PDF转Excel表格怎么转换?这个方法其实很简单
  8. license生成方案
  9. [SQL-码农]SQL server 无法打开数据库 'msdb'。恢复操作已将该数据库标记为 SUSPECT
  10. 红米k30可以用鸿蒙系统吗,红米K30遭狙击:挖孔双摄+麒麟990+鸿蒙OS系统 华为5G强势来袭...