编程之美---最大公约数

该文出自于编程之美中关于最大公约数问题一章。

任意给定两个数字,得到其最大公约数 GCD(greatest common divisor),如果两个数字都很大怎么解决。

分析:最大公约数早在公元前300年,欧几里得的《几何原本》里就提出了一个高效率算法---辗转相除法。

解法一:

假设f(x,y)表示x,y的最大公约数,取k=x/y,b=x%y,则x=ky+b,如果一个数字能同时整除x,y,那么必能够同时整除b,y;而能够同时整除b,y的数必能够同时整除x,y,即x,y的公约数与b,y的公约数是相同的,其最大公约数也是相同的,则必有f(x,y)=f(y,x%y)(x>=y>0),从而把两个数字的最大公约数转化为求两个更小数字的最大公约数,直到其中一个数字为0,剩下的另一个数字就是两者最大的公约数。

例如: f(42,30) = f(30,12) = f(12,6) = f(6,0) = 6

代码如下:

int gcd(int x,int y)
{
    return (!y)?x:gcd(y,x%y);
}

解法二:

由于辗转相除法中存在一个最大的问题是,取模运算(其中用到了除法运算)是非常昂贵的开销,成为了算法的瓶颈。根据解法一的思路分析,假设一个数能够同时整除x,y,则必能够同时整除x-y,y;而同时能够整除x-y,y的数字也必能够同时整除x,y,即x,y的公约数与x-y,y的公约数相同,其最大公约数也是相同的 即:f(x,y) = f(x-y,y)。这样做就不需要进行大量的去摸运算,而转化为减法运算。

例如:f(42,30) = f(30,12) = f(12,18) = f(12,6) = f(6,6,)=f(6,0) = 6

代码如下:

int gcd(int x,int y)
{
    if (x < y)
    return gcd(y,x);
    if (y == 0)
    return x;
    return gcd(x-y,y);
}

解法三:

解法一处理大整数整除问题比较复杂,解法二处理虽然将大整数问题除法转换为减法运算,降低了复杂度,但是他的问题在于减法迭代次数太多了。最好的方法就是解法一和解法二结合使用。

分析: 对于 y和x来说,如果y = k*y1, x = k*x1。那么有 f(y,x)=k*(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/2,y) = f(x,y>>1);

若:x,y均为奇数,f(x,y)=f(y,x-y),那么就存在f(x,y)=f(y,x-y)之后,(x-y)是一个偶数,下一步一定会有除以2的操作了。

因此这样的复杂度为 O(log2(max(x,y)));

例如:

f(42,30) = f(1010102,111102)

      =2*f(101012,11112)

      =2*f(11112,1102)

      =2*f(11112,112)

      =2*f(11002,112)

     =2*f(112,112)

=2*f(02,112)

     = 2*112

       =6

代码如下:

//奇数 偶数判断
bool isEven(int n)
{
    return (n&1)?false:true;
}
//高效率gcd
int gcd(int x,int y)
{
    if (x<y)
    return gcd(y,x);
    if (y == 0)
    return x;
    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);
    }
}

原文地址:https://www.cnblogs.com/twoheads/p/11438804.html

时间: 2024-10-10 17:51:18

编程之美---最大公约数的相关文章

2.7 编程之美--最大公约数的3种解法[efficient method to solve gcd problem]

[本文链接] http://www.cnblogs.com/hellogiser/p/efficient-method-to-solve-gcd-problem.html [题目] 求两个正整数的最大公约数Greatest Common Divisor (GCD).如果两个正整数都很大,有什么简单的算法吗?例如,给定两个数1 100 100 210 001, 120 200 021,求出其最大公约数. [解法] [1. 辗转相除法] 辗转相除法:f(x,y) = f(y , x % y)(x>y

编程之美----最大公约数问题

