喜迎
春节

最大公约数几种算法分析


程序设计方法学上机——(一)

 

  • 题目:

运行最大公约数的常用算法,进行程序的调试与测试,要求程序设计风良好,并添加异常处理模块(如非法输入)。

 

  1. 辗转相除法:

其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数

==>大数放a中、小数放b中;

==>求a/b的余数;

==>若temp=0则b为最大公约数;

==>如果temp!=0则把b的值给a、temp的值给a;

==>返回第二步;

(1)非递归

(2)递归

  1. 穷举法:

穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。

  1. 更相减损法:

==>任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。

==>以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。

其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。

  1. Stein算法

对两个正整数 x>y :

1.均为偶数 gcd( x,y ) =2gcd( x/2,y/2 );

2.均为奇数 gcd( x,y ) = gcd( (x+y)/2,(x-y)/2 );

2.x奇y偶   gcd( x,y ) = gcd( x,y/2 );

3.x偶y奇   gcd( x,y ) = gcd( x/2,y )  或 gcd( x,y )=gcd( y,x/2 );

现在已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x ,就用这个。

  1. 非递归
  2. 递归
  • 算法构造:
  1. 辗转相除流程图:

(1)非递归:

 

(2)递归:

 

 

 

  1. 穷举法流程图:

 

 

  1. 更相减损法流程图:

 

  1. Stein算法流程图:
  1. 非递归:

 

  1. 递归:

 

  • 算法实现:(源码)
  1. 辗转相除法:

