—————————————————————————————————————————————————————————————————————————————
看《编程之美》这本书,写的很不错,可是只看不把代码敲一下,总觉得不是真正的吸收了,而在敲代码的过程中说不定也会有更深层次的了解,所以就把其中的思路写下来同时也方便以后查阅。
PS:这篇博文是自己码的,里面的思想几乎全部都是摘抄自《编程之美》这本书,喜欢这本书,请买原书。
—————————————————————————————————————————————————————————————————————————————–
写一个程序,求两个非负整数的最大公约数(greatest common divisor),如果两个正整数都很大呢,如果两个数差别比较大呢
首先求最大公约数一般是使用辗转相除法,代码如下:
int gcd1(int a, int b)
{
if (b == 0)
{
return a;
}
return gcd1(b, a%b);
}
上述代码是根据辗转相除的定义来的,里面有%操作,对于大数来说,这是很费时的操作,有没有不用%的思路呢?
其实,(x, y) 和 (x-y, y)是相等的()表示求两个数的最大公约数,当然这里需要x > y,当x < y 时候,可以实用 y-x 来保证满足题意。
代码如下:
int gcd2(int a, int b)
{
if (0 == b)
{
return a;
}
if (a < b)
{
return gcd2(b, a);
}
return gcd2(a-b, b);
}
把%运算换成了-运算,本质上其实没有多大变化,gcd2也有问题,如当a 和 b相差比较大的时候(如222222 和 1)求公约数的过程中将递归很深而使得程序崩溃
有没有可能结合gcd1 和 gcd2的思路呢?分析如下:
我们知道,如果x = k *a, y = k*b 那么(x, y)= k * (a, b)并且若 x = p*x1,p为素数且y%p != 0的话(x, y) = (x1, y)。那么使用最小的素数2便可以有下分析
- x, y 均为偶数 则 (x, y) = 2 * (x >> 1, y >> 1)
- x为偶数,y为奇数 则 (x, y) = (x >> 1, y )
- x为奇数,y为偶数 则 (x, y) = (x , y >> 1)
- x为奇数,y为奇数 则 (x, y) = (x – y, y )
代码如下:
int gcd3(int a, int b)
{
if (0 == b)
{
return a;
}
if (a < b)
{
return gcd3(b, a);
}
if (a&1)
{
if (b&1)
{
return gcd3(a-b, b);
}
else
{
return gcd3(a, b >> 1);
}
}
else
{
if (b&1)
{
return gcd3(a >> 1, b);
}
else
{
return 2 * gcd3(a >> 1, b >> 1);
}
}
}
通过移位和减法来避免取模运算,提高了算法的效率。
PS: 这里的示例代码都是用int来说明问题,可以轻易地推广到大数问题,只有重载相应的运算。