【瞎口胡】基础数学 2 裴蜀定理 exgcd 同余相关
写在前面#
在第一篇当中我们介绍了一点基本知识。
第二篇的内容较第一篇稍有难度,但是也非常基础,相信只要学过小学数学就能看明白。
裴蜀定理与扩展欧几里得算法#
-
裴蜀定理
对于任意非负整数 :
-
任意整数 都满足 是 的倍数。
证明:,则一定有 。
-
存在整数 使得 。
证明:数学归纳法。回忆欧几里得算法的最后一步,,取 ,即有 。
根据欧几里得算法的流程,。
因此有 。对于每一层的 都是如此,在回溯完成之后就会得到一组正确的解。
-
-
扩展欧几里得算法
简称扩欧,exgcd。
按照证明裴蜀定理的方式,将欧几里得算法稍作修改:
inline int exgcd(int _a,int _b,int &_x,int &_y){ if(!_b){ _x=1,_y=0; // 最后一步 return _a; } int g=exgcd(_b,_a%_b,_x,_y),Temp; // 先算出下一层的答案 Temp=_x,_x=_y,_y=Temp-(_a/_b)*_y; // 计算这一层的答案 注意顺序是先算 x 再算 y,还需要一个辅助变量 return g; }
-
应用
-
求方程 的解集
考虑我们 exgcd 求出了该方程的一组特解 。我们希望在此基础上将 加上一个值,设其为 。
那么它们必须满足 。即
将 约分,设其最简形式为 ,显然 。此时有
因为 是整数,因此 必须是 的倍数。设 ,则 ,其中 是任意整数。
综上, 的解集为 。
-
求方程 的解集
由裴蜀定理, 一定是 的倍数。因此如果 ,该方程无解。
令 。
令 ,此时用扩欧求出该方程的解 ,则原方程的一组特解为 。
利用求 时的证明方法,可以知道该方程的解集为 。
-
同余#
对于整数 和正整数 ,若 ,则成 ,读作「 在模 意义下与 同余」。
基本性质#
-
性质 :自反性
-
性质 :对称性
-
性质 :传递性
-
性质 :可加、可乘性
-
性质
当 与 互质的时候:
证明:考虑 的充要条件是 。
可以推出 ,即 。当 与 互质的时候, 才一定被 整除。换句话说, 是上式成立的充分条件。
性质 非常重要,一定要牢记只有 的时候该性质才成立。
剩余系与剩余类#
所有对 同余的整数构成模 的一个剩余类。考虑一个整数 的取值范围是 ,于是剩余类显然有 个。若某个剩余类中的整数模 的结果与 互质,则称该剩余类为互质剩余类。
注意,模 为 的整数构成的剩余类不是互素剩余类。
完全剩余系:在所有同余类中各任取一个整数,它们构成了模 的一个完全剩余系。
简化剩余系(缩系):在所有互质剩余类中任取一个整数,它们构成了模 的一个缩系。
完全剩余系和缩系的个数是无限的。
费马小定理#
对于质数 和任意满足 的 ,有
-
引理
对于满足上述条件的 , 构成模 的一个缩系。
证明:显然上面的 个数与 互质。如果存在 使得 ,由性质 将两边同时除以 ,得 ,因为 ,即 ,与假设矛盾。
显然
由性质 ,将两边化简并调换位置得
欧拉定理#
对于任意模数 和任意满足 的 ,有
其中 表示小于 的正整数中与 互质的个数。
证明和证明费马小定理类似,不再赘述。
注意到费马小定理是欧拉定理的特例。
扩展欧拉定理#
对于任意 ,有
先取 的一个质因子 ,设 ,其中 。则有 。又 ,即 是 的倍数,那么也有 。
设 ,那么 ,。
观察到
注意到,当 时,上式才一定成立。这是因为 不保证互质,此时 的负数次幂(即逆元)在模 意义下不一定有定义。
注意到 ,那么又有 。我们可以说,当 时,。令 ,那么我们可以说,当 时,,从而得证。
乘法逆元#
对于整数 和模数 ,称 的整数 为 在模 意义下的乘法逆元。 的乘法逆元记为 。
求法 :对于 是质数的情况下,由费马小定理有 ,则 的乘法逆元为 。
求法 :对于 不是质数的情况下,有 ,此时 为定值,使用 exgcd 求出 的最小非负整数解即可。
上述求法的时间复杂度均为 ,由求法 可知, 在模 意义下存在逆元的充要条件是 。
求法 :在已知 的情况下,怎样快速求出 的每一个数在模 意义下的逆元?
我们有一个线性的做法。对于 ,如果我们知道 中每一个数的逆元,那么将 写成 的形式,则有:
左右两侧同乘 :
整理得
。
long long inv[MAXN];
......
n=read(),m=read(); // m 为模数
inv[1]=1;
for(rr int i=2;i<=n;++i){
inv[i]=(m-m/i)*inv[m%i]%m; // m-m/i 为模意义下的 -p/i
}
for(rr int i=1;i<=n;++i){
printf("%lld\n",inv[i]);
}
记忆技巧:用 表示 ,再乘上逆元。
阶乘逆元#
如果题目的模数 较大且为质数,而 ,则 一定和 互质,于是根据费马小定理, 一定存在模 意义下的逆元。
方法 :我们可以使用 次费马小定理,利用快速幂进行计算,从而得到每一个 ,时间复杂度 。
方法 :观察到 。我们可以用 的时间计算出 中每一个数的逆元和 ,然后倒着递推。
线性同余方程组#
中国剩余定理 / CRT#
给定同余方程组
其中 是任意整数, 两两互质。
不失一般性,令 。设 ,则该方程存在一解 ,其中 (即:), 是 在模 意义下的乘法逆元。
证明:对于每一个 考虑和式中的任意 。由 的定义知它是 的倍数,于是 。再来考虑和式中 一项。由乘法逆元和 的定义只 ,于是 。综上,,满足该约束条件。
中国剩余定理断言该方程的通解是 。易证这一定是原方程组的解。接下来证明不存在其它任意解。
考虑有同余方程组
那么对于任意满足 的整数 ,方程的解 。
整理得 。令 ,即 。容易观察到这是 exgcd 的形式。观察到如果 ,那么一定存在一组 的特解 。同时,我们也知道了,该方程有解当且仅当 (不失一般性,我们假设 )。由裴蜀定理,。
则
于是我们将之前的同余方程组变成了一个同余方程
这证明了我们可以将这两个同余方程合并成一个模数为 的新同余方程。同时我们得到了一个结论:不存在该方程组的两个解 使得 ,因为 的所有解在模 意义下同余。
将该结论推广到原同余方程组
我们可以说,不存在该方程组的两个解 使得 。
由于 两两互质,即不存在该方程组的两个解 使得 。不失一般性,设 。假设存在解 但 ,则我们可以找到另外一个解 。,于是这和我们得到的结论矛盾。
扩展中国剩余定理 / exCRT#
问题与 CRT 的应用场景基本一致,但 不一定两两互质。中国剩余定理不再适用。
考虑求出前 个方程的一个解 。记 。我们要做的是找到一个正整数 ,使得 。这很容易理解,因为前 个方程合并之后的模数就是 。
移项,得 ,可以 exgcd 求出,合并前 个方程即可。
该方程的通解是 。这很容易证明,像证明 CRT 那样就可以了。
注意到当 不一定两两互质时,可能会出现无解的情况。
高次同余方程 / BSGS 算法#
BSGS 算法,全程 Baby Step, Giant Step 算法,一译「小步大步算法」。
其作用是求解形如 的方程,其中 且 是质数。
由费马小定理,,于是我们只需要考虑在 范围的解。
BSGS 算法的名字「小步大步」解释了该算法的流程:
考虑将某个指数拆成 的形式,其中 。如果有 ,那么也有 。
我们可以用 的时间计算出 (并存放在 hash 表或 map 中。然后,我们用 的时间检查:对于每一个满足 ,hash 表或 map 中是否存在 和 相等。
总时间复杂度为 ,当 时最优,为 。如果使用了 map,则还需要带一个 的复杂度。
# include <cstdio>
# include <cmath>
# include <cstring>
# define int long long
const int MOD=9997;
const int N=100010,INF=0x3f3f3f3f;
int p,b,n;
struct HashTable{
struct Hash_Edge{
int key,value,next;
}edge[100010];
int head[10010],sum;
inline void add(int key,int value){
edge[++sum].key=key;
edge[sum].value=value;
edge[sum].next=head[key%MOD];
head[key%MOD]=sum;
return;
}
inline int query(int key){
for(int j=head[key%MOD];j;j=edge[j].next){
if(edge[j].key==key){
return edge[j].value;
}
}
return -1;
}
}HashT;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
inline int qpow(int d,int p,int mod){
int ans=1;
while(p){
if(p&1){
ans=ans*d%mod;
}
p>>=1,d=d*d%mod;
}
return ans;
}
# undef int
int main(void){
# define int long long
while(~scanf("%lld%lld%lld",&p,&b,&n)){
memset(HashT.head,0,sizeof(HashT.head));
HashT.sum=0;
if(n==1){ //特判 x^0 = 1 (x != 0)
printf("0\n");
continue;
}
int m=ceil(sqrt(double(p)));
int now=1;
for(int i=0;i<m;++i){ // baby step
if(!i){
HashT.add(now*n%p,i);
continue;
}
now=now*b%p;
HashT.add(now*n%p,i);
}
int base=qpow(b,m,p),ans=1;
for(int i=1;i<=m;++i){ // giant step
ans=ans*base%p;
if(HashT.query(ans)!=-1){
printf("%lld\n",i*m-HashT.query(ans));
goto END;
}
}
puts("no solution");
END:;
}
return 0;
}
作者:Meatherm
出处:https://www.cnblogs.com/Meatherm/p/14391300.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探