Order and primitive root and exponential equations(阶、原根和指数方程)
一、概念
1、阶
阶:
2、原根
3、指数方程(BSGS)
BSGS(baby step giant step)
不暴力的解
二、阶
1.理解
如果
阶可以理解为循环长度的是多少。
我们不停的乘a模
eg.a = 2,m = 7
1 ——>2——>4
↑———3————|
eg2.a = 3,m = 7
1 ——>3——>2 ——>6——>4——>5
↑————————6——————————|
2.求阶
那么我们怎么去求这个阶呢?
1.(纯暴力)因为我们知道
2.阶|
3.(正解)我们求阶的时候可以从
for(每个因子pi){
while(
d/=pi
}
}
最后的这个d就是答案
eg. a = 9,m = 31,
一开始,令
对于因子
所以这里a mod m的阶是15。
从
比如上述例子:
例题:
给定素数p,回答T(1e5)个询问。
给定一个a,问最小的正数x满足ax≡1(mod p)。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1010; int pf[N],t; ll ksm(ll a,ll b,ll mod) { ll ans = 1,base = a; while(b) { if(b&1) { ans = ans*base; ans%=mod; } base = base*base; base%=mod; b>>=1; } return ans; } int main() { int p,T; cin>>p>>T; int m = p-1; for(int i = 2;i*i<=m;i++) { if(m%i==0) { pf[t++] = i; while(m%i==0)m/=i; } } if(m!=1) pf[t++] = m; for(int i = 1;i<=T;i++) { int a; cin>>a; int d = p-1; for(int i = 0;i<t;i++) while(d%pf[i]==0&&ksm(a,d/pf[i],p)==1)//pf[i]是d的因子,且a^{d/pf[i]} mod p == 1说明这个因子可以被除掉 d/=pf[i]; cout<<d<<endl; } return 0; }
三、原根(useful)
1.理解
如果
阶是在互质数 (a,m)间的定义:满足
明显,在 a,m 不互质时,a 的无论多少次幂全都是
而当互质时,依据扩展欧拉定理,必有
只有
结论(记一下):奇素数的幂次是一定有原根的,2的幂次是不一定有原根的。
2.求原根
从小到大或随机枚举
对于素数
记住
对于素数幂次,如果
如果
结论:
- g是很多的,严格意义上来说有
个 - 最小的原根不会很大
- (重要)若
是一个原根的话,所有 到 之间与 互质的数都会在循环节里面出现,可以表示为
eg.a = 3,m = 7
1 ——>3——>2 ——>6——>4——>5
↑ |
———————6—————————
例题:
回答T组询问,每次给一个素数p,输出它最小的原根g。
//给定素数p,回答T(1e5)个询问。 //给定一个a,问最小的正数x满足ax≡1(mod p)。 #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1010; int pf[N]; ll ksm(ll a,ll b,ll mod) { ll ans = 1,base = a; while(b) { if(b&1) { ans = ans*base; ans%=mod; } base = base*base; base%=mod; b>>=1; } return ans; } int main() { int p,T; cin>>T; for(int i = 1;i<=T;i++) { cin>>p; int m = p-1,t = 0; for(int i = 2;i*i<=m;i++) { if(m%i==0) { pf[t++] = i; while(m%i==0)m/=i; } } if(m!=1) pf[t++] = m; for(int g = 1;g<p;g++) { bool ok = true; int d = p-1; for(int i = 0;i<t;i++) { if(ksm(g,d/pf[i],p)==1) { ok = false; break; } } if(ok) { cout<<g<<endl; break; } } } return 0; }
三、指数方程
1.理解
指数方程
2.解法
1.暴力解法,时间复杂度O(m):
for(x = 0...
累乘,看
}
2.正解:令
eg. m = 100,对于0~99之间的数都可以表示成10q+r
那么
未知数由原来的x变为了q和r
现在问题变成了:
相当于给你两列数,问你有没有相同的数字,可以用hash或者map写,时间复杂度O(T*log)。
我们写的时候令
eg1指数方程1
给定素数p,回答T(1e5)个询问。
给定一个a,问最小的正数x满足ax≡b(mod p)。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int a,b,m; ll ksm(ll a,ll b,ll mod) { ll ans = 1,base = a; while(b) { if(b&1) { ans = ans*base; ans%=mod; } base = base*base; base%=mod; b>>=1; } return ans; } void solve() { cin>>a>>b>>m; int T = sqrt(m)+2;//小心精度误差,我们+2 ll v = ksm(a,T,m),d = 1; unordered_map<int,int>ha; for(int q = 1;q<=T;q++) { d = d*v%m; //因为我们要求较小的解,如果我们有两个相同的d,取小的那个 if(!ha.count(d))ha[d] = q*T; } int ans = m+1; d = b; for(int r = 1;r<=T;r++) { d = d*a%m; if(ha.count(d))ans = min(ans,ha[d]-r); } if(ans>=m)cout<<-1<<endl; else cout<<ans<<endl; } int main() { int t; cin>>t; while(t--) { solve(); } return 0; }
eg2.[指数方程2](指数方程2 - 题目 - Daimayuan Online Judge)
题意:回答
思路:
因为不一定互质,就不能和上一题那样写了。为什么呢?
因为
那怎么办呢?如果不互质,我们就除到它互质为止
举个例子:
现在它们互质了,可以用
最后
总结一下就是:
while((a,m)!=1)
{
if(b==1)x = t;
int g = gcd(a,m);
if(g%b)无解
else{
a/g*a^{x-t-1}≡b/g(mod m/g)
a^{x-t-1} ≡ b/g*(a/g)^{-1}(mod m/g)
}
}
每次都会除掉一个gcd,这个过程最多会迭代log次
那么写法为:
for(x = 0~30)看看有没有解
然后木有的话,直接写成a^30 * a^{x-30} ≡ b(mod m)
g = (a^30,m)
a^{x-30} ≡ b/g(a^30/g)(mod m/g)
对于
#include<bits/stdc++.h> using namespace std; typedef long long ll; int a,b,m; ll gcd(ll a,ll b) { if(b==0)return a; else return gcd(b,a%b); } ll ksm(ll a,ll b,ll mod) { ll ans = 1,base = a; while(b) { if(b&1)ans = ans*base%mod; base = base*base%mod; b>>=1; } return ans; } ll exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x = 1,y = 0; return a; } ll d = exgcd(b,a%b,y,x); y -= (a/b)*x; return d; } //求a*x = b(mod m)的解 ll modequ(ll a,ll b,ll m) { ll x,y; ll d = exgcd(a,m,x,y); if(b%d)return -1; m/=d,a/=d,b/=d; x = x*b%m; if(x<0)x+=m; return x; } int MAGIC = 30; void solve() { int a,b,m; cin>>a>>b>>m; ll v = 1; for(int i = 0;i<MAGIC;i++) { if(v == b) { cout<<i<<endl; return; } v = v*a%m; } //v * a^{x-30} = b(mod m); ll g = gcd(v,m); if(b%g) { cout<<-1<<endl; return; } b/=g,m/=g; a%=m; //v/g * ? = b/g(mod m) b = modequ(v/g,b,m); //BSGS int T = sqrt(m)+2;//小心精度误差,我们+2 v = ksm(a,T,m); ll d = 1; unordered_map<int,int>ha; for(int q = 1;q<=T;q++) { d = d*v%m; //因为我们要求较小的解,如果我们有两个相同的d,去小的那个 if(!ha.count(d))ha[d] = q*T; } int ans = m+1; d = b; for(int r = 1;r<=T;r++) { d = d*a%m; if(ha.count(d))ans = min(ans,ha[d]-r); } if(ans>=m)cout<<-1<<endl; else cout<<ans+MAGIC<<endl; } int main() { int t; cin>>t; while(t--) { solve(); } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
· 零经验选手,Compose 一天开发一款小游戏!