一、题目分析
本章程序的算法设计实验课的内容为运行求解两个正整数最大公约数的几种常用算法,比较不同的算法在给定不同规模测试数据的情况下的平均运行时间的长短,并找出在不同规模下最适用的算法。
求解两数最大公约数的常用算法有以下四种:
1.辗转相除法
2.穷举法(利用数学定义)
穷举法(也叫枚举法)求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数。
分析:对两个正整数a、b,如果能在区间[a,0]或[b,0]中找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。
3.更相减损法
更相减损术,是出自《九章算术》中的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。《九章算术》是中国古代的数学专著,其中的“更相减损术”可以用来求两个数的最大公约数,即“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
翻译成现代语言如下:
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
4.Stein算法
分析:
由于需要比较不同算法在给定不同规模测试数据的情况下的平均运行时间,所以需要引入能对程序运行时间进行计时的函数clock();
#include <stdio.h>
#include <time.h>
clock_t start, stop; //clock_t是clock()函数返回的变量类型
double duration; //记录被测函数的运行时间,以秒为单位
void main()
{
//不在测试范围内的准备工作写在clock()调用之前
start = clock(); //开始计时
MyFunction(); //把被测函数加在这里
stop = clock(); //停止计时
duration = ((double)(stop-start)) / CLOCKS_PER_SEC;
//其它不在测试范围内的处理写在后面
}
二、算法构造(流程图)
1.辗转相除法
2.穷举法(枚举法)
3.更相减损法
4.Stein算法
三、算法实现(源代码)
#include <stdio.h>
#include <math.h>
#include <time.h>
// 辗转相除法(函数嵌套调用)
int gcd1(int a, int b)
{
int temp;
if(a<b) //通过比较求出两个数中的最大值和最小值
{
temp = a;
a = b;
b = temp;
}
while(b!=0) //通过循环求两数的余数,直到余数为0
{
temp = a%b;
a = b;
b = temp;
}
return (a); //返回最大公约数到调用函数处
}
// 穷举法
int gcd2(int a, int b)
{
int temp;
temp = (a>b)?b:a; //通过条件运算表达式求出两个整数中的最小值
while(temp>0)
{
if(a%temp==0 && b%temp==0) //只要找到一个数能被a,b同时整除,则终止循环
break;
temp--; //如果不满足if条件则变量自减,直到能被a,b整除
}
return (temp); //返回满足条件的数到主调函数
}
// 更相减损法
int gcd3(int a, int b)
{
int i = 0, temp, x;
while(a%2==0 && b%2==0) //判断a和b能被多少个2整除
{
a/=2;
b/=2;
i+=1;
}
if(a<b) //a保存大的值
{
temp = a;
a = b;
b = temp;
}
while(x)
{
x = a-b;
if(b==(a-b))
break;
a = (b>x)?b:x;
b = (b<x)?b:x;
}
if(i==0)
return b;
else
return (int)pow(2,i)*b;
}
// Stein算法(非递归)
int gcd4(unsigned int x, unsigned int y)
{
int factor = 0;
int temp;
if(x<y)
{
temp = x;
x = y;
y = temp;
}
if(0==y)
{
return 0;
}
while(x!=y)
{
if(x & 0x1)
{
if(y & 0x1)
{
y = (x-y)>>1;
x-=y;
}
else
{
y>>=1;
}
}
else
{
if(y & 0x1)
{
x>>=1;
if(x<y)
{
temp = x;
x = y;
y = temp;
}
}
else
{
x>>=1;
y>>=1;
++factor;
}
}
}
return (x<<factor);
}
void main()
{
int m, n, x, t1;
clock_t start, stop;
double duration;
printf("您想采用那种算法求解两数的最大公因数,请选择对应的算法序号:\n");
printf("**** 1.辗转相除法 ****\n");
printf("**** 2.穷举法 ****\n");
printf("**** 3.更相减损法 ****\n");
printf("**** 4.Stein算法 ****\n");
scanf("%d", &x);
printf("please input two integer number:\n");
scanf("%d", &m);
scanf("%d", &n); //键入两个整数
start = clock(); //开启计时器
for(int i = 0; i<10000; i++)
{
switch(x) //选择用哪种算法
{
case 1:
t1 = gcd1(m, n);
printf("最大公约数为%d\n", t1);
break;
case 2:
t1 = gcd2(m, n);
printf("最大公约数为%d\n", t1);
break;
case 3:
t1 = gcd3(m, n);
printf("最大公约数为%d\n", t1);
break;
case 4:
t1 = gcd4(m, n);
printf("最大公约数为%d\n", t1);
break;
}
}
stop = clock(); //关闭计时器
duration = ((double)(stop - start)) / CLOCKS_PER_SEC; //计算算法的运行时间
printf("该算法的运行时间为%lf秒\n", duration);
}
四、调试、测试及运行结果
1.输入 a = 4, b = 12; (输入的两个整数较小时)
循环10000次
辗转相除法:
穷举法(枚举法)
更相减损法
Stein算法
2.输入a = 5376, b = 5088; (输入的两个整数较大时)
循环10000次
辗转相除法:
穷举法(枚举法)
更相减损法:
Stein算法:
结论:对于规模较大的两个整数应该避免使用穷举法求两数的最大公约数。
五、总结
通过这次实验我从中体会到了同一问题的不同解决算法对于程序的运行时间有着影响。同一种算法应用于不同规模的数据中也有着不同的运行时间。优良的算法设计对于问题的解决、效率的提高有着重要的意义。我们应当在程序设计中注重程序的算法优化。此次试验在程序的编写、实现过程中我遇到了很多的问题,就比如不知道如何计算程序的运行时间,最后通过查阅有关资料,我学会了运用时间函数clock()计算程序运行的时间。这次实验受益匪浅!ps:由于自身能力的原因,代码实现可能还存在着一些问题,希望大家能够批评指正,不吝赐教。