求最大公约数算法

一、欧几里德算法(辗转相除法)

定理:对于任意整数a,b,有gcd(a,b) = gcd(b,a mod b)

证明:

a = kb + r --> r = a % b

假设a、b的公约数为x,即 x|a、x|b  ("|"表示a、b可被x整除),用同余符号表示为:a≡b(mod x)。

x|b --> x|kb

由取模运算性质

可得:x|(a - b) --> x|(a - kb) --> x|(kb+r - kb) --> x|(r)

即r可以被x整除,所以x是a,b,r的公约数,也就是说a,b,r有相同的公约数,那么最大公约数自然也相等。

定理证毕。

 

C代码实现:

1.递归实现

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

 

2.迭代实现

int gcd(int a, int b) { int tmp; while (b != 0) { tmp = a; a = b; b = tmp % a; } return a; }

 

二、拓展欧几里德算法

扩展欧几里得算法是欧几里得算法(又叫辗转相除法)的扩展。除了计算a、b两个整数的最大公约数,此算法还能找到整数x、y(其中一个很可能是负数),使它们满足贝祖等式

 注意x、y的解不唯一。

原理:

假设a' = b,b' = a%b.

内层中,假设存在x,y,使得a'x + b'y = gcd(a',b') --> bx + (a%b)*y = gcd(a',b') --> bx + (a - a/b * b)*y = gcd(a',b')

在欧几里德算法中已经证明gcd(a,b) = gcd(b,a%b) = gcd(a',b')

所以:bx + (a - a/b * b)*y = gcd(a,b) --> ay + b(x - y*a/b) = gcd(a,b).

显然:只要求出内层a',b'对应的x,y,就能够求出外层a,b对应的x,y.

 

C代码实现:

int gcd(int a, int b, int *x, int *y) { int ret; int tmp; if (b == 0) { *x = 1; *y = 0; return a; } ret = gcd(b, a%b, x, y); tmp = *x; *x = *y; *y = tmp - (*y)*(a/b); return ret; }

 

从代码中可以看出,递归极限(b=0)的情况下,a*x + b*y = gcd(a,0) = a,这样就很容找出一组x,y满足此方程。

我们选择x=1,y=0。

 

三、Stein算法

当求两个非常大的数的最大公约数且其中有素数时,欧几里德算法在效率上有缺陷,Stein算法可以弥补此缺陷。

算法规则如下:

x=0,则y是最大公约数。

y=0,则x是最大公约数。

x偶y偶:gcd(x,y) = 2gcd(x/2,y/2)

x偶y奇:gcd(x,y) = gcd(x/2,y)

x奇y偶:gcd(x,y) = gcd(x,y/2)

x奇y奇:gcd(x,y) = gcd((x+y)/2,|x-y|/2)

基本思想就是把要求的两个数不断化小,直到其中一个为0.

 

为了证明上述等式成立,需要先了解几个预备知识:

1.一个奇数的所有约数都是奇数

2.gcd(nx,ny) = n * gcd(x,y)

3.如果x%a=0,y%a=0,则(x+y)%a=0,(x-y)%a=0  (这个很容易理解)

4.奇数 * 偶数 = 偶数  奇数 + 奇数 = 偶数  奇数 - 奇数 = 偶数

 

下面开始证明:

1.x偶y偶:gcd(x,y) = 2gcd(x/2,y/2)

这个很简单,根据gcd(nx,ny) = n * gcd(x,y),令n=2即可的上述等式。

 

2.x偶y奇:gcd(x,y) = gcd(x/2,y)

假设a=gcd(x,y),根据预备知识1可知,a是奇数。x%a=0,设x=na,根据预备知识4可知,n必定为偶数,

所以x/2=(n/2)*a,也就是x/2能够被a整除,(x/2)%a=0.

x/2,y有公约数a --> gcd(x,y) <= gcd(x/2,y)  (这里应该很容易理解吧)

gcd(x,y) >= gcd(x/2,y)  (显而易见)

所以,gcd(x,y) = gcd(x/2,y),证毕。

 

3.x奇y偶:gcd(x,y) = gcd(x,y/2)

和上面的2相同,不重复。

 

4.x奇y奇:gcd(x,y) = gcd((x+y)/2,|x-y|/2)

当x>y时,

根据预备知识4可知,(x+y),(x-y)都是偶数,所以gcd((x+y),(x-y)) = 2*gcd((x+y)/2,(x-y)/2).

假设(x+y)/2=m,(x-y)/2=n,那么m+n=x,m-n=y.

如果a=gcd(m,n) --> m%a=0,n%a=0,根据预备知识3,(m+n)%a=0,(m-n)%a=0 --> x%a=0,y%a=0.

所以a是x,y的公约数 --> gcd(m,n) <= gcd(x,y)

现在来证gcd(m,n) >= gcd(x,y):

设b=gcd(x,y).因为x,y都是奇数,所以b必定为奇数。

x%b=0,y%b=0 --> (x+y)%b=0,(x-y)%b=0.根据预备知识4可知,(x+y),(x-y)都是偶数,那么((x+y)/2)%b=0,((x-y)/2)%b=0.(这一点在证明2时已经证明过了)

即m%b=0,n%b=0 --> b是m,n的公约数 --> gcd(m,n) >= gcd(x,y)

所以,gcd(x,y) = gcd(m,n) = gcd((x+y)/2,(x-y)/2)

当y>x时,gcd(x,y) = gcd((x+y)/2,(y-x)/2)

这就是为什么要加绝对值符号的原因。

 

int stein(int x, int y) { if (x == 0) return y; if (y == 0) return x; /* 右移1位代替除以2 */

    if ((x%2 ==0) && (y%2 == 0)) return 2 * stein(x>>1, y>>1); else if ((x%2 ==0) && (y%2 != 0)) return stein(x>>1, y); else if ((x%2 !=0) && (y%2 == 0)) return stein(x, y>>1); else

        return stein((x+y)>>1, abs(x-y)>>1); }

 

你可能感兴趣的:(算法)