算法设计与分析
实验报告书
实验名称: 求两个自然数m和n的最大公约数
学 号:
姓 名:
实验时间: 2015年 4 月1 日
一 实验目的和要求
(1)复习数据结构课程的相关知识,实现课程间的平滑过渡;
(2)掌握并应用算法的数学分析和后验分析方法;
(3)理解这样一个观点:不同的算法能够解决相同的问题,这些算法的解题思路不同,复杂程度不同,解题效率也不同。
二 实验内容
(1) 至少设计出三个版本的求最大公约数算法;
(2) 对所设计的算法采用大O符号进行时间复杂性分析;
(3) 上机实现算法,并用计数法和计时法分别测算算法的运行时间;
三 实验环境
VC++6.0
四 设计思想及实验步骤
设计思想:
先分别用三种算法求出一组m和n的最大公约数,判断三个算法都是可行的,然后用text函数分别调用三个求最大公约数的函数,将k从100,每次扩大10倍,到50000000. 在每个k值附近求得1000对(m,n),调用一种函数运算1000次,用时间函数和计数器求得函数运行1000次的运行时间和计数,并求出每次的平均时间和平均计数。然后根据结果分别做出平均时间和平均计数的图标并进行分析。
实验步骤:
1. 设计三种求最大公约数的算法和测算平均时间和平均计数的算法
2. 对所设计的算法采用大O符号进行时间复杂性分析
3. 实现算法并运行,分别测算每个算法的平均时间和平均计数
4. 根据结果分别作出平均时间和平均计数的图标
5. 根据图标分析的出结论。
主要基本算法描述如下:
CommFactor1 (欧几里得)
输入:两个自然数m和n
返回:m和n的最大公约数
1.Count=0 ; r =m%n;
2. 循环直到r=0
2.1 m=n;
2.2 n=r;
2.3 r=m%n;
2.4 count++;
3. num+=count;
CommFactor2 (更相减损)
输入:两个自然数m和n
返回:m和n的最大公约数
1. count=0
2. 循环知道m==n
2.1 如果m>n ,则m=m-n;
2.2 否则 n=n-m;
2.3 count++;
3. num+=count;
CommFactor3 (蛮力法)
输入:两个自然数m和n
返回:m和n的最大公约数
1. Factor=1;
2. 循环变量i从2到min{m,n},执行下述操作:
2.1 ++count ,如果i是m和n的公因子,则执行下述操作:
2.1.1 factor=factor*i ;
2.1.2 m=m/i ;n=n/i ;
2.2 如果i不是m和n的公因子,则i=i+1;
3. num +=count;
text算法如下:
输入:待检测的函数
输出:待检测函数的运行时间和运算次数
1. 循环变量k, for(intk=100;k<=50000000;k=k*10)
1.1 输出当前k值
1.2 t =0; num=0//num用来记录待测函数运行1000次的运算次数
1.3 获得时钟频率dfFreq
1.4 获得函数运行开始时间start
1.5 循环t知道t=1000
1.5.1 获得k附近的一对随机点(m,n)
1.5.2 调用待测函数求得(m,n)的最大公约数temp
1.5.3 t ++
1.6 获得函数运行的结束时间over
1.7 求得函数运行1000次的时间 dfMinus=(double)(over-start);
1.8 求得函数每次运行的平均时间dfTim=dfMinus/dfFreq/1000;
1.9 输出平均运行时间
1.10 输出平均计数
六 核心源代码
#include
#include
#include
#include
using namespace std;
double num;
int CommFactor1(int m,int n)
{//欧几里得
int r;
r=m%n;
int count=0;
while(r!=0)
{
m=n;
n=r;
r=m%n;
count++;
}
num+=count;
return n;
}
int CommFactor2(int m,int n)
{//更相减损法
int count=0;
while(m!=n)
{
count++;
if(m>n)
m=m-n;
else
n=n-m;
}
num+=count;
return n;
}
int CommFactor3(int m,int n)
{//蛮力法
int factor=1,i;
int count=0;
for(i=2;i<=m&&i<=n;i++)
while(++count&&m%i==0&&n%i==0)
{
factor*=i;
m/=i;
n/=i;
}
num+=count;
return factor;
}
int Random(int a,int b)
{
return(rand()%(b-a)+a);
}
void text( int(*CommFactor)(int,int))
{
int m,n,t,temp;
LARGE_INTEGER litmp;
LONGLONG start,over;
doubledfMinus,dfFreq,dfTim;
for(intk=100;k<=50000000;k=k*5)
{
cout<<"k="<
t=0;
num=0;
QueryPerformanceFrequency(&litmp);
dfFreq=(double)litmp.QuadPart;
QueryPerformanceCounter(&litmp);
start=litmp.QuadPart;
while(t<1000){
m=Random(k-5,k+5);
n=Random(k-5,k+5);
temp=CommFactor(m,n);
t++;
}
QueryPerformanceCounter(&litmp);
over=litmp.QuadPart;
dfMinus=(double)(over-start);
dfTim=dfMinus/dfFreq/1000;
num=num/1000;
cout<<"平均时间T(k): ";
//cout<
printf("%e\t",dfTim);
//cout<<"平均计算次数:"<
printf("平均计算次数%.2lf\n",num);
cout<
}
}
int main()
{
int m,n;
cout<<"求m和n的最大公约数"<
cout<<"输入m和n:";
cin>>m>>n;
cout<<"欧几里得"<<"\t";
cout<<"最大公约数:"<<"\t"<
cout<<"更相减损"<<"\t";
cout<<"最大公约数:"<<"\t"<
cout<<"蛮力方法"<<"\t";
cout<<"最大公约数:"<<"\t"<
cout<<"用计数法和计时法测试算法的运行时间"<
cout<<"欧几里得"<
text(CommFactor1);
cout<<"更相减损"<
text(CommFactor2);
cout<<"蛮力方法"<
text(CommFactor3);
return 0;
}
七 实验结果及分析
1. 数学分析:采用大O符号进行的时间复杂性分析
1.1 CommFactor1(欧几里得)
欧几里得算法的时间复杂性O(log N)
假设m>n,函数会按照gcd(m,n)->gcd(n,m%n)->gcd(m%n,n%(m%n))这样递归下去,当n>m/2时有m%n=m-n
1.2 CommFactor2(更相减损)
更相减损算法的时间复杂性是O(N)
更相减损算法在两个数字大小区别很大时的效率很低,假设m很大,而n很小仅为1,则需要辗转相减m次,所以在最坏情况下,更相减损算法的时间复杂性是O(N)。
1.3 CommFactor3(蛮力法)
蛮力法的时间复杂性是O(N* )
对于蛮力法我也是按照最坏情况下分析其时间复杂性的。假设m和n是互质的,则i要从2开始一直到min(m,n),规模不能在比较中减小,所以蛮力法的时间复杂性应该也是O(N).
2 后验分析
2.1 实验运行结果如下:
求最大公约数的三个算法都是可行的
欧几里得:
更相减损:
蛮力法:
2.2 平均时间的图标
T(k)1:欧几里得的平均时间
T(k)2:更相减损的平均时间
T(k)3:蛮力方法的平均时间
2.3 平均次数的图标
Count1:欧几里得的平均计数
Count2:更相减损的平均计数
Count3:蛮力方法的平均计数
2.4后验比较分析
2.4.1 从平均时间和平均次数两张图标上可以看出,各个算法所用时间和运算次数的增长是一致的。
2.4.2 欧几里得算法是三个算法中最高效的,随着规模的不断增大,欧几里得算法的斜率几乎为0,它的时间花费和运算次数没有明显的增加,基本上保持不变,可见欧几里得算法的高效性。
2.4.3 蛮力法效率是最低的,并且随着规模的不断增大,其耗时和运算次数增长很快。
2.4.4在由实验结果所得到的图中更相减损算法的时间复杂性要小于O(N),应该是因为m,n为k附近的两个随机数,所以m和n的值不会相差太多,也就不会出现最坏的情况。
八 实验体会(包括本实验中遇到的问题、具体的解决方法、还没有解决的问题、实验收获等)
1. 这个实验让我对求两个数的最大公约数的有了更清晰的认识。虽然很耗费时间,但是知道了做一个实验的基本步骤和流程。之间分析一个算法的时间复杂性,我喜欢凭直觉,现在知道并学会使用这两种分析时间复杂性的方法。同时也学会如何使用时间函数来对程序计时了。
2.在该实验中遇到个感觉很特别的问题,在开始的实验中,为了得到每个k附近的1000对随机点,因为考虑到如果范围太小的话,那么得到的随机对的重复率很高,所以我就把范围定位k+-99 的范围内,m=Random(k-99,k+99); n=Random(k-99,k+99); 但是因为前面的k值比较小,所以前面的数据不够准确,就得到了下面的图标,在开始的数据中,更相减损和欧几里得相差很小,反而和蛮力法的时间复杂性相差很大,这和前面分析的时间复杂性有差别,经过2天的思考才终于找到问题所在,把范围减小后就可以解决这个问题了m=Random(k-5,k+5); n=Random(k-5,k+5);
3.在分析更相减损和蛮力法的时间复杂性的过程中,我都是直接考虑最坏的情况,并用最坏情况下的时间复杂性来表示算法的时间复杂性,不知道这样合理不合理。也有想过像推导欧几里得算法那样用数学方法那样推导,但是不知道要怎样推,所以我在这一点上还是有些问题的。