欧几里得算法及扩展欧几里得(含)

普通的欧几里得算法

欧几里得算法是用于求两个正整数最大公约数的算法,这个算法十分基础,99%的 OIer / ACMer 都会 XD
如何求最大公约数?
一种十分朴素的方法是枚举。(以下全部默认 a > b a>b a>b)若 a ≡ 0 ( m o d    b ) a \equiv 0 (\mod b) a0(modb),最大公约数为b,否则从 a 2 \frac{a}{2} 2a 1 1 1 枚举试除。然而显然这种做法超时概率为1。
那么有没有快一点的做法呢?
显然是有的,伟大的欧几里得在几千年前就提出了这种做法 orz。
显然, g c d ( a , b ) = g c d ( b , a m o d    b ) gcd(a, b)=gcd(b, a\mod b) gcd(a,b)=gcd(b,amodb),于是只要这样不断递归,当 b = 0 b=0 b=0时,就可以求出最大公约数,即为a。
给出代码:

int gcd(int a, int b)
{
	return (b == 0) ? a : gcd(b, a % b);
}

证明留给读者自证。
(当然不会这样子啦QAQ)

证:
a = k b + r a = kb + r a=kb+r(a, b, k, r皆为正整数, 且r<b),则 r = a m o d &ThinSpace;&ThinSpace; b r = a \mod b r=amodb
d d d a a a b b b 的一个公约数, 即 d ∣ a d |a da, d ∣ b d|b db
因为 r = a − k b r = a - kb r=akb, 两边同时除以 d d d, r/d=a/d-kb/d=m,由等式右边可知m为整数,因此 d ∣ r d|r dr
因此 d d d也是 b , a m o d b b,a mod b b,amodb的公约数
假设 d d d b , a m o d b b,a mod b b,amodb的公约数, 则 d ∣ b , d ∣ ( a − k ∗ b ) , k d|b,d|(a-k*b),k db,d(akb),k是一个整数。
进而 d ∣ a d|a da.因此 d d d也是 a , b a,b a,b的公约数
因此 ( a , b ) (a,b) (a,b) ( b , a m o d b ) (b,a mod b) (b,amodb)的公约数是一样的,其最大公约数也必然相等。
得证。
这种算法还有个比较形象的名字叫辗转相除法,时间复杂度为 O ( lg ⁡ b ) O(\lg{b}) O(lgb)
这是如何得到的?
这里

适合高精度的改编版

如果a和b都特别大,需要高精度怎么办?
高精度除以高精度是非常棘手的,通常没人写那种东西。所以我们需要将它转移成别的运算或高精除以int。
代码:

int gcd(int m, int n)
{
	if (m == n)
  		return m;
 	if (m < n)
  		return gcd(n, m);
 	if (m & 1)
 	{
  		if (n & 1)
   			return gcd(n, m - n);
  		return gcd(m, n >> 1);
 	}
 	if (n & 1)
  		return gcd(m >> 1, n);
 	return gcd(m >> 1, n >> 1);
}

大致就是这个意思啦,我没写高精度,不过像除以2,判断奇偶这种东西是很容易实现的。这么做就很简单了。

扩展欧几里得算法

扩欧通常是用来求 a x + b y = c ax+by=c ax+by=c这类方程的整数解。比如方程 x + 2 y = 3 x+2y=3 x+2y=3的整数解为 x = 1 − 2 t , y = 1 + t ( t ∈ Z ) x=1-2t,y=1+t ( t∈Z) x=12t,y=1+t(tZ)
需要注意的是,当 c c c不为 gcd(a,b) 的倍数时,方程是没有整数解的(想想看为什么?)。
通过我们平时手算丢番图方程的过程,可以发现可以这么做:(下面的这段伪代码摘自《算法导论》的31.2)

EXTENDED-EUCLID(a,b)
	if b==0
		return(a,1,0)
	else(d',x',y')=EXTENDED-EUCLID(b,a mod b)
		(d,x,y)=(d',y',x'-[a/b]y')
		return(d,x,y)

这样可以求出一组解(然后就很容易得出通解)并且还顺便求出了gcd(a,b)的值——因为其实扩欧是普通辗转相除法的一点变形。

下面是exgcd的代码:

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);
	x = y;
	y = t - a / b * y;
	return g;
}

P.S.对于解丢番图方程、辗转相除法证,其他方法 可以看看 R·柯朗的《什么是数学?》中讲数论的那一部分,还是写得不错的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值