Animals
蒟蒻

拓展欧几里得

欧几里得算法

 

  欧几里得算法(也称辗转相除法)是目前已知求最大公约数的最快通用算法,具有代码复杂度低、易理解、用途广等诸多优点,也是OI中不可或缺的的一种算法。(当然更相减损术也是)

  若有两个数a和b,需要求a和b的最大公约数,怎么办?

  枚举它们的因子? 小数据可以,大数据的话,这个O(n)的算法就图森破了。

  分解质因数?在大数面前也是拿衣服的。

  总不能不搞吧,上帝喊了一声“效率”,于是我们便有了O(log n)效率极高的欧几里得算法。

引理

  欧几里得有个非常强的定理,即gcd(a,b)=gcd(b,a mod b),让我们来证明一下

  约定:mod <=> 取余,gcd <=> 最大公约数,| <=> 整除

  假设a、b的公约数为k,a = bx + y

  则 k | a,k | b,a mod b=y

  因为 k | b,所以 k |bx,又因为 k | a,所以 k | (a - bx),即 k | y

  而a mod b = y,所以 k | a mod b

  再假设b、a mod b的公约数为kk,同理得 kk | a,所以(a,b)和(b,a mod b)的公约数是相同的,所以它们的最大公约数也是相同的.

  所以gcd(a,b)=gcd(b,a mod b)

时间复杂度(讨论情况为a>=b

  a mod b必然是小于a/2的,而上一次的b会变成下一次的a,上一次的a mod b会变成下一次的b,最坏情况也就是b在a/2附近,即a mod b在a/2附近。在最坏情况时每次的值也会减少一半,所以说时间复杂度是O(log n)的。(实际中往往会更低)

算法实现

  有了以上的引理算法实现就非常简单了,递归和非递归形式都可以。任何数与0的最大公约数是它本身,所以一直操作直到b = 0时的a就是原a,b的最大公约数了。

  伪代码:

while(b){
	r=a%b;
	a=b;
	b=r;
}
//最终的a即为原a,b最大公约数

 

扩展欧几里得算法

  扩展欧几里得算法的功能就更强大了,它可以用来求二元一次方程的通解,还可以用来求乘法逆元。

  在此顺便简介一下乘法逆元:

  若有 a*x ≡ 1 (mod m),则称 x 为a关于m的乘法逆元,等价式 a * x+m * y = 1  (y=a*x/m)

  这就也是个二元一次方程了,ExGcd可搞。

引理

  裴蜀定理:若ax+by = z,则 gcd(a,b)| z

  再顺手证明一下裴蜀定理:

  设k = gcd(a,b),则 k | a, k | b,根据整除的性质,有 k | (ax+by)

  设 s为ax+by的最小正数值

  再设 q = [a / s](a整除s的值);r = a mod s = a-q(ax+by) = a(1 - qx)+b(-qy);

  由此可见r也为a,b的线性组合;(ax+by称为a,b的线性组合)

  又因为s为a,b的线性组合的最小正数值,0<= r < s,所以r的值为0,即 a mod s = r =0;s | a;

  同理可得 s | b,则 s | k;

  又因为 k | (ax+by),s为ax+by的最小正数值,所以 k | s;

  因为 s | k,k | s,所以s = k;

  原命题得证。

如何求解(以下讨论a>b

  显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;

  当a>b>0 时

  设 ax1+ by1= gcd(a,b);

  bx2+ (a mod b)y2= gcd(b,a mod b);

  根据欧几里德原理有 gcd(a,b) = gcd(b,a mod b);

  则:ax1+ by1= bx2+ (a mod b)y2

  即:ax1+ by1= bx2+ (a - [a / b] * b)y2 = ay2+ bx2- [a / b] * by2

(a mod b = a - [a / b]*b;[a / b]代表a整除b)

  也就是ax1+ by1 = ay2 + b(x2- [a / b] *y2)

  根据恒等定理得:x1=y2;y1=x2- [a / b] *y2

  这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2

  由引理我们知道:ax+by = z,z为gcd(a,b)若干倍。

  所以我们先求解ax+by = gcd(a,b),再将求出的解乘以 z/gcd(a,b)就好了

算法实现

  我们依旧递归实现,因为上一次的x,y与下一次的x,y的值有关,所以我们从x=1,y=0(此时b=0)的情况开始递归上来,套公式就好了。(a,b下去,x,y上来)
     伪代码:

int ExGcd(int a,int b,int &x,int &y){           //x,y可设为全局 
	if(!b){
		x=1;
		y=0;
		return a;
	}
	int r=ExGcd(b,a%b,x,y);
	t=x;
	x=y;
	y=t-a/b*y;
	return r;
}

  

 


 

  本文转载自@leader_one

 

posted @ 2018-11-07 11:43  年下丶  阅读(226)  评论(0编辑  收藏  举报
--- 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 百里守约 ---