(1)非递归:

  1. //辗转相除法
  2. /*函数嵌套调用*/
  3. #include<stdio.h>  /*输入输出类头文件*/
  4. #include<stdlib.h>
  5. #include<time.h>
  6. #include<windows.h>
  7. int GCD (int a,int b)    /*自定义函数求两数的最大公约数*/
  8. {
  9.   int  temp;          /*定义整型变量,用于交换变量时做临时变量*/
  10.   if(a<b)             /*通过比较求出两个数中的最大值和最小值*/
  11.     { temp=a;a=b;b=temp;} /*设置中间变量进行两数交换*/
  12.    while(b!=0)           /*通过循环求两数的余数,直到余数为0*/
  13.     {
  14.       temp=a%b;
  15.       a=b;              /*变量数值交换*/
  16.       b=temp;
  17.     }
  18.   return (a);            /*返回最大公约数到调用函数处*/
  19. }
  20. int main()
  21. {
  22. /*计时函数所需定义语句*/
  23. double dur;
  24. LARGE_INTEGER time_start;
  25. LARGE_INTEGER time_over;
  26. double dqFreq;
  27. LARGE_INTEGER f;
  28. QueryPerformanceFrequency(&f);
  29. dqFreq=(double)f.QuadPart;
  30.  int gcd; /*公约数*/
  31.  int i,a[100001],b[100001]; /*定义两个数组*/
  32.  srand(1);                  /*随机函数*/
  33.  for(i=0;i<20;i++)         /*循环来控制数据规模*/
  34.  {
  35.   a[i]=1+rand()%100;
  36.   b[i]=1+rand()%100;
  37.  }
  38.  QueryPerformanceCounter(&time_start);
  39. for(i=0;i<20;i++)
  40. {
  41.   gcd=GCD(a[i],b[i]);    /*自定义函数*/
  42.   printf("随机数为%d,%d",a[i],b[i]);
  43.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  44.   QueryPerformanceCounter(&time_over);
  45.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  46. }
  47. printf("所需时间%f seconds\n",dur);
  48.  return 0;
  49. }
  1. 递归:
  1. //辗转相除法
  2. /*函数递归*/
  3. #include<stdio.h>  /*输入输出类头文件*/
  4. #include<stdlib.h>
  5. #include<windows.h>
  6. int GCD (int a,int b) /*自定义函数求两数的最大公约数*/
  7. {  if(a%b==0) /*看b是否是最大公约*/
  8.        return b;
  9. else
  10.        return GCD(b,a%b);
  11. }
  12. int main() /*主函数*/
  13. {
  14. /*计时函数所需定义语句*/
  15. double dur;
  16. LARGE_INTEGER time_start;
  17. LARGE_INTEGER time_over;
  18. double dqFreq;
  19. LARGE_INTEGER f;
  20. QueryPerformanceFrequency(&f);
  21. dqFreq=(double)f.QuadPart;
  22.  int gcd; /*变量用于存最大公约数*/
  23.  int i,a[100001],b[100001]; /*定义两个数组*/
  24.  srand(10);                     /*随机函数*/
  25.  for(i=0;i<100000;i++)
  26.  {
  27.   a[i]=1+rand()%100;
  28.   b[i]=1+rand()%100;
  29.  }
  30.  QueryPerformanceCounter(&time_start);
  31. for(i=0;i<100000;i++)
  32. {
  33.   gcd=GCD(a[i],b[i]);           /*自定义主调函数*/
  34.   printf("随机数为%d,%d",a[i],b[i]);
  35.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  36.   QueryPerformanceCounter(&time_over);
  37.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  38. }
  39. printf("所需时间%f seconds\n",dur);
  40.  return 0;
  41. }

 

 

  1. 穷举法:
  1. //穷举法
  2. #include<stdio.h>  /*输入输出类头文件*/
  3. #include<stdlib.h>
  4. #include<time.h>
  5. #include<windows.h>
  6. #include<math.h>
  7. int GCD (int a,int b) /*自定义函数求两数的最大公约数*/
  8. {
  9.     int  temp;          /*定义义整型变量*/
  10.     temp=(a>b)?b:a;    /*采种条件运算表达式求出两个数中的最小值*/
  11.     while(temp>0)
  12.     {
  13.        if (a%temp==0&&b%temp==0) /*只要找到一个数能同时被a,b所整除,则中止循环*/
  14.           break;
  15.        temp--;      /*如不满足if条件则变量自减,直到能被a,b所整除*/
  16.     }
  17.   return (temp); /*返回满足条件的数到主调函数处*/
  18. }
  19. int main()
  20. {
  21. /*计时函数所需定义语句*/
  22. double dur;
  23. LARGE_INTEGER time_start;
  24. LARGE_INTEGER time_over;
  25. double dqFreq;
  26. LARGE_INTEGER f;
  27. QueryPerformanceFrequency(&f);
  28. dqFreq=(double)f.QuadPart;
  29.  int gcd; /*变量用于存最大公约数*/
  30.  int i,a[100001],b[100001];     /*定义两个数组*/
  31.  srand(10);                     /*随机函数*/
  32.  for(i=0;i<100000;i++) /*通过循环输入规模*/
  33.  {
  34.   a[i]=1+rand()%100;
  35.   b[i]=1+rand()%100;
  36.  }
  37.  QueryPerformanceCounter(&time_start);
  38. for(i=0;i<100000;i++)
  39. {
  40.   gcd=GCD(a[i],b[i]); /*自定义主调函数*/
  41.   printf("随机数为%d,%d",a[i],b[i]);
  42.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  43.   QueryPerformanceCounter(&time_over);
  44.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  45. }
  46. printf("所需时间%f seconds\n",dur);
  47.  return 0;
  48. }

 

  1. 更相减损:
  1. //更相减损
  2. #include<stdio.h>  /*输入输出类头文件*/
  3. #include<stdlib.h>
  4. #include<time.h>
  5. #include<windows.h>
  6. #include<math.h>
  7. int GCD(int m,int n)
  8. {
  9. int i=0,temp,x;
  10. while(m%2==0 && n%2==0)  //判断m和n能被多少个2整除
  11. {
  12. m/=2;
  13. n/=2;
  14. i+=1;
  15. }
  16. if(m<n)     //m保存大的值
  17. {
  18. temp=m;
  19. m=n;
  20. n=temp;
  21. }
  22. while(x)
  23. {
  24. x=m-n;
  25. m=(n>x)?n:x;
  26. n=(n<x)?n:x;
  27. if(n==(m-n))
  28. break;
  29. }
  30. if(i==0)
  31. return n;
  32. else
  33. return (int )pow(2,i)*n;
  34. }
  35. int main()
  36. {
  37. //计时函数所需的定义语句
  38. double dur;
  39. LARGE_INTEGER time_start;
  40. LARGE_INTEGER time_over;
  41. double dqFreq;
  42. LARGE_INTEGER f;
  43. QueryPerformanceFrequency(&f);
  44. dqFreq=(double)f.QuadPart;
  45.  int gcd; //定义变量用于存放最大公约数
  46.  int i,a[100001],b[100001];//定义两个数组
  47.  srand(10);      //随机函数
  48.  for(i=0;i<100000;i++) //通过循环控制规模
  49.  {
  50.   a[i]=1+rand()%100;
  51.   b[i]=1+rand()%100;
  52.  }
  53.  QueryPerformanceCounter(&time_start);
  54. for(i=0;i<100000;i++)
  55. {
  56.   gcd=GCD(a[i],b[i]); //自定义主调函数
  57.   printf("随机数为%d,%d",a[i],b[i]);
  58.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  59.   QueryPerformanceCounter(&time_over);
  60.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  61. }
  62. printf("所需时间%f seconds\n",dur);
  63.  return 0;
  64. }

 

  1. Stein算法:
  1. 非递归:
  1.  //stein
  2.  /*非递归*/
  3. #include<stdio.h>  /*输入输出类头文件*/
  4. #include<stdlib.h>
  5. #include<time.h>
  6. #include<windows.h>
  7. #include<math.h>
  8.  int SteinGCD( unsigned int x, unsigned int y )
  9.   /* return the greatest common divisor of x and y */
  10. {
  11.         int factor = 0;
  12.         int temp;
  13.         if ( x < y )
  14.         {
  15.                 temp = x;
  16.                 x = y;
  17.                 y = temp;
  18.         }
  19.         if ( 0 == y )
  20.         {
  21.                 return 0;
  22.         }
  23.         while ( x != y )
  24.         {
  25.                 if ( x & 0x1 )
  26.                 {/* when x is odd */
  27.                         if ( y & 0x1 )
  28.                         {/* when x and y are both odd */
  29.                                 y = ( x - y ) >> 1;
  30.                                 x -= y;
  31.                         }
  32.                         else
  33.                         {/* when x is odd and y is even */
  34.                                 y >>= 1;
  35.                         }
  36.                 }
  37.                 else
  38.                 {/* when x is even */
  39.                         if ( y & 0x1 )
  40.                         {/* when x is even and y is odd */
  41.                                 x >>= 1;
  42.                                 if ( x < y )
  43.                                 {
  44.                                         temp = x;
  45.                                         x = y;
  46.                                         y = temp;
  47.                                 }
  48.                         }
  49.                         else
  50.                         {/* when x and y are both even */
  51.                                 x >>= 1;
  52.                                 y >>= 1;
  53.                                 ++factor;
  54.                         }
  55.                 }
  56.         }
  57.         return ( x << factor );
  58. }
  59. int main()
  60. {
  61. //计时函数所需要定义的语句
  62. double dur;
  63. LARGE_INTEGER time_start;
  64. LARGE_INTEGER time_over;
  65. double dqFreq;
  66. LARGE_INTEGER f;
  67. QueryPerformanceFrequency(&f);
  68. dqFreq=(double)f.QuadPart;
  69.  int gcd;//定义变量用于存放最大公约数
  70.  int i,a[100001],b[100001];//定义两个数组
  71.  srand(10);   //随机函数
  72.  for(i=0;i<100000;i++)//通过循环控制规模
  73.  {
  74.   a[i]=1+rand()%100;
  75.   b[i]=1+rand()%100;
  76.  }
  77.  QueryPerformanceCounter(&time_start);
  78. for(i=0;i<100000;i++)
  79. {
  80.   gcd=SteinGCD(a[i],b[i]);
  81.   printf("随机数为%d,%d",a[i],b[i]);
  82.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  83.   //printf("所需时间%f\n",(double)(end-start)/CLOCKS_PER_SEC);
  84.   QueryPerformanceCounter(&time_over);
  85.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  86. }
  87. printf("所需时间%f seconds\n",dur);
  88.  return 0;
  89. }
  1. 递归:
  1. //stein
  2. /*递归实现*/
  3. #include<stdio.h>  /*输入输出类头文件*/
  4. #include<stdlib.h>
  5. #include<time.h>
  6. #include<windows.h>
  7. #include<math.h>
  8. int SteinGCD(int u,int v)
  9. {
  10.     if (u == 0) return v;
  11.     if (v == 0) return u;
  12.     // look for factors of 2
  13.     if (~u & 1) // u is even
  14.     {
  15.         if (v & 1) // v is odd
  16.             return SteinGCD(u >> 1, v);
  17.         else // both u and v are even
  18.             return SteinGCD(u >> 1, v >> 1) << 1;
  19.     }
  20.      if (~v & 1) // u is odd, v is even
  21.         return SteinGCD(u, v >> 1);
  22.      // reduce larger argument
  23.     if (u > v)
  24.         return SteinGCD((u - v) >> 1, v);
  25.      return SteinGCD((v - u) >> 1, u);
  26. }
  27. int main()
  28. {
  29. //计时函数所需要定义的语句
  30. double dur;
  31. LARGE_INTEGER time_start;
  32. LARGE_INTEGER time_over;
  33. double dqFreq;
  34. LARGE_INTEGER f;
  35. QueryPerformanceFrequency(&f);
  36. dqFreq=(double)f.QuadPart;
  37.  int gcd;//定义变量用于存放最大公约数
  38.  int i,a[100001],b[100001];//定义两个数组
  39.  srand(10);   //随机函数
  40.  for(i=0;i<100000;i++)//通过循环控制规模
  41.  {
  42.   a[i]=1+rand()%100;
  43.   b[i]=1+rand()%100;
  44.  }
  45.  QueryPerformanceCounter(&time_start);
  46. for(i=0;i<100000;i++)
  47. {
  48.   gcd=SteinGCD(a[i],b[i]);
  49.   printf("随机数为%d,%d",a[i],b[i]);
  50.   printf("这两个整数的最大公约数是 %d\n\n",gcd);  /*输出最大公约数*/
  51.   //printf("所需时间%f\n",(double)(end-start)/CLOCKS_PER_SEC);
  52.   QueryPerformanceCounter(&time_over);
  53.   dur=(time_over.QuadPart-time_start.QuadPart)/dqFreq;
  54. }
  55. printf("所需时间%f seconds\n",dur);
  56.  return 0;
  57. }

 

  • 调试、测试:
  1. 调试:
  1. 随机数调试:(以3组数据为例)

 