求两个很大的数的最大公约数问题. 解法一:辗转相除法,但当数很大时,取模运算很耗时间. 解法二:利用f(x,y) = f(x-y, y)可以避免取模,但是当第一个数很大,而第二个数很小如1时,也比较耗时. 解法三:对于y和x来说,如果y=k*y1, x= k*x1,那么f(y,x)=k*f(y1,x1).如果x=p*x1,假设p是素数,并且y%p!=0,那么f(x,y)=f(p*x1,y)=f(x1,y).于是对于二进制表示的大整数而言可以每次除二了. 1 BigInt gcd(BigInt x

编程之美之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次,即

编程之美-分层遍历二叉树

问题:给定一个二叉树,要求按分层遍历该二叉树,即从上到下按层次访问该二叉树(每一层将单独输出一行),每一层要求访问的顺序为从左到右,并将节点依次编号.那么分层遍历如图的二叉树,正确的输出应该为: <span style="font-size:14px;">1 2 3 4 5 6 7 8</span> 书中还给出了问题2:打印二叉树中的某层次的节点(从左到右),其中根结点为第0层,成功返回true,失败返回false 分析与解法 关于二叉树的问题,由于其本身固有的

读书问题之《编程之美》 -----12061161 赵梓皓

我阅读的书是<编程之美> 刚开始的时候阅读序,就觉得控制cpu利用率这个问题很好玩,所以重点看了这部分和解决办法,问题也都大部分是这部分的.那么问题就来了(挖掘机技术xxx?中国山东找蓝翔) 咳咳,问题在下面: 1.关于问题的提出.(也是一点点建议) 本书的主要内容是告诉读者如何思考问题和解决问题.但是提出问题也是很重要的,正如爱因斯坦所说“提出一个问题往往比解决一个问题更重要”,很多面试题(比如井盖为啥是圆的)我觉得正常人很少会想到.所以,这个问题是怎么想出来的...我很好奇.也希望作者能够

《编程之美》3.6判断链表是否相交之扩展:链表找环方法证明

先看看原题:<编程之美>3.6编程判断两个链表是否相交,原题假设两个链表不带环. 为了防止剧透使得没看过原题目的读者丧失思考的乐趣,我把最好的解法隐藏起来.由于这个问题本身的解答并不是本文的重点,扩展问题也采用这种形式呈现. 注:位于(*)符号之间的文字出自于:http://blog.csdn.net/v_july_v/article/details/6447013,作者v_JULY_v. 用指针p1.p2分别指向两个链表头,不断后移:最后到达各自表尾时,若p1==p2,那么两个链表必相交 用

编程之美之买票找零

题目:假设有2N个人在排队买票,其中有N个人手持50元的钞票,另外有N个人手持100元的钞票,假设开始售票时,售票处没有零钱,问这2N个人有多少种排队方式,不至使售票处出现找不开钱的局面? 分析:队伍的序号标为0,1,...,2n-1,并把50元看作左括号,100元看作右括号,合法序列即括号能完成配对的序列.对于一个合法的序列,第0个一定是左括号,它必然与某个右括号配对,记其位置为k.那么从1到k-1.k+1到2n-1也分别是两个合法序列.那么,k必然是奇数(1到k-1一共有偶数个),设k=2i

【编程之美】java实现重建二叉树

package com.cn.binarytree.utils; /** * @author 刘利娟 [email protected] * @version 创建时间:2014年7月20日 下午2:03:30 类说明: */ class Node { Node left; Node right; char chValue; Node(char chValue) { left = null; right = null; this.chValue = chValue; } } public cla

编程之美之字符串移位包含问题

[题目] 给定两个字符串s1和s2,要求判断s2是否能够被通过s1做循环移位(rotate)得到的字符串包含.例如,S1=AABCD和s2=CDAA,返回true:给定s1=ABCD和s2=ACBD,返回false. [分析] [思路一] 从题目中可以看出,我们可以使用最直接的方法对S1进行循环移动,再进行字符串包含的判断,从而遍历其所有的可能性. 字符串循环移动,时间复杂度为O(n),字符串包含判断,采用普通的方法,时间复杂度为O(n*m),总体复杂度为O(n*n*m). 字符串包含判断,若采