灵感编程:最大公约数算法解析

开发 后端 算法
开始研究算法,用数学的思想解决程序问题,眼前顿时一亮啊。

给定两个正整数,求其最大公约数,相信这是每一个写代码的同学绝壁遇见过的练习,当然解法也非常多,下面先给出一个没有经过任何算法处理的程序:

 

 

 

 

 

 

 

 

  1. public static int getResult(int a,int b){  
  2. int max = (a>b)?a:b;  
  3.     int result=0;  
  4.     for(int i = 1;i < max;i++){  
  5.         if(a%i == 0 && b%i == 0){  
  6.             result=i;  
  7.         }  
  8.     }  
  9.     return result;  
  10. }  

 

这样当然是可以解出来的,但是要循环遍历其中较大的正整数,如果两个数量都非常大的话,效率是非常低的,每当遇到效率低下的程序,我们必然会想到优化,算法优化总是很靠谱的一种方法。下面就列出几种添加算法的方法来解决最大公约数的问题。

解法一:

辗转相除法,假设求正整数 num1,num2 的最大公约数,假设f(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),这样递归运算,比如

f(42,30) = f(30,12) = f(12 , 6) = f(6,0) = 6; 这样将运算次数直接降低了很多。下面附上代码:

 

  1. int result = ((y == 0) ? x : gcd(y, x % y));  
  2.     return result;  

 

解法二:

解法一虽然很好的解决了求公约数的问题,但是算法中包含有除法,在计算机中除法的开销是很大的,能不能不用除法呢。可以这样考虑,一个数能被x , y整出,必然也能被x-y,y整出,也就是一个数被x,y整出是这个数被x-y,y整出的充分必要条件。那么f(x, y) = f(x-y , y);这样计算的话,就可以把大整数之间的取模运算转换为大整数之间的减法运算。由于f(x,y)= f(y,x),为了避免求出一个正数和一个负数的最大公约数,要灵活运用f(x,y)= f(y,x),迭代进行,直到一方为0;比如:

f(42,30) = f(30,12) = f(18 , 12)= f(12 , 6) = f(6,6)= f(6 , 0) = 6;这样运算跟上面的方法比起来,优化了大数据取模的问题,但是运算次数会增大,代码如下:

private static int gcd(int x, int y) {

 

 

 

 

 

 

 

 

  1. if (y == 0) {  
  2.         return x;  
  3.     }  
  4.     if (x < y) {  
  5.         return gcd(y, x);  
  6.     } else {  
  7.         return gcd(x - y, y);  
  8.     }  

 

解法三:

解法一的不足之处在于复杂的大数据除法运算,解法二虽然干掉了大数据的除法运算,但是增加了操作次数。两种方法都不是非常的完美,那么我们就用第三种方法来解决,第三种方法使用的二进制方案,估计很多同学看到01100100就要放弃了,千万不要,其实这东西不难。

对于x,y来说,有x=k * x1,y = k * y1 ,则f(x ,y) = f(k * x1,k * y1) = k * f(x1 ,y1);此为一。

另外,如果 x = p * x1,且p为素数,y%p != 0,则f(x ,y)= f(p * x1, y) = f(x1 ,y);此为二。

由一和二两个公式,我们可以计算公约数了:

设p=2:

假设x,y都是偶数:f(x,y)= 2f(x»1,y»1);

假设x是偶数,y是奇数:f(x,y) = f(x»1,y);

假设x是奇数,y是偶数:f(x,y) = f(x,y»1);

假设x,y都是奇数:f(x,y) = f(y,x-y);—这是根据解法二中推出来的

下面还以42 和 30 为例:

f(42,30) = f(101010,11110) = 2f(10101,1111) = 2f(1111,110)=2 * f(1111,11) = 2 f (1100,11) = 2f(110,11)=2 f(11,11) = 2 f(0,11) = 2 3=6

括号中均为二进制表达,这样最坏的情况下,复杂度也就是log 2(max(x,y));—-2是底数,尼玛,这格式弄不出来。

下面附上代码:

 

  1. if (x < y) {  
  2.         return gcd(y, x);  
  3.     }  
  4.     if (y == 0) {  
  5.         return x;  
  6.     }  
  7.     if (isEven(x)) {  
  8.         if (isEven(y)) {  
  9.             // x,y都为偶数,f(x,y)=2*f(x/2,y/2)  
  10.             return gcd(x >> 1, y >> 1) << 1;  
  11.         } else {  
  12.             // x偶数,y奇数,f(x,y)=f(x/2,y)  
  13.             return gcd(x >> 1, y);  
  14.         }  
  15.     } else {  
  16.         if (isEven(y)) {  
  17.             // x奇数,y偶数,f(x,y)=2*f(x,y/2)  
  18.             return gcd(x, y >> 1);  
  19.         } else {  
  20.             // x,y都为奇数,f(x,y)=f(x-y,y)  
  21.             return gcd(x - y, y);  
  22.         }  
  23.     }  
  24. }  
  25.  
  26. public static boolean isEven(int x) {  
  27.     return (x % 2 == 0) ? true : false;  

 

以前根本没有想过这么些玩意,第一次看算法,顿时感觉高大上啊,不过的确,看到这样解决以前常用来解决的公约数问题,的确眼前一亮啊,希望大家多多给意见哦~

 

原文链接:http://my.oschina.net/u/858241/blog/209774

责任编辑:林师授 来源: 开源中国社区
相关推荐

2023-10-16 23:49:29

2020-06-19 14:55:10

微信拍一拍社交

2023-01-11 08:51:34

2012-11-16 10:15:12

算法

2014-04-11 13:25:01

编程编程效率

2016-12-08 10:53:46

程序员编程

2019-08-22 11:09:26

程序员技能开发者

2015-06-02 15:37:21

2011-09-24 12:09:24

2010-12-14 15:40:36

Web设计师

2013-09-22 16:36:07

扁平化UI设计

2010-08-04 14:34:35

Flex编程模型

2016-10-31 20:46:22

函数式编程Javascript

2010-01-25 17:33:25

Android Men

2013-07-19 09:31:09

2016-04-25 17:58:46

数字鸿沟最大公共WI-Fi

2012-05-09 09:31:33

HTML5

2012-03-15 10:49:52

苹果Android

2009-08-31 18:17:32

C#接口编程

2021-08-16 12:35:25

C语言内存编程
点赞
收藏

51CTO技术栈公众号