分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数

程序的算法设计

一、辗转相除法
1.题目分析
辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,实质它依赖于下面的定理:
a b=0
gcd(a,b) = {
gcd(b,a mod b) b!=0
根据这一定理可以采用函数嵌套调用形式进行求两个数的最大公约数和最小公倍数。
2.算法构造
流程图如下:分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第1张图片
3.算法实现
#include
#include
#include
int divisor(int a,int b) /自定义函数求两数的最大公约数/
{
int temp; /定义整型临时变量/
if(a通过比较求出两个数中的最大值和最小值/
{
temp = a;
a = b;
b = temp;
}
while(b!=0) /通过循环求两数的余数,直到余数为0/
{
temp = a%b;
a = b;
b = temp;
}
return a; /返回最大公约数到调用函数处/
}
int multiple (int a,int b) /自定义函数求两数的最小公倍数/
{
int divisor (int a,int b); /自定义函数返回值类型/
int temp;
temp=divisor(a,b); /再次调用自定义函数,求出最大公约数/
return (a*b/temp); /返回最小公倍数到主调函数处进行输出/
}
int main()
{
int m,n,t1,t2,d,i;
clock_t start,finish; /clock_t是用来保存时间的数据类型,是一个长整形数/
double duration;
d = 0;
i = 20;
/测量事件持续的时间/
start = clock(); /计时函数:clock()/
while(i–)
{
do /判断是否输入非法/
{
printf(“Please input two integer number:”);
d = scanf("%d %d",&m,&n); /如果m和n都被成功读入,那么scanf的返回值就是2/
fflush(stdin); /清空输入缓冲区/
}while(d != 2);
t1 = divisor(m,n); /求两数的最大公约数/
t2 = multiple(m,n); /求两数的最小公倍数/
printf(“The higest common divisor is %d.\n”,t1); /输出最大公约数/
printf(“The lowest common multiple is %d.\n”,t2); /输出最小公倍数/
}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC; /常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。/
printf(“Time to do 20 loops is %f seconds.\n”,duration);
return 0;
}
4.调试、测试及运行结果
测量事件持续时间的代码:
#include
start = clock(); /计时函数:clock()/
while(i–)
{

}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC; /常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。/
printf(“Time to do 20 loops is %f seconds.\n”,duration); 分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第2张图片
运行结果
分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第3张图片
平均运行时间:45.357s

二、穷举法(利用数学定义)
1.题目分析
穷举法(也叫枚举法),穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。
对两个正整数a,b如果能在区间[a,0]或[b,0]内能找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。
对两个正整数a,b,如果若干个a之和或b之和能被b所整除或能被a所整除,则该和数即为所求的最小公倍数。
2.算法构造
流程图如下: 分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第4张图片
3.算法实现
#include
#include
#include
int divisor (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 multiple (int a,int b)
{
int p,q,temp;
p = (a>b)?a:b; /求两个数中的最大值/
q = (a>b)?b:a; /求两个数中的最小值/
temp = p; /最大值赋给p为变量自增作准备/
while(1) /利用循环语句来求满足条件的数值/
{
if(p%q == 0)
break; /只要找到变量的和数能被a或b所整除,则中止循环/
p += temp; /如果条件不满足则变量自身相加/
}
return p; /返回满足条件的数到主调函数处/
}
int main()
{
int m,n,t1,t2,a,i;
clock_t start,finish; /clock_t是用来保存时间的数据类型,是一个长整形数/
double duration;
a = 0;
i = 20;
/测量事件持续的时间/
start = clock(); /计时函数:clock()/
while(i–)
{
do /判断是否输入非法/
{
printf(“please input two integer number:”);
a = scanf("%d%d",&m,&n); /如果m和n都被成功读入,那么scanf的返回值就是2/
fflush(stdin); /清空输入缓冲区/
}while(a != 2);
t1 = divisor(m,n); /求两数的最大公约数/
t2 = multiple(m,n); /求两数的最小公倍数/
printf(“The higest common divisor is %d\n”,t1);
printf(“The least common multiple is %d\n”,t2);
}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC; /常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。/
printf(“Time to do 20 loops is %f seconds.\n”,duration);
return 0;
}
4.调试、测试及运行结果
测量事件持续时间的代码:
#include
start = clock(); /计时函数:clock()/
while(i–)
{

}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC; /常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。/
printf(“Time to do 20 loops is %f seconds.\n”,duration);
运行结果
分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第5张图片
平均运行时间:34.663s

三、更相减损法
1.题目分析
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
2.算法构造
分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第6张图片
3.算法实现
#include
#include
#include
#include
int gcd(int m,int n)
{
int i,temp,x;
i = 0;
while(m%2 == 0&&n%2 == 0) //判断m和n能被多少个2整除
{
m /= 2;
n /= 2;
i += 1;
}
if(m {
temp = m;
m = n;
n = temp;
}
while(x)
{
x = m-n;
m = (n>x)?n:x;
n = (n if(n == (m-n))
break;
}
if(i == 0)
return n;
else
return (int)pow(2,i)*n;
}
int main()
{
int m,n,t,d,i;
clock_t start,finish; /clock_t是用来保存时间的数据类型,是一个长整形数/
double duration;
d = 0;
i = 20;
/测量事件持续的时间/
start = clock(); /计时函数:clock()/
while(i–)
{
do /判断是否输入非法/
{
printf(“Please input two integer number:”);
d = scanf("%d %d",&m,&n); /如果m和n都被成功读入,那么scanf的返回值就是2/
fflush(stdin); /清空输入缓冲区/
}while(d != 2);
t = gcd(m,n); /求两数的最大公约数/
printf(“The higest common divisor is %d.\n”,t); /输出最大公约数/
}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC; /常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。/
printf(“Time to do 20 loops is %f seconds.\n”,duration);
return 0;
}
4.调试、测试及运行结果
测量事件持续时间的代码:
#include
start = clock(); /计时函数:clock()/
while(i–)
{

}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC;
printf(“Time to do 20 loops is %f seconds.\n”,duration);
平均运行时间:46.684s

四、Stein算法
1.题目分析
对两个正整数 x>y :
1.均为偶数 gcd( x,y ) =2gcd( x/2,y/2 );
2.均为奇数 gcd( x,y ) = gcd( (x+y)/2,(x-y)/2 );
3.x奇y偶 gcd( x,y ) = gcd( x,y/2 );
4.x偶y奇 gcd( x,y ) = gcd( x/2,y ) 或 gcd( x,y )=gcd( y,x/2 )。
现在已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x ,就用这个。
2.算法构造
分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第7张图片
3.算法实现
#include
#include
#include
int gcd(int u,int v)
{
if(u == 0) return v;
if(v == 0) return u;
if(~u&1) /u是偶数/
{
if(v&1) /v是奇数/
return gcd(u >> 1,v);
else /u和v都是偶数/
return gcd(u >> 1,v >> 1) << 1;
}
else if(~v&1) /u是奇数,v是偶数/
return gcd(u,v >> 1);
else /u和v都是奇数/
if (u > v)
return gcd((u - v) >> 1, v);
return gcd((v - u) >> 1, u);
}
int main()
{
int m,n,t,d,i;
clock_t start,finish; /clock_t是用来保存时间的数据类型,是一个长整形数/
double duration;
d = 0;
i = 20;
/测量事件持续的时间/
start = clock(); /计时函数:clock()/
while(i–)
{
do /判断是否输入非法/
{
printf(“Please input two integer number:”);
d = scanf("%d %d",&m,&n); /如果m和n都被成功读入,那么scanf的返回值就是2/
fflush(stdin); /清空输入缓冲区/
}while(d != 2);
t = gcd(m,n); /求两数的最大公约数/

	printf("The higest common divisor is %d.\n",t);    /*输出最大公约数*/
}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC;    /*常量CLOCKS_PER_SEC,用来表示一秒钟会有多少个时钟计时单元。*/
printf("Time to do 20 loops is %f seconds.\n",duration);

return 0;
}
4.调试、测试及运行结果
测量事件持续时间的代码:
#include
start = clock(); /计时函数:clock()/
while(i–)
{

}
finish = clock();
duration = (double)(finish-start)/CLOCKS_PER_SEC;
printf(“Time to do 20 loops is %f seconds.\n”,duration);
运行结果
分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数_第8张图片
平均运行时间:31.7458s

五、经验归纳与总结
1.注意算法中变量数值之间的相互交换方法、如何取模、怎样进行自定义函数及主调函数与被调函数间的相互关系,函数参数的定义及对应关系特点,利用控制语句如何实现。
2.采用递归调用法要注意递归终止条件的描述,只有找到递归变化的规律,才能有效地解决问题。
3.根据数学定义求任意两个正整数的最大公约数和最小公倍数,相对辗转相除法来说,易懂,容易被接受,但要注意强制退出循环过程的条件、变量的特点及控制语句的使用。
4.欧几里得算法在处理较小数字时优势是明显的,但对于大整数时,高精度的整除和取余运算就显得非常复杂,所以Stein算法的优点就在于只需要进行移位(位运算)和减法操作,处理高精度GCD问题时相对简便。
5.按位左移(<<):a<<=x即为使a乘以2的x次幂,原理是让a的二进制形式左移x位;应用为对与2的幂次方相乘使运算更快更方便;
按位右移(>>):a>>=x即为使a除以2的x次幂,原理是让a的二进制形式右移x位;应用为对与2的幂次方相除使运算更快更方便。
6.C/C++中的计时函数是clock(),而与其相关的数据类型是clock_t。在MSDN中,查得对clock函数定义如下:
clock_t clock(void);
这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间(wal-clock)。其中clock_t是用来保存时间的数据类型,在time.h文件中,我们可以找到对它的定义:
#ifndef _CLOCK_T_DEFINED
typedef long clock_t;
#define _CLOCK_T_DEFINED
#endif
很明显,clock_t是一个长整形数。在time.h文件中,还定义了一个常量CLOCKS_PER_SEC,它用来表示一秒钟会有多少个时钟计时单元,其定义如下:
#define CLOCKS_PER_SEC ((clock_t)1000)
可以看到每过千分之一秒(1毫秒),调用clock()函数返回的值就加1。

你可能感兴趣的:(分别利用辗转相除法、穷举法、更相减损法、Stein法求最大公约数和最小公倍数)