【瞎口胡】基础数学 2 裴蜀定理 exgcd 同余相关

写在前面#

在第一篇当中我们介绍了一点基本知识。

第二篇的内容较第一篇稍有难度,但是也非常基础,相信只要学过小学数学就能看明白。

裴蜀定理与扩展欧几里得算法#

  • 裴蜀定理

    对于任意非负整数 a,b

    1. 任意整数 x,y 都满足 ax+bygcd(a,b) 的倍数。

      证明:gcd(a,b)a,gcd(a,b)b,则一定有 gcd(a,b)ax+by

    2. 存在整数 x,y 使得 ax+by=gcd(a,b)

      证明:数学归纳法。回忆欧几里得算法的最后一步,a0=gcd(a,b),b0=0,取 x=1,y=0,即有 a0x+b0y=gcd(a0,b0)

      根据欧几里得算法的流程,a0=b,b0=amodb

      a0x+b0y=gcd(a0,b0)

      bx+(amodb)y=gcd(a,b)

      bx+(aabb)y=gcd(a,b)

      ay+b(xaby)=gcd(a,b)

      因此有 x=y,y=xaby。对于每一层的 a,b 都是如此,在回溯完成之后就会得到一组正确的解。

  • 扩展欧几里得算法

    简称扩欧,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;
    }
    
  • 应用

    • 求方程 ax+by=gcd(a,b) 的解集

      考虑我们 exgcd 求出了该方程的一组特解 x0,y0。我们希望在此基础上将 x0,y0 加上一个值,设其为 Δx,Δy

      那么它们必须满足 aΔx+bΔy=0。即

      Δy=abΔx

      ab 约分,设其最简形式为 ab,显然 a=agcd(a,b),b=bgcd(a,b)。此时有

      Δy=abΔx

      因为 Δy 是整数,因此 Δx 必须是 b 的倍数。设 Δx=lb,则 Δy=la,其中 l 是任意整数。

      综上,ax+by=gcd(a,b) 的解集为 x=x0+kbgcd(a,b),y=y0kagcd(a,b)

    • 求方程 ax+by=c 的解集

      由裴蜀定理,ax+by 一定是 gcd(a,b) 的倍数。因此如果 gcd(a,b)c,该方程无解。

      g=gcd(a,b)c=t×g

      ax+by=c

      ax+by=gt

      axt+byt=g

      x=xt,y=yt,此时用扩欧求出该方程的解 x0,y0,则原方程的一组特解为 x0=tx0,y0=ty0

      利用求 ax+by=gcd(a,b) 时的证明方法,可以知道该方程的解集为 x=x0+kbgcd(a,b),y=y0kagcd(a,b)

同余#

对于整数 a,b 和正整数 p,若 amodp=bmodp,则成 ab(modp),读作「a 在模 p 意义下与 b 同余」。

基本性质#

  • 性质 1:自反性

    aa(modp)

  • 性质 2:对称性

    abba(modp)

  • 性质 3:传递性

    ab,bcac(modp)

  • 性质 4:可加、可乘性

    abanbn,a±nb±n(modp)

  • 性质 5

    cp 互质的时候:

    acbcab(modp)

    证明:考虑 ab 的充要条件是 p(ab)

    acbc 可以推出 p(acbc),即 p(ab)c。当 cp 互质的时候,ab一定p 整除。换句话说,gcd(c,p)=1 是上式成立的充分条件。

    性质 5 非常重要,一定要牢记只有 gcd(c,p)=1 的时候该性质才成立。

剩余系与剩余类#

所有对 p 同余的整数构成模 p 的一个剩余类。考虑一个整数 modp 的取值范围是 [0,p1],于是剩余类显然有 p 个。若某个剩余类中的整数模 p 的结果与 p 互质,则称该剩余类为互质剩余类

注意,模 p0 的整数构成的剩余类不是互素剩余类。

完全剩余系:在所有同余类中各任取一个整数,它们构成了模 p 的一个完全剩余系。

简化剩余系(缩系):在所有互质剩余类中任取一个整数,它们构成了模 p 的一个缩系。

完全剩余系和缩系的个数是无限的。

费马小定理#

对于质数 p 和任意满足 gcd(a,p)=1a,有

