数论进阶 
数论进阶
扩展欧几里得算法
裴蜀定理(Bézout's identity)
:对于任意整数 , ,存在一对整数 , ,满足 。
2:对于任意整数 , ,二元一次不定方程 有整数解 当且仅当 。
扩展欧几里得算法(Extended Euclidean algorithm)
首先,我们来回顾一下欧几里得算法:
void gcd(int a,int b){
return b==0?a:gcd(b,a%b);
}
扩展欧几里得算法是用于在已知 , 的情况下求一组解 , ,使他们之间满足:。(GCD即最大公约数)。我们要计算的是 和 的最大公约数,并求出 和 使得 。
此时,我们已经计算出了下一个状态: 和 的最大公约数,并求出了一组 使得: 。而相邻状态之间的关系是怎样的呢?
我们知道: ,所以有:
所以: 。
特别的:在欧几里得算法执行到最后一步,即 时,我们可以得到一对整数 ,使得 。
由此便可以写出扩展欧几里得的模板(exGCD),代码如下:
typedef long long ll;
ll exgcd(ll a,ll b,ll &x,ll &y){
if (b==0) {
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return d;
}
线性同余方程
乘法逆元
中国剩余定理
扩展中国剩余定理
原根
BSGS
代码如下:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
int log_mod(ll a,ll b,ll n) {
if (b==1) return 0;
gp_hash_table<int,int> hash;
int t=ceil(sqrt(n));
ll z=1;
for (int i=0; i<t; i++) {
hash[b*z%n]=i;
z=z*a%n;
}
int now=1;
for (int i=1; i<=t; i++) {
now=now*z%n;
if (hash.find(now)!=hash.end()) {
return i*t-hash[now];
}
}
return -1;
}
高次剩余
很遗憾,这个内容作者在OIWIKI上找到,而百度百科上的内容也不全。
为了学习高次剩余部分的内容,我们先给出一个问题:
求解
我们规定: 是个质数。
在学习了原根方面的知识后,我们可以得知:p一定存在一个原根,我们假设这个原根为 。因此,对于模 意义下的任意的数 有且仅有一个数满足 满足 。
接下来将会列出解决方案。
方案一
我们令 ,由于 是 的原根(而这个 和 ,通过之前学习的方法,我们一定可以找到),那么问题将转化为求解 ,我们可以在将其变换,得到:
于是,这就变成了离散对数的基本模型了,我们可以用 解决这道题,只要在 内解出 ,这样就可以得到原方程的一个特解
方案二
仍然令 ,此时,我们在规定 ,于是我们得到:
方程两边同时取离散对数后得到:
而方程中的 可以通过 求解 得到,于是这就转化成了一个线性同余方程的问题。这样便可解除一个特解 , 的所有通解为 ,其中 为整数。
于是 的通解为: ,而我们要求的是所有在 范围内的解,可以将解一个个放进 中,直到重复(可以用 或者 来判断重复,这里我们采用的是 的方法,不了解 hash 的读者可以看这里,但我们的 hash 是直接调用的,而不是手写的)
代码如下:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
vector<int> extract_mod(int a,int b,int p){
vector<int> res;
int g=find_yg(p);
int c=log_mod(pow_mod(g,a,p),b,p);
if (c==-1) return res;
int x=pow_mod(g,c,p),t=pow_mod(g,(p-1)/gcd(a,p-1),p);
gp_hash_table<int,int> hash;
while(1){
res.push_back(x);
hash[x]=1;
x=(ll)x*t%p;
if (hash[x]==1) break;
}
return res;
}
exBSGS
代码如下:
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
int exgcd(int a,int b,int &x,int &y){
if (b==0){
x=1;
y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int t=x;
x=y;
y=t-a/b*y;
return d;
}
int exBGSG(int a,int b,int n){
if (b==1) return 0;
int cnt=0,x,y;
ll w=1;
while(1){
int d=exgcd(a,n,x,y);
if (d==1) break;
if (b%d!=0) return -1;
b/=d;
n/=d;
w=w*a/d%n;
cnt++;
if (b==w) return cnt;
}
exgcd(w,n,x,y);
b=(ll)b*(x%n+n)%n;
a=a%n;
gp_hash_table<int,int> hash;
int t=ceil(sqrt(n));
ll z=1;
for (int i=0;i<t;i++){
hash[b*z%n]=i;
z=z*a%n;
}
int now=1;
for (int i=1;i<=t;i++){
now=now*z%n;
if (hash.find(now)!=hash.end()){
return i*t-hash[now]+cnt;
}
}
return -1;
}
int main(){
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!