同余1:欧几里得算法及其拓展学习笔记


前言:搞定了类欧几里得后,我决定把欧几里得算法给复习一下,顺带复习整个同余相关问题( 这是逆生长吗?(手动滑稽)

欧几里得算法

欧几里得算法没什么可说的,就是求最大公约数,不过有多种方式,这里不再讨论暴力的求法了。
辗转相除法
接下来,直接就到辗转相除法了,辗转相除法用到下面的结论,当其中一个数辗转到零时另一个数就是答案。
结论: G C D ( X , Y ) = G C D ( Y , X % Y ) GCD(X,Y) =GCD(Y,X \% Y) GCD(X,Y)=GCD(Y,X%Y)
证明:设对任意两个不为一的正整数 x x x y y y有最大公因数为 d d d,则 x x x可表示为 t 1 ∗ d t_1*d t1d y y y可表示为 t 2 ∗ d t_2 * d t2d,根据最大公因数的定义可知, t 1 t_1 t1 t 2 t_2 t2互质。这里,不妨设 x > y x > y x>y,则 t 1 > t 2 t_1 > t_2 t1>t2,根据基本常识 a % b = a − b ∗ ⌊ a b ⌋ a \% b = a - b * \lfloor {a \over b} \rfloor a%b=abba,因此 X % Y X \% Y X%Y可表示成 t 1 ∗ d − t 2 ∗ d ∗ ⌊ t 1 ∗ d t 2 ∗ d ⌋ t_1 * d - t_2 * d * \lfloor {t_1 * d \over t_2 * d} \rfloor t1dt2dt2dt1d,约去 d d d,得 t 1 ∗ d − t 2 ∗ d ∗ ⌊ t 1 t 2 ⌋ = ( t 1 − t 2 ∗ ⌊ t 1 t 2 ⌋ ) ∗ d t_1 * d - t_2 * d * \lfloor {t_1\over t_2 } \rfloor = (t_1 - t_2 * \lfloor {t_1 \over t_2} \rfloor)*d t1dt2dt2t1=t1t2t2t1d,不难看出括号内的数为整数,则上述结论成立。
通过这个方法,不难看出 G C D ( X , Y ) = G C D ( Y , X − Y ) GCD(X,Y) =GCD(Y,X - Y) GCD(X,Y)=GCD(Y,XY)也成立。

inline int gcd( int x , int y ) {
	return y == 0 ? x : gcd( y , x % y );/*上述讨论建立在x > y的基础上,实际上
	这里若x < y,gcd会自动帮我们交换两个数*/
} 

时间复杂度为 O ( l o g ( X ) ) O(log(X)) O(log(X))

二进制算法
我以为这样就最优了,实际上我还看到一种“二进制算法”,是在《信息学竞赛之数学一本通》(东南大学出版社)里面看到的,这里也稍微提一下,具体思想就是通过不断去掉因子2来降低常数。过程如下:
1.若 x x x y y y均为偶数,则 G C D ( x , y ) = 2 ∗ G C D ( x , y ) GCD(x,y) =2*GCD(x,y) GCD(x,y)=2GCDxy
2.若 x x x y y y均为奇数,则 G C D ( x , y ) = G C D ( x − y , y ) GCD(x,y) =GCD(x - y,y) GCD(x,y)=GCDxyy
3.若 x x x y y y一奇一偶,不妨设 x x x为偶数(否则将两数置换),则 G C D ( x , y ) = G C D ( x 2 , y ) GCD(x,y) =GCD({ x \over 2},y) GCD(x,y)=GCD2xy
4.当其中一个数为零时返回另一个数。
这个做法的正确性显然,不难得出以下代码:

inline int gcd( int x , int y ) {
	if ( !y ) {
		return x;
	}
	if ( x < y ) {
		x ^= y ^= x ^= y;//整数的快速交换 
	}
	if ( x & 1 && y & 1 ) {
		return gcd( y , x - y );
	} else if ( x & 1 ) {
		return gcd( x , y / 2 );
	} else if ( y & 1 ) {
		return gcd( x / 2 , y );
	} else {
		return 2 * gcd( x / 2 , y / 2 );
	}
}

最小公倍数LCM可利用GCD求出,就是 L C M ( a , b ) = a ∗ b G C D ( a , b ) LCM(a,b)={ a * b \over GCD(a,b)} LCM(a,b)=GCD(a,b)ab,证明自己想吧。
在数据非常大以致不得不用高精度的时候,二进制算法的优越性更加明显。

拓展欧几里得算法

拓展欧几里得算法
拓展欧几里得算法可用来求解以下问题,对于已知数(a,b),找出另一组数(p,q),使得满足 a ∗ p + b ∗ q = G C D ( a , b ) a*p+b*q=GCD(a,b) ap+bq=GCD(a,b),没错,这就是裴蜀定理的内容,我们的目标是证明存在性,如果存在就找到一个合适的算法。
接下来,让我们利用 G C D ( X , Y ) = G C D ( Y , X % Y ) GCD(X,Y) =GCD(Y,X \% Y) GCD(X,Y)=GCD(Y,X%Y)搞些事情:
已知 a ∗ p + b ∗ q = G C D ( a , b ) a*p+b*q=GCD(a,b) ap+bq=GCD(a,b),通过 G C D ( a , b ) = G C D ( b , a % b ) GCD(a,b) =GCD(b,a \% b) GCD(a,b)=GCD(b,a%b)
得出 b ∗ p ′ + a % b ∗ q ′ = a ∗ p + b ∗ q b*p&#x27;+a \% b * q&#x27; = a*p+b*q bp+a%bq=ap+bq
推出 b ∗ p ′ + ( a − b ∗ ⌊ a b ⌋ ) ∗ q ′ = a ∗ p + b ∗ q b*p&#x27;+(a - b * \lfloor {a \over b} \rfloor) * q&#x27; =a*p+b*q bp+(abba)q=ap+bq
最终得出: a ∗ q ′ + b ∗ ( p ′ − q ′ ∗ ⌊ a b ⌋ ) = a ∗ p + b ∗ q a *q&#x27;+b*(p&#x27; - q&#x27;*\lfloor {a \over b} \rfloor) = a*p+b*q aq+b(pqba)=ap+bq
这样就将p,q的变化与a,b的变化挂钩了。
当b = 0时,GCD为a,不难看出p= 1,q = 0,再将(p,q)回溯,经过层层递归回去,即可得到原先(p,q)的值,至此存在性的证明与算法都出来了。

inline int exgcd( int a, int b , int & x , int & y ) {//返回x,y的值即为(p,q) 
	if ( !b ) {
		x = 1;
		y = 0;
		return a;
	}
	int ans = exgcd( b , a % b , x , y );
	int tem = x;
	x = y;
	y = tem - a / b * y;
	return ans;
}

拓展欧几里得算法的应用(解线性同余方程)

拓展欧几里得算法主要用来线性同余方程。
对于方程 a ∗ x + b ∗ y = c a*x+b*y=c ax+by=c, 该方程等价于 a ∗ x ≡ c ( m o d b ) a * x\equiv c\pmod b axc(modb),而该方程有整数解的充要条件为 c % G C D ( a , b ) = 0 c \% GCD(a,b)=0 c%GCD(a,b)=0
对于求解方程,我们先求出 ( x o , y o ) (x_o,y_o) (xo,yo)满足 a ∗ x o + b ∗ y o = G C D ( a , b ) a*x_o+b*y_o=GCD(a,b) axo+byo=GCD(a,b),则方程解可表示为 a ∗ x o ∗ c G C D ( a , b ) + b ∗ y o ∗ c G C D ( a , b ) = G C D ( a , b ) ∗ c G C D ( a , b ) a*x_o * {c \over GCD(a,b)}+b*y_o* {c \over GCD(a,b)}=GCD(a,b)* {c \over GCD(a,b)} axoGCD(a,b)c+byoGCD(a,b)c=GCD(a,b)GCD(a,b)c,这同时也说明了上述充要条件的合理性。
不过,这样求出的只是一组特解,解的一般形式为 x = x o ∗ c G C D ( a , b ) + t ∗ b G C D ( a , b ) x = x_o * {c \over GCD(a,b)} +t*{b \over GCD(a,b)} x=xoGCD(a,b)c+tGCD(a,b)b y = y o ∗ c G C D ( a , b ) − t ∗ a G C D ( a , b ) y = y_o * {c \over GCD(a,b)} -t*{a \over GCD(a,b)} y=yoGCD(a,b)ctGCD(a,b)a
其中 t t t为任意正整数。
不过,在做题时,我们一般要求最小正整数解,求法为:
z = b G C D ( a , b ) z = {b \over GCD(a,b)} z=GCD(a,b)b x = ( x % z + z ) % z x = (x \% z + z) \% z x=(x%z+z)%z,y同理。
a a a b b b互质, c = 1 c = 1 c=1时,求出的 x x x即为 a a a b b b意义下的逆元(不懂逆元的可以假装没看到 )。
好了,总体上来说欧几里得算法就没什么了(线性逆元以后讲吧 ),最重要的还是它的应用。这个算法我以前也早已掌握,但在做题时总是依赖题解,证明自己其实并不熟悉它的原理,所谓数论,不是拿来出题的,而是用来快速算出某一步式子的解的,还是要有灵活的思维啊。所以,想要牢固掌握一个知识点,还得多刷题。
板子
同余方程(太裸了https://www.luogu.org/problem/P1082
我都不想讲了。

#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
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 , x , y );
	ll t = x;
	x = y;
	y = t - ( a / b ) * y;
	return d;
}
int main () {
	ll a , b , c , x , y;
	cin >> a >> b;
	c = 1;//其实是多余的 
	ll d = exgcd( a , b , x , y );
	x = x * ( c / d );
	y = y * ( c / d );
	ll z = b / d;
	ll k = x;
	k = ( x % z + z ) % z;
	printf("%lld\n",k);
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值