1.题目分析
题目:求两正整数的最大公约数。
(1).辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,实质它依赖于下面的定理:
gcd(a,b) = a b=0
gcd(a,b) = gcd(b,a mod b) b!=0
根据这一定理可以采用函数嵌套调用和递归调用形式进行求两个数的最大公约数和最小公倍数,现分别叙述如下:
①函数嵌套调用
其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步;
②函数递归调用
(2).
①对两个正整数a,b如果能在区间[a,0]或[b,0]内能找到一个整数temp能同时被a和b所整除,则temp即为最大公约数。
②对两个正整数a,b,如果若干个a之和或b之和能被b所整除或能被a所整除,则该和数即为所求的最小公倍数。
(3).更相减损法
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
(4).Stein算法
对两个正整数 x>y :
1.均为偶数 gcd( x,y ) =2gcd( x/2,y/2 );
2.均为奇数 gcd( x,y ) = gcd( (x+y)/2,(x-y)/2 );
2.x奇y偶 gcd( x,y ) = gcd( x,y/2 );
3.x偶y奇 gcd( x,y ) = gcd( x/2,y ) 或 gcd( x,y )=gcd( y,x/2 );
现在已经有了递归式,还需要再找出一个退化情况。注意到 gcd( x,x ) = x 。
2.算法构造(绘制出所有算法的流程图以及N-S盒图)
(1).辗转相除嵌套调用
(2).辗转相除递归调用
(3).第一种穷举法
(4).第二种穷举法
(5).Stein算法递归调用
3.算法实现
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<windows.h>
int gcd_gengxiang(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)
break;
}
if(i==0)
return n;
else
return (int )pow(2,i)*n;
}
int Stein(int u,int v) //Stein算法求最大公约数
{
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 Stein(u>>1,v);
else // both u and v are even
return Stein(u>>1,v>>1)<<1;
}
if (~v & 1) // u is odd, v is even
return Stein(u,v>>1); // reduce larger argument
if (u>v)
return Stein((u-v)>>1,v);
return Stein((v-u)>>1,u);
}
int divisor(int a,int b) //辗转相除求最大公约数,嵌套法
{
int temp;
if(a<b)
{
temp=a;
a=b;
b=temp;
}
while(b!=0)
{
temp=a%b;
a=b;
b=temp;
}
return a;
}
bool check(char a[]) //检测是否为正整数
{
for(int i=0; i<30; i++)
{
if((a[i])==0)
break;
if((a[i]-48)<0 || (a[i]-48)>9)
return false;
}
return true;
}
int divisor_list1(int a,int b) //第一种穷举法求最大公约数
{
int temp;
temp=(a>b)?b:a;
while(temp>0)
{
if(a%temp==0 && b%temp==0)
break;
else
temp--;
}
return temp;
}
int divisor_list2(int a,int b) //第二种穷举法求最大公约数
{
int p,q,t;
p=a>b?a:b;//max number
q=a>b?b:a;//min number
t=p;
while(1)
{
if(p%q==0)
break;
p+=t;
}
return (a*b)/p;
}
int gcd(int a,int b) //辗装相除递归求最大公约数
{
if(a%b==0)
return b;
else
return gcd(b,a%b);
}
void time_test(int N)
{
int a,b,i;
double time=0;
LARGE_INTEGER nFreq;
LARGE_INTEGER nBeginTime;
LARGE_INTEGER nEndTime;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
gcd(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("辗装相除递归求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
gcd_gengxiang(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("更相减损法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
Stein(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("Stein算法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("辗转相除嵌套法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor_list1(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("第一种穷举法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor_list2(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("第二种穷举法求最大公约数 %d 组数据所用时间为:%fms\n\n",N,time*1000);
}
void main()
{
for(int i=100;i<401;i+=100) //测试时间代码
time_test(i);
char a[30],b[30]; //求两数最大公约数部分
printf("please input two positive integer number:\n");
scanf("%s%s",a,b);
if(check(a) && check(b))
printf("The higest common divisor is:%d \n",Stein(atoi(a),atoi(b)));
else
printf("数据有误\n");
}
4.调试、测试及运行结果(至少比较4种GCD算法在给定不同规模测试数据的情况下的平均运行时间)
① 调试截图:
②对各模块进行单独测试验证模块功能是否完整正确:
(1)bool check(char a[]) //检测两个数是否为正整数
(2)int divisor(int a,int b) //辗转相除求最大公约数,嵌套法
(3)int divisor_list1(int a,int b) //第一种穷举法求最大公约数
(4)int divisor_list2(int a,int b) //第二种穷举法求最大公约数
(5)int gcd(int a,int b) //辗装相除递归求最大公约数
③ 时间性能测试:
测试函数:
void time_test(int N)
{
int a,b,i;
double time=0;
LARGE_INTEGER nFreq;
LARGE_INTEGER nBeginTime;
LARGE_INTEGER nEndTime;
QueryPerformanceFrequency(&nFreq);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
gcd(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("辗装相除递归求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
gcd_gengxiang(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("更相减损法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
Stein(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("Stein算法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("辗转相除嵌套法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor_list1(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("第一种穷举法求最大公约数 %d 组数据所用时间为:%fms\n",N,time*1000);
QueryPerformanceCounter(&nBeginTime);//开始计时
for(i=0; i<N; i++)
{
a=rand() %10000000;
b=rand() %10000000;
divisor_list2(a,b);
}
QueryPerformanceCounter(&nEndTime);//停止计时
time=(double)(nEndTime.QuadPart-nBeginTime.QuadPart)/(double)nFreq.QuadPart;//计算程序执行时间单位为s
printf("第二种穷举法求最大公约数 %d 组数据所用时间为:%fms\n\n",N,time*1000);
}
void main()
{
for(int i=100;i<401;i+=100)
time_test(i);
}
测试截图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/20190309231345411.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjAyMjcyOA==,size_16,color_FFFFFF,t_70)
5.经验归纳
①字符串处理:查漏补缺,复习常见ASCII码值与字符的关系,并以此为依据判断输入的字符是否为数字字符,是否出现’-’,’.’, 等与题目要求相违背的字符。
查找头文件<stdlib.h>中int atoi(const char *str)的用法,用此函数转化可转化数字字符串。
字符串数组作为函数的参数时,参数应该为地址。字符串数组中末尾会默认加’\0’字符。
②调试及测试:将各功能分模块写程序,并分模块测试该部分功能是否完整及正确。
时间性能测试:出现问题为使用<time.h>文件时测出时间数据为0,在论坛检索后使用高精度时控函数QueryPerformanceFrequency(),QueryPerformanceCounter()
作者:幸运的铁匠
来源:CSDN
原文:https://blog.csdn.net/u012286517/article/details/50331865
原理:
QueryPerformanceCounter()这个函数返回高精确度性能计数器的值,它可以以微妙为单位计时.但是QueryPerformanceCounter()确切的精确计时的最小单位是与系统有关的,所以,必须要查询系统以得到QueryPerformanceCounter()返回的嘀哒声的频率. QueryPerformanceFrequency()提供了这个频率值,返回每秒嘀哒声的个数. 计算确切的时间是从第一次调用QueryPerformanceCounter()开始的 假设得到的LARGE_INTEGER为nStartCounter,过一段时间后再次调用该函数结束的, 设得到nStopCounter. 两者之差除以QueryPerformanceFrequency()的频率就是开始到结束之间的秒数.由于计时函数本身要耗费很少的时间,要减去一个很少的时间开销.但一般都把这个开销忽略。