编程之美之2.7 最大公约数问题
问题:
求两个数的最大公约数
解法一:
欧几里得辗转相除法:
f(x,y) = GCD(x,y), 取k = x / y, b = x % y,则:x = k*y + b;
如果一个数能整除x,y,则它也能整除b,y; 而且能整除b,y的数必能整除x,y,即x,y和b,y的公约数是相同的,其最大公约数也是相同的,即f(x,y) = f(y ,x % y) (x>=y>0)
例如,计算a = 1071和b = 462的最大公约数的过程如下:从1071中不断减去462直到小于462(可以减2次,即商q0 = 2),余数是147:
1071 = 2 × 462 + 147.
然后从462中不断减去147直到小于147(可以减3次,即q1 = 3),余数是21:
462 = 3 × 147 + 21.
再从147中不断减去21直到小于21(可以减7次,即q2 = 7),没有余数:
147 = 7 × 21 + 0.
此时,余数是0,所以1071和462的最大公约数是21
递归算法:
- #include<stdio.h>
- //递归形式
- int GCD(int a,int b)
- {
- if(b == 0){
- return a;
- }
- else{
- //a,b和b,a%b有相同的最大公约数
- return GCD(b,a%b);
- }
- }
- int main(){
- int a,b;
- scanf("%d %d",&a,&b);
- printf("%d\n",GCD(a,b));
- }
例如GCD(1071, 462)的计算过程是:
函数的第一次调用计算GCD(462, 1071 mod 462) = GCD(462, 147);
下一次调用计算 GCD(147, 462 mod 147) = GCD(147, 21),
在接下来是 GCD(21, 147 mod 21) = GCD(21, 0) = 21。
- #include<stdio.h>
- //非递归形式
- int GCD(int a,int b)
- {
- int temp = a;
- while(b){
- a = b;
- b = temp % b;
- }
- return a;
- }
- int main(){
- int a,b;
- scanf("%d %d",&a,&b);
- printf("%d\n",GCD(a,b));
- }
解法二:
在解法一中我们用到了取模运算。在大整数中取模运算(涉及到除法运算)是非常高贵的开销。
我们想想避免用取模运算。
类似前面的分析,一个数能整除x,y则必能同时整除x - y,y。能同时整除x - y,y 则必能同时整除x,y。即x,y的公约数和x-y,y的公约数是一样的,其最大公约数也是一样的。
- #include<stdio.h>
- int GCD(int a,int b)
- {
- //如果a < b
- if(a < b){
- return GCD(b,a);
- }
- if(b == 0){
- return a;
- }
- else{
- return GCD(a - b,b);
- }
- }
- int main(){
- int a,b;
- scanf("%d %d",&a,&b);
- printf("%d\n",GCD(a,b));
- }
解法三:
分析:
对于x,y,如果y = k * y1,x = k * x1,则f(y,x) = K*f(x1,y1);
如果x = p * x1, 假设p是素数,且 y % p != 0 ,即y不能被p整除,则f(x,y) = f(x1,y).
可以利用上面两点进行改进。因为2是素数,同时对于二进制表示的大整数而言可以很容易的将除以2和乘以2的算法转换为移位运算,从而避免大整数除法。
可以充分利用2进行分析:
若x,y都为偶数(2肯定是公约数),则f(x,y) = 2*f(x / 2,y / 2) = 2*f(x>>1,y>>1);
若x为偶数,y为奇数(2肯定不是公约数),则f(x,y) = f(x / 2, y / 2) = f(x>>1, y)
若x为奇数,y为偶数2肯定不是公约数),则f(x,y)= f(x, y / 2) = f(x, y>>1)
若x,y都为奇数(2肯定不是公约数),则f(x,y) = f(y, x-y) (x-y肯定为偶数) = f(y, (x-y)/2)
- #include<stdio.h>
- //判断奇偶性
- int IsEvenOdd(int n){
- if(n % 2 == 0){
- return 1;
- }
- else{
- return 0;
- }
- }
- int GCD(int a,int b)
- {
- //如果a < b
- if(a < b){
- return GCD(b,a);
- }
- if(b == 0){
- return a;
- }
- //若x,y都为偶数
- if(IsEvenOdd(a) == 1 && IsEvenOdd(b) == 1){
- return 2 * GCD(a>>1,b>>1);
- }
- //若x,y都为奇数
- else if(IsEvenOdd(a) == 0 && IsEvenOdd(b) == 0){
- return GCD(b,a-b);
- }
- //若x是偶数y是奇数
- else if(IsEvenOdd(a) == 1 && IsEvenOdd(b) == 0){
- return GCD(a>>1,b);
- }
- //若x是奇数y是偶数
- else{
- return GCD(a,b>>1);
- }
- }
- int main(){
- int a,b;
- scanf("%d %d",&a,&b);
- printf("%d\n",GCD(a,b));
- }
这个算法的好处就是用移位操作来代替除法操作,大大节约时间。
最后更新:2017-04-03 08:26:12
上一篇:
ObjectArx学习笔记-画线并修改颜色
下一篇:
ObjectArx学习笔记-画线并修改颜色改进写法
magento 开发-- 单页结账时根据选择的配送方式来控制支付方式的显示
百倍增长 数据驱动
利用virtual box 自带remote display 实现windows 远程桌面连接ubuntu
HDU 1249 三角形
Subqueries are not allowed in this context. Only scalar expressions are allowed.
数据库连接字在Web.config里的用法
关于eclipse启动时报Failed to create the Java Virtural Machine.错误的解决方案
Android.mk中添加宏定义
C# dev gridcontrol “时间”字符串格式化
SQL UNION 和 UNION ALL 操作符