===>观察a[i],b[i]当i在变化是每次的不同随机数,此时为未开始,二者为0;

 

===>i=0时,a[i]=72,b[i]=100

 

 

===>i=1时,a[i]=73,b[i]=95

 

===>i=2,a[i]=98,b[i]=97

 

 

 

  1. 测试:

 

--Stein递归--

 

 

 

--Stein非递归--

 

 

--更相减损--

 

--穷举法--

 

 

--辗转相除递归--

 

 

--辗转相除非递归--

  • 分析:

 

 

--测试时间的表格结果--

  1. 6种算法比较:规模相同,数据范围相同:

在100组数据和1000组数据:辗转相除非递归算法效率比较高;

在10000组数据:stein算法效率较高

 

 

  1. 同一种算法比较,数据范围不同时,运行时间的变化幅度:

对于6种方法,当规模相同,随着随机数的范围不同,运行时间都有所增加,其中辗转相除非递归增加最为明显,

 

  • 总结:

本程序运用六种代码来测试效率,采取了平均值来避免偶然性误差。

 

本实验为了测试求最大公约数的算法效率,在实验过程中,遇到了一些问题。

  1. 刚开始手动输入两位两位数据,发现效率低且不能进行大规模数据测试
  2. 计时函数在用的时候是在网上看了一些程序采用到本程序上

不足:

  1. 测试数据没有实现数据为0时求公约数
  2. 每次运行时间差异较大,没有找到原因所在
  3. 感觉数据测试不是很准确,数据规模范围没有达到比较好的地步

 


如果有什么错误请各位大神指出来,完成之后记得撒花撒花~~


文章作者: 周周
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 周周 !
评 论
  目录