题目分析
一.实验目的
二.实验内容
运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。
在这里用了四种算法来求最大公约数与最小公倍数,实行输入数据测试结果和随机产生一定数据规模的数据比较算法运行时间两种方式。
算法结构
程序总流程图:
盒图:
穷举法(流程图):
Stein(流程图):
盒图:
算法实现
源代码:
#include<iostream>
#include<string>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
using namespace std;
class A
{
public:
int c;//用于选择的变量
int a;//输入数据1
int b;//输入数据2
string sa;//数据1对应的字符串
string sb;//数据2对应的字符串
int max;//求出的最大公约数
int min;//求出的最小公倍数
A();//构造函数,初始化变量值
int max1f(int a,int b);//最大公约数-辗转相除法
int max2(int a,int b);//最大公约数--穷举法
int gcd(int a,int b);//更相减损法 求最大公约数
int Stein( unsigned int x, unsigned int y );//Stein--最大公约数
int display(int max,int min);//输出函数
int ifinteger(string c);//判断输入的数是否为大于零的正整数
int f(string c);//进行异常处理
};
A::A()//初始化变量的值
{
a=0;
b=0;
sa=' ';
sb=' ';
max=0;
min=0;
}
int A::max1f(int a,int b)//最大公约数-辗转相除法
{
int t;
if(a<b)//始终保持a>b
{
t=a;
a=b;
b=t;
}
while(b>0)//辗转相除
{
t=a%b;
a=b;
b=t;
}
return a;
}
int A::max2(int a,int b)//最大公约数--穷举法
{
int t;
t=(a>b)?b:a;//将两数中的较小数赋给变量t
while(t>0)
{
if(a% t==0&&b% t==0)
break;
t--;
}
return t;
}
int A::gcd(int a,int b)//更相减损法 求最大公约数
{
while (a != b)
{
if (a > b)
a = a - b;
else
b = b - a;
}
return a;
}
int A::Stein( unsigned int x, unsigned int y )//Stein--最大公约数
{
int f= 0;
int t;
if (x < y)//使得x是最大数
{
t = x;
x = y;
y = t;
}
if ( y==0 )
{
return 0;
}
while ( x != y )
{
if ( x & 0x1 )//x为奇数
{
if ( y & 0x1 )//x,y为奇数
{
y = ( x - y ) >> 1;//相当于除以二
x -= y;
}
else//x为奇数,y为偶数
{
y >>= 1;
}
}
else//x为偶数
{
if ( y & 0x1 )//x偶数,y奇数
{
x >>= 1;
if ( x < y )
{
t = x;
x = y;
y = t;
}
}
else// x,y均为偶数
{
x >>= 1;
y >>= 1;
++f;
}
}
}
return (x<< f);
}
int A::display(int max,int min)//输出函数
{
cout<<"最大公约数为:"<<max<<endl;
cout<<"最小公倍数为:"<<min<<endl;
return 0;
}
int A::ifinteger(string c)//判断输入的数是否为大于零的正整数
{
float b=atof(c.c_str());//将输入的字符串转换为数字
if(b<=0||(b!=(int)b))//进行输入的数的异常处理
{
cout<<"输入的数"<<"不是一个正整数!"<<endl;
cout<<endl;
return 0;
}
else
return 1;//返回1带代表输入的数正确
}
int A:: f(string c)//异常处理
{
int x;
while(!(ifinteger(c)))//输入的数不符合规定时,报错并重新输入,直至输入正确!
{
c="";
cout<<"请重新输入:"<<endl;
cin>>c;
}
x=atoi(c.c_str());//装换为int型数据
return x;
}
int main(void)
{
int q;
cout<<"请输入选择:0代表自己输入一组数据进行测试"<<endl;
cout<<" 1代表随机产生一定量的数据测试"<<endl;
cin>>q;
switch(q)
{
case 0:
{
A y;
cout<<"请输入数 a:"<<endl;
cin>>y.sa;
y.a=y.f(y.sa);//输入数据的判断
cout<<"请输入数 b:"<<endl;
cin>>y.sb;
y.b=y.f(y.sb);//输入数据的判断
cout<<"请输入选择:"<<endl;
cout<<"1~4分别对应四个算法:"<<endl;
cout<<"1 代表辗转相除法(非递归)"<<endl;
cout<<"2 代表更相减损法"<<endl;
cout<<"3 代表穷举法"<<endl;
cout<<"4 代表Stein算法"<<endl;
cout<<endl;
cout<<"选择:"<<endl;
cin>>y.c;
switch(y.c)
{
case 1://选择算法1,辗转相除法
{
y.max=y.max1f(y.a,y.b);//最大公约数
y.min=(y.a*y.b/y.max);//最小公倍数
y.display(y.max,y.min);//输出结果
break;
}
case 2://选择算法2,更相减损法
{
y.max=y.gcd(y.a,y.b);
y.min=(y.a*y.b/y.max);
y.display(y.max,y.min);//输出结果
break;
}
case 3://选择算法3,穷举法
{
y.max=y.max2(y.a,y.b);
y.min=(y.a*y.b/y.max);
y.display(y.max,y.min);//输出结果
break;
}
case 4://选择算法4,Stein算法
{
y.max=y.Stein(y.a,y.b);
y.min=(y.a*y.b/y.max);
y.display(y.max,y.min);//输出结果
break;
}
default:
break;
}
break;
}
case 1:
{ A y;
srand((unsigned)time(NULL));//根据系统时间产生随机种子
int k;
int i=0;
time_t s;//记录开始时间
time_t e;//记录结束时间
int a,b=0;
cout<<"请输入数据规模的大小:"<<endl;
cin>>k;//测试数据规模量的大小
/*算法1的测试时间*/
int i1=0;
s=clock();//获取当前计时单元数
while(i1<=k)
{
a=rand();//每次随机获得一个数a
b=rand();//每次随机或得一个数b
y.max1f(a,b);//辗转相除法(非递归)求最大公倍数
i1++;
}
Sleep(1000);//将程序挂起一段时间,这里是挂起了1000ms,即1s
e=clock();//获取结束时刻计时单元数
cout<<"辗转相除法的测试时间为:"<<(double)(e-s)/CLOCKS_PER_SEC-1.000<<"s"<<endl;
/*算法2的测试时间*/
int i2=0;
s=clock();
while(i2<=k)
{
a=rand();//每次随机获得一个数a
b=rand();//每次随机获得一个数b
y.gcd(a,b);//辗转相除法(递归)求最大公倍数
i2++;
}
Sleep(1000);
e=clock();
cout<<"更相减损法的测试时间为:"<<(double)(e-s)/CLOCKS_PER_SEC-1.000<<"s"<<endl;
/*算法3的测试时间*/
int i3=0;
s=clock();
while(i3<=k)
{
a=rand();//每次随机获得一个数a
b=rand();//每次随机获得一个数b
y.max2(a,b);//穷举法求最大公倍数
i3++;
}
Sleep(1000);
e=clock();
cout<<"穷举法的测试时间为:"<<(double)(e-s)/CLOCKS_PER_SEC-1.000<<"s"<<endl;
/*算法4的测试时间*/
int i4=0;
s=clock();
while(i4<=k)
{
a=rand();//每次随机获得一个数a
b=rand();//每次随机获得一个数b
y.Stein(a,b);//实用Stein算法求最大公倍数
i4++;
}
Sleep(1000);
e=clock();
cout<<"Stein算法的测试时间为:"<<(double)(e-s)/CLOCKS_PER_SEC-1.000<<"s"<<endl;
}
break;
default:
break;
}
return 0;
}
.调试与测试
手动输入数据测试:
输入异常处理:
辗转相除法
更相减损法
穷举法
Stein法
生成一定数据规模的随机数进行测试:
调试图:
异常处理:
输入数据错误:
输入时输入的是字符串y.sa
调用函数来判定数据的正确性:
将输入的字符串转换为float型变量b
当输入数据是小数时,满足条件“b!=int(b)”输出错误信息!
当ifinteger()返回0时,函数f()进行异常处理,再次输入数据,并且判断是否为正整数,直至判定为正整数之后,将输入的字符串转换为int型数据,最为函数的返回值赋给相应变量y.a;
b的输入类似如此
输入数据调试:
输入a与b:
选择算法
这里选择1(即代表算法1—辗转相除法)
输出最大公约数(y.max)和最小公倍数(y.min)
随机函数产生结果:
s和e是算法开始和结束的cpu时间单位数,两者相减之后,再除以CLOCKS_PER_SEC(每秒钟的时间单位数),得到消耗时间(在这里因为使用了Sleep(1000),即程序挂起了1000毫秒,所以在最后的结果上减去1.000秒)
其他三种算法也是类似
总结
在本次的上机作业中,遇到了以下几个问题:
atof():int atof(const char *str );
功能:把字符串转换成单精度浮点型数。
str:要进行转换的字符串
返回值:每个函数返回 float 值,此值由将输入字符作为数字解析而生成。 如果该输入无法转换为该类型的值,则atof的返回值为 0。
说明:当第一个字符不能识别为数字时,函数将停止读入输入字符串。
即只要输入的一个字符串中识别到一个无法解析为数字的字符,停止读入字符串,并且将返回0;
2.在进行几大算法求最大公约数的时间比较时,选取了clock()函数,s,e分别对应相应的开始时钟节点数和结束时钟节点数,两者相减的差再除以CLOCKS_PER_SEC(每一秒对应的时钟节点数),即可得到消耗的时间(单位:s),原理上这样是没有什么错误的,但是进行数据测试后发现输出的时间都是0 s,后来考虑到可能是,在数据规模不是很大的情况下,测试时间的代码段运行的时间很短,s和e几乎没有差值,得到的时间太小,比如0.00001自动转换成了0秒,
改正:在s和e对应的clock节点计数之间加入Sleep(1000),将程序挂起1000ms,以便于s和e不再相同,最后在得到的时间中减去1.000秒即可得到最终的时间;
cout<<“辗转相除法的测试时间为:”<<(double)(e-s)/CLOCKS_PER_SEC-1.000<<“s”<