下面写说一个算法题:求两个正整数的最大公约数,一开始我是这么写的代码:
一:暴力枚举:暴力枚举就是把可能的答案一一列举出来,并加以判断。
但想想,我这样利用暴力枚举的效率不高呀,如果我输入的这两个数是100000000和10000001,岂不是程序得循环100000000/2=50000000次,我如果输入更大的数那,接下来我就不刚想了。。。。。。。下面介绍一种算法——辗转相除public static int GetGreatestCommonDivisor(int numA, int numB) { int SmallNum=numA<numB?numA:numB; int BigNum=numA>=numB?numA:numB; if (BigNum % SmallNum == 0) { return SmallNum; } int greatestcommonDivisor=1; //初始最大公约数为1 for(int i=2;i<=SmallNum/2;i++) { if(numA%i==0 && numB%i==0) { greatestcommonDivisor=i; //每次只要都能被整除,就假设该数是最大的公约数 } } return greatestcommonDivisor; }
二:辗转相除:又名欧几里得算法,原理:两个正整数a和b(a>b),他们的最大公约数等于a除以的余数c和b之间的 最大公约数。例如10和25的最大公约数:25除以10的余数 为5,5和10的最大公约数为5,即10和25的最大公约数为5.
根据这条原理,再利用递归的方法,即先计算出a除以b的余数c,b与c的最大公约数为e,e与c的最大公约数为f,......依次类推,直到两个数可以整除,即余数为0。代码思路如下:
三:更相减损术:原理:两个正整数a和b(a>b),它们的最大公约数为a-b与b的最大公约数.例如:10和25的最大公约数为(25-10)=15 与10的最大公约数.//方法接口,其实此方法就是先要判断输入的两个数的大小 public static int getGreatestDivisor(int numA,int numB) { int divisor = 1; //判断这两个数的大小 if (numA > numB) { divisor = GetDivisor(numA, numB); } else { divisor = GetDivisor(numB, numA); } return divisor; } //找最大公约数 public static int GetDivisor(int a,int b) { //连个数是否可整除 if (a % b == 0) { return b; } else { return GetDivisor(b, a % b);//调用自己的方法,利用递归找最大公约数 } }
注意:当整型数a,b较大时,a%b取模的性能会很低(所谓的性能,解释参考:https://zhidao.baidu.com/question/15701184.html)。
根据这条原理,再利用递归方法,即a减b的差为c,c与b的最大公约数e,b减e的差为f,e与f的最大公约数为g,.......依次类推,知道两个数相等,即差为0.代码思路如下:
四:移位算法,该算法的性能比较快,利用更相减损术和移位结合,可避免取模运算,而且算法性能稳定注意:此方法运算的次数大于辗转相除法取模的方式,更相减损法不稳定,当a,b相差较大时,a=10000,b=1,就要递归9999次。//递归求最大公约数 public static int GetDivisor(int numA, int numB) { if (numA - numB == 0)//两个数相等的情况 { return numA; } if (numA > numB) { return (GetDivisor(numB, numA - numB));//调用自己的方法,实现递归 } else { return(GetDivisor(numA,numB-numA));//调用自己的方法,实现递归 } }
1.a和b均为偶数,GetDivisor(a,b)=2*GetDivisor(a/2,b/2)=2*GetDivisor(a>>1,b>>1)
2.a为偶数,b为奇数,GetDivisor(a,b)=GetDivisor(a/2,b)=GetDivisor(a>>1,b)
3.a为奇数,b为偶数,GetDivisor(a,b)=GetDivisor(a,b/2)=GetDivisor(a,b>>1)
4.a,b均为奇数时,则利用更相减损术运算一次,GetDivisor(a,b)=GetDivisor(b,a-b),此时a-b必然是偶数,又可以继续进行移位运算了。
【总结】public static int GetDivisor(int numA, int numB) { if (numA == numB) { return numA; } if (numA < numB)//保证numA永远是较大者,较少代码量 { return GetDivisor(numB, numA); } else { if (numA % 2 ==0 && numB % 2==0) //偶偶 { return GetDivisor(numA >> 1, numB >> 1) << 1; } else if (numA % 2==0 && numB % 2!=0)//偶奇 { return GetDivisor(numA >> 1, numB); } else if (numA % 2!=0 && numB % 2==0)//奇偶 { return GetDivisor(numA, numB >> 1); } else //奇奇 { return GetDivisor(numB, numA - numB); } } }
以上方法的时间复杂度O各自有所不同,有关时间复杂度的知识还有待研究。