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

38 篇文章 0 订阅
8 篇文章 0 订阅

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


在这里插入图片描述

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

引理

① 若 m > 1 并 且 g c d ( a , m ) = 1 , 又 满 足 a n ≡ 1 m o d    m , 那 么 δ 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>1gcd(a,m)=1,an1modmδm(a)nδm(a)ϕ(m)

欧拉定理

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

原根

若存在与m互质的整数g,并且g模m的阶为 ϕ ( m ) \phi(m) ϕ(m),那么我们称模m有原根,并称g为模m的一个原根。
若 一 个 数 能 分 解 为 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=p^k或者x=2*p^k的形式,x才有可能有原根 x=rs,(r,s)=1r,s>2xx=pkx=2pkx

相关定理

① 一 个 正 整 数 m 有 原 根 的 充 要 条 件 是 m = 2 , 4 , p e , 2 p e , 其 中 , p 为 奇 素 数 , e 为 正 整 数 ② 每 一 个 素 数 p 都 有 ϕ ( p − 1 ) 个 原 根 , 事 实 上 , 每 一 个 正 整 数 m 都 有 ϕ ( ϕ ( m ) ) 个 原 根 。 ③ 若 g 是 m 的 一 个 原 根 , 则 g , g 2 , . . . , 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)个数的一个排列。 mm=2,4,pe,2pepepϕ(p1)mϕ(ϕ(m))gmg,g2,...,gϕ(m)mmmϕ(m)

原根的求法

( 1 ) 首 先 求 ϕ ( m ) 的 素 幂 分 解 式 : ϕ ( m ) = p 1 e 1 ∗ p 2 e 2 ∗ . . . ∗ p k e k 然 后 枚 举 g , 若 恒 满 足 g ϕ ( m ) p i ≠ 1 m o d    m , 其 中 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)=p1e1p2e2...pkekggpiϕ(m)=1modmi=1,2,...,kgm

离散对数

离散对数是一种再整数中基于同余运算和原根的对数运算。
当 模 m 有 原 根 时 , 设 G 为 模 m 的 一 个 原 根 , 则 当 : x ≡ G k m o d    k 时 , l o g G ( x ) ≡ k m o d    ϕ ( m ) 此 处 的 l o g G ( 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)的离散对数值。 mGmxGkmodklogG(x)kmodϕ(m)logG(x)xGϕ(m)

BSGS

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

穷举法

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

基础BSGS

只能解决C为素数的情况。
设 m = C 上 取 整 , x = i ∗ m + j , 那 么 A x = ( A m ) i ∗ A j , 0 ≤ i < m , 0 ≤ j < m 然 后 可 以 枚 举 i , 这 是 O ( C ) 级 别 的 枚 举 对 于 一 个 枚 举 出 来 的 i , 令 D = ( A m ) i 。 现 在 问 题 转 化 为 求 D ∗ A j ≡ B m o d    C , 如 果 把 A j 当 作 一 个 整 体 , 套 上 拓 展 欧 几 里 得 算 法 就 可 以 解 出 来 了 。 ( 而 且 因 为 C 是 质 数 , A 是 C 的 倍 数 的 情 况 容 易 特 判 , 除 此 之 外 必 有 G C D ( 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=im+jAx=(Am)iAj,0i<m0j<miO(C )iD=(Am)iDAjBmodCAjCACGCDD,C=1
求出来 A j A^j Aj,现在的问题是我怎么知道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)持续步骤三直到 g c d ( A , p ∏ i − 1 k d i ) = 1 gcd(A,\frac{p}{\prod _{i-1}^kd_i})=1 gcd(A,i1kdip)=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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值