ap11(modp)

  • 引理

    对于满足上述条件的 aa,2a,3a,...,(p1)a 构成模 p 的一个缩系。

    证明:显然上面的 p1 个数与 p 互质。如果存在 0ij<p 使得 aiaj(modp),由性质 5 将两边同时除以 a,得 ij(modp),因为 0<i,j<p,即 i=j,与假设矛盾。

显然

1×2×...×(p1)a×2a...×(p1)a(modp)

1×2×..×(p1)1×2×...×(p1)×ap1(modp)

由性质 5,将两边化简并调换位置得

ap11(modp)

欧拉定理#

对于任意模数 p 和任意满足 gcd(a,p)=1a,有

aφ(p)1(modp)

其中 φ(x) 表示小于 x正整数中与 x 互质的个数。

证明和证明费马小定理类似,不再赘述。

注意到费马小定理是欧拉定理的特例。

扩展欧拉定理#

对于任意 a,b,m,有

ab{abb<φ(m)a(bmodφ(m))+φ(m)bφ(m)(modm)

先取 m 的一个质因子 p,设 m=pk×s,其中 gcd(p,s)=1。则有 pφ(s)1(mods)。又 φ(m)=φ(s)×φ(pk),即 φ(m)φ(s) 的倍数,那么也有 pφ(m)1(mods)

pφ(m)=ts+1,那么 pφ(m)+k=pk(ts+1)=tm+pkpφ(m)+kpk(modm)

观察到

pbpbk+k(modm)

pbpbkpk(modm)

pbpbkpφ(m)+k(modm)

pbpb+φ(m)(modm)

注意到,当 bk 时,上式才一定成立。这是因为 b,m 不保证互质,此时 p 的负数次幂(即逆元)在模 m 意义下不一定有定义。

注意到 φ(pk)=(p1)pk1,那么又有 kφ(pk)φ(m)。我们可以说,当 bφ(m) 时,pbpb+φ(m)(modm)。令 b=b+φ(m),那么我们可以说,当 b2φ(m) 时,pbpbφ(m)(modm),从而得证。

乘法逆元#

对于整数 a 和模数 p,称 ax1(modp) 的整数 xa 在模 p 意义下的乘法逆元。a 的乘法逆元记为 a1

求法 1:对于 p 是质数的情况下,由费马小定理有 a×ap21(modp),则 a 的乘法逆元为 ap2

求法 2:对于 p 不是质数的情况下,有 axpy=1,此时 a,p 为定值,使用 exgcd 求出 x 的最小非负整数解即可。

上述求法的时间复杂度均为 O(logn),由求法 2 可知,a 在模 p 意义下存在逆元的充要条件是 gcd(a,p)=1

求法 3:在已知 1in,gcd(i,p)=1 的情况下,怎样快速求出 1n 的每一个数在模 p 意义下的逆元?

我们有一个线性的做法。对于 i,如果我们知道 1i1 中每一个数的逆元,那么将 p 写成 ki+r(0r<i) 的形式,则有:

ki+r0(modp)

左右两侧同乘 i1×r1

k×r1+i10(modp)

整理得

i1=(pi×(pmodi)1)modp

	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]);
	}

记忆技巧:用 i×pi+pmodi 表示 p,再乘上逆元。

阶乘逆元#

如果题目的模数 p 较大且为质数,而 n<p,则 n! 一定和 p 互质,于是根据费马小定理,n! 一定存在模 p 意义下的逆元。

方法 1:我们可以使用 n 次费马小定理,利用快速幂进行计算,从而得到每一个 i!(1in),时间复杂度 O(nlogn)

方法 2:观察到 (i+1)!=i!×(i+1)。我们可以用 O(n) 的时间计算出 [1,n] 中每一个数的逆元和 n!,然后倒着递推。

线性同余方程组#

中国剩余定理 / CRT#

给定同余方程组

{xa1(modm1)xa2(modm2)xa3(modm3)xan(modmn)

其中 ai 是任意整数,mi 两两互质

不失一般性,令 0ai<mi。设 p=i=1nmi,则该方程存在一解 x0=i=1naitiMi,其中 Mi=pmi(即:Mi=1rnrimr),tiMi 在模 mi 意义下的乘法逆元。

证明:对于每一个 i(1in)考虑和式中的任意 ajtjMj(ji)。由 Mj 的定义知它是 mi 的倍数,于是 ajtjMj0(modmi)。再来考虑和式中 aitiMi 一项。由乘法逆元和 ti 的定义只 tiMi=1,于是 aitiMiai(modmi)。综上,x0ai(modmi),满足该约束条件。

中国剩余定理断言该方程的通解是 x=x0+kp(kZ)。易证这一定是原方程组的解。接下来证明不存在其它任意解。

考虑有同余方程组

{xa1(modm1)xa2(modm2)

那么对于任意满足 m1j1+a1=m2j2+a2 的整数 j1,j2,方程的解 x=m1j1+a1

整理得 m1j1m2j2=a2a1。令 j2=j2,即 m1j1+m2j2=a2a1。容易观察到这是 exgcd 的形式。观察到如果 gcd(m1,m2)a2a1,那么一定存在一组 j1,j2 的特解 j1,0,j2,0。同时,我们也知道了,该方程有解当且仅当 gcd(m1,m2)a2a1(不失一般性,我们假设 a2a1)。由裴蜀定理,j1=j1,0+km2gcd(m1,m2),j2=j2,0km1gcd(m1,m2)(kZ)

x=m1j1+a1

=m1(j1,0+km2gcd(m1,m2)+a1

=m1j1,0+km1m2gcd(m1,m2)+a1

=klcm(m1,m2)+m1j1,0+a1

于是我们将之前的同余方程组变成了一个同余方程

xm1j1,0+a1(modlcm(m1,m2))

这证明了我们可以将这两个同余方程合并成一个模数为 lcm(m1,m2) 的新同余方程。同时我们得到了一个结论:不存在该方程组的两个解 x1=k1lcm(m1,m2)+r1,x2=k2lcm(m1,m2)+r2 使得 r1r2,因为 x 的所有解在模 lcm(m1,m2) 意义下同余。

将该结论推广到原同余方程组

{xa1(modm1)xa2(modm2)xa3(modm3)xan(modmn)

我们可以说,不存在该方程组的两个解 x1=k1lcm(m1,m2,,mn)+r1,x2=k2lcm(m1,m2,,mn)r2 使得 r1r2

由于 mi 两两互质,即不存在该方程组的两个解 x1=k1p+r1,x2=k2p+r2 使得 r1r2。不失一般性,设 0x0<p。假设存在解 kp+r(0r<p)rx0,则我们可以找到另外一个解 kp+x0rx0,于是这和我们得到的结论矛盾。

扩展中国剩余定理 / exCRT#

问题与 CRT 的应用场景基本一致,但 mi 不一定两两互质。中国剩余定理不再适用。

考虑求出前 k1 个方程的一个解 x。记 lcm(m1,m2,...,mk1)=M。我们要做的是找到一个正整数 t,使得 x+tMai(modmi)。这很容易理解,因为前 k1 个方程合并之后的模数就是 lcm(m1,m2,,mk1)

移项,得 tMaix(modmi),可以 exgcd 求出,合并前 k 个方程即可。

该方程的通解是 x+k×lcm(m1,m2,...,mn)(kZ)。这很容易证明,像证明 CRT 那样就可以了。

注意到当 mi 不一定两两互质时,可能会出现无解的情况。

高次同余方程 / BSGS 算法#

BSGS 算法,全程 Baby Step, Giant Step 算法,一译「小步大步算法」。

其作用是求解形如 axb(modm) 的方程,其中 gcd(a,m)=1m 是质数。

由费马小定理,am1=1(modm),于是我们只需要考虑在 [0,m1) 范围的解。

BSGS 算法的名字「小步大步」解释了该算法的流程:

考虑将某个指数拆成 ikr 的形式,其中 0r<k,k>1。如果有 aikrb(modm),那么也有 aikar×b(modm)

我们可以用 O(k) 的时间计算出 armodm(0r<k)(并存放在 hash 表或 map 中。然后,我们用 O(mk) 的时间检查:对于每一个满足 0i,hash 表或 map 中是否存在 ar×bmodmaikmodm 相等。

总时间复杂度为 O(k+mk),当 k=m 时最优,为 O(m)。如果使用了 map,则还需要带一个 log 的复杂度。

模板题 POJ2417

# 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 国际」许可协议进行许可。

posted @   Meatherm  阅读(331)  评论(1编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示