1.题目分析
分别用辗转相除法、穷举法、更相减损法、stein算法求最大公约数,并测试比较几种算法在不同规模测试数据下的平均运行时间。
2.算法构造(流程图)
3.算法实现
(1)辗转相除法的函数嵌套调用实现
int divisor (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); /*返回最大公约数到调用函数处*/
}
2)辗转相除法的函数递归调用
int gcd (int a,int b)
{
if(a%b==0)
return b;
else
return gcd(b,a%b);
}
(3)穷举法:
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); /*返回满足条件的数到主调函数处*/
}
(4)更相减损术
int gcd(int m,int n)
{
int i=0,temp,x;
while(m%2==0 && n%2==0) //判断m和n能被多少个2整除
{
m/=2;
n/=2;
i+=1;
}
if(m<n) //m保存大的值
{
temp=m;
m=n;
n=temp;
}
while(x)
{
x=m-n;
m=(n>x)?n:x;
n=(n<x)?n:x;
if(n==(m-n))
break;
}
if(i==0)
return n;
else
return (int )pow(2,i)*n;
}
(5)Stein算法非递归实现
int Stein( unsigned int x, unsigned int y )
/* return the greatest common divisor of x and 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 )
{/* when x is odd */
if ( y & 0x1 )
{/* when x and y are both odd */
y = ( x - y ) >> 1;
x -= y;
}
else
{/* when x is odd and y is even */
y >>= 1;
}
}
else
{/* when x is even */
if ( y & 0x1 )
{/* when x is even and y is odd */
x >>= 1;
if ( x < y )
{
temp = x;
x = y;
y = temp;
}
}
else
{/* when x and y are both even */
x >>= 1;
y >>= 1;
++factor;
}
}
}
return ( x << factor );
}
(6)Stein算法递归实现
int gcd(int u,int v)
{
if (u == 0) return v;
if (v == 0) return u;
//look for factors of 2
if(~u & 1) // u is even
{
if(v & 1) // v is odd
return gcd(u >> 1, v);
else // both u and v are even
return gcd(u >> 1, v >> 1) << 1;
}
if (~v & 1) // u is odd, v is even
return gcd(u, v >> 1);
//reduce larger argument
if(u > v)
return gcd((u - v) >> 1, v);
return gcd((v - u) >> 1, u);
}
具体实现:
#include<iostream> //主函数通过调用gcd函数,测算时间
#include"stdio.h"
#include<windows.h>
using namespace std;
LONGLONG GetLastTime()
{
// CPU频率
LARGE_INTEGER liQPF;
// 记录开始和结束时间
LARGE_INTEGER liStartTime, liEndTime;
// 记录过程时间
LONGLONG llLastTime;
// 获取CPU频率
QueryPerformanceFrequency(&liQPF);
// 获取开始时间
QueryPerformanceCounter(&liStartTime);
Sleep(10);
// 获取结束时间
QueryPerformanceCounter(&liEndTime);
// 计算持续时间(us)
llLastTime = 1000000 * (liEndTime.QuadPart - liStartTime.QuadPart) / liQPF.QuadPart;
return llLastTime;
}
int IsInteger(int a)//判断输入是否合法
{
if(a%1==0)
return 1;
return 0;
}
int main()
{
//定义两个数组,存放测试数据
int a[20] ={10032,24534,32323,4738,534345,645426,24456,8536,9632,10364,1231,23353,45352,345245,3245234,5923,45234,559,234,56765 };
int b[20]= { 234234,252344,374328,412432,53343224,664245,743425,84426,9643254,1340654,123,234,56,455,456,45,56,435,345,345 };
for (int i = 0; i < 20; i++)
{
if(!(IsInteger(a[i])&&IsInteger(b[i])))
{
cout<<"非法输入!";
exit(1);
}
cout <<"最大公约数:" <<gcd(a[i], b[i]) << "\t";
cout << "运行时间:" << GetLastTime() << "us" << endl;
}
system("pause");
return 0;
}
4.调试、测试及运行结果
使用gettickcount();方法效果:
优化后的效果:
测试及调试:
比较结果:
5.经验归纳
在这次程序设计中,通过对几种不同的求最大公约数算法的比较学习中。更明显感受到几种算法各自的优点。欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来。Stein算法提供了一个优化的方法。总结两种计算时间的方法:方法一:gettickcount();这个函数并不需要开始,任何时候,调用它就会得到一个时间值,这个时间值是从开机到现在的毫秒数。头文件Windows.h。而与其相关的数据类型是DWORD 方法二:clock();用于计算该程序从启动到函数调用占用CPU的时间;精度为毫秒,头文件:time.h/ctime,与其相关的数据类型是clock_t。另外,在求算法运行时间的过程中,发现有些测试数据运算的时间为0ms,计算时间太短,
解决方法:(1),增多数据求平均值(2),精确计算时间的技术(这里用us),我用第二种方法,通过使用更加精确的方法来求时间; 获得应用程序中精确获取系统时间,则使用下面函数QueryPerformanceCounter(IN OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL);但这是获取CPU频率就需要下面这个函数了:QueryPerformanceFrequency(IN OUT PLARGE_INTEGER PerformanceFrequency OPTIONAL);