求最大公约数

求最大公约数,一个很直观的方法就是暴力枚举

从x, y中较小的一个开始,循环递减,x%y == 0 && y%y==0 的时候 y即为最大公约数。

2

公元前的欧几里得给出了一个高效的解法——辗转相除法。

假设 f(x,y)表示x, y的最大公约数,取k = x/y, b = x%y,则 x = ky + b,如果一个数能够同时整出x和y,即x和y的公约数与b和y的公约数是相同的。如果一个数能同时整除x, y,那么必然能够整除b 和 y.

故f(x,y) = f(y, x%y)

则有代码:

unsigned long long gcd(unsigned long long x, unsigned long long y)
{
	// 递归抹除求
	return (!y) ? x : gcd(y, x % y);
}
递归容易用循环来替换

unsigned long long gcd(unsigned long long x, unsigned long long y)
{
	// 迭代抹除求
	while(y != 0){
		unsigned long long t = x % y;
		x = y;
		y = t;
	}
	return x;

}
3

2中的%运算用到除法,我们知道除法在运算的时候是比较慢的,能不能把模运算换一个?

当然是可以的,x 和 y的公约数,同样是 y 和 x-y的公约数。即 f(x, y) = f(x-y, y)

递归代码:

unsigned long long gcd(unsigned long long x, unsigned long long y)
{
	// 递归相减求
	if(x < y)
		return gcd(y, x);
	if(y == 0)
		return x;
	else
		return gcd(x-y, y);
}
把取模运算换成减运算之后,显然递归次数增加,若是(1000000000,1)这样的组合,那么递归很容易溢出栈。

4

《编程之美》P152

对于 y 和x 来说,如果 y = k*y1, x = k*x1。 那么有f(y, x) = k*f(y1,x1).

另外,如果x = p*x1, 假设p是素数,并且y%p != 0(即,y不能被p整除),那么 f(x, y) = f(p*x1, y) = f(x1, y).


我们知道2是一个素数,同时2也可以跟移位联系一起,移位比做除法要快。取 p = 2

若想x, y都是偶数, f(x, y) = 2* f(x/2, y/2) = 2 * f(x >>1, y>>1)

若x为偶数,y为奇数 f(x, y) = f(x/2, y) = f(x>>1, y)

若x为奇数,y为偶数  f(x, y) = f(x, y/2) = f(x, y>>1)

若想x,y都为奇数,f(x, y) = f(y, x-y)

得代码如下:

bool IsEven(unsigned long long n)
{
	return n%2 == 0 ? true : false;
}
// 求最大公约数
unsigned long long gcd(unsigned long long x, unsigned long long y)
{
	
	// 移位操作
	if(x < y)
		return gcd(y, x);
	if(y == 0)
		return x;
	else{
		if(IsEven(x)){
			if(IsEven(y))
				return (gcd(x>>1, y>>1) << 1);
			else
				return gcd(x >> 1, y);
		}
		else{
			if(IsEven(y))
				return gcd(x, y >> 1);
			else
				return gcd(y, x-y);
		}
	}
}




你可能感兴趣的:(C++程序,经典用法)