最大公约数算法设计
一.实验目的
-
明确算法的概念和特点。
-
通过对问题的分析,设计合理的算法解决问题;
二.实验内容
运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。
四种算法都用同一个相似的测试代码运行得出最大公约数和最小公倍数,并进行100个随机数的运算,并计算运行时间
1.辗转相除法
辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,
#include "stdio.h"
#include "math.h"
#include <stdlib.h>
#include <time.h>
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); /*返回最大公约数到调用函数处*/
}
int multiple (int a,int b) /*自定义函数求两数的最小公倍数*/
{
int divisor (int a,int b); /*自定义函数返回值类型*/
int temp;
temp=divisor(a,b); /*再次调用自定义函数,求出最大公约数*/
return (a*b/temp); /*返回最小公倍数到主调函数处进行输出*/
}
void test() //测试代码
{
srand(time(NULL));
int i,j;
j=0;
int x,y;
clock_t start,stop;
double d;
start=clock();
while(j<100)
{
for (i = 0; i < 2; i++)
{
int a,b;
printf("随机生成两个随机数\n");
a=1+rand()%100;
b=1+rand()%100;
x=a;
y=b;
printf("x=%d y=%d\n",x,y);
printf("这两个数的最大公约数为\n");
printf("%d\n",divisor(x,y));
printf("最小公倍数为\n %d\n",multiple(x,y));
}
j++;
}
stop=clock();
d=(double)(stop-start)/CLOCKS_PER_SEC;
printf("计算用时%lf\n",d);
}
void main()
{
int m,n,t1,t2; /*定义整型变量*/
printf("请输入两个数:"); /*提示输入两个整数*/
scanf("%d%d",&m,&n); /*通过终端输入两个数*/
while(m==0||n==0)
{
printf("输入的数必须是非0的\n请重新输入这两个数:");
scanf("%d%d",&m,&n);
}
t1=divisor(m,n); /*自定义主调函数*/
t2=multiple(m,n); /*自定义主调函数*/
printf("最大公约数是 %d\n",t1); /*输出最大公约数*/
printf("最小公倍数是 %d\n", t2); /*输出最小公倍数*/
test();
}:
2.穷举法(利用数学定义)
穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数
#include "stdio.h"
#include <stdlib.h>
#include <time.h>
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);
}
void test() //测试代码
{
srand(time(NULL));
int i,j;
j=0;
int x,y;
clock_t start,stop;
double d;
start=clock();
while(j<100)
{
for (i = 0; i < 2; i++)
{
int a,b;
printf("随机生成两个随机数\n");
a=1+rand()%100;
b=1+rand()%100;
x=a;
y=b;
printf("x=%d y=%d\n",x,y);
printf("这两个数的最大公约数为\n");
printf("%d\n",multiple(x,y));
printf("最小公倍数为\n %d\n",multiple(x,y));
}
j++;
}
stop=clock();
d=(double)(stop-start)/CLOCKS_PER_SEC;
printf("计算用时%lf\n",d);
}
void main()
{
int m,n,t1,t2;
printf("请输入两个数:");
scanf("%d%d",&m,&n);
while(m==0||n==0) //除数为0导致错误
{
printf("输入的数必须是非0的\n请重新输入这两个数:");
scanf("%d%d",&m,&n);
}
t1=divisor(m,n);
printf("最大公约数为 %d\n",t1);
t2=multiple(m,n);
printf("最小公倍数为 %d\n",t2);
test();
}
- 更相减损法
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
#include "stdio.h"
#include "math.h"
#include <stdlib.h>
#include <time.h>
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;
}
void test() //测试代码
{
srand(time(NULL));
int i,j;
j=0;
int x,y;
clock_t start,stop;
double d;
start=clock();
while(j<10)
{
for (i = 0; i < 2; i++)
{
int a,b,t1;
printf("随机生成两个随机数\n");
a=1+rand()%100;
b=1+rand()%100;
x=a;
y=b;
printf("x=%d y=%d\n",x,y);
t1=gcd(x,y);
printf("这两个数的最大公约数为\n %d\n",t1);
printf("最小公倍数为\n %d\n",x*y/t1);
}
j++;
}
stop=clock();
d=(double)(stop-start)/CLOCKS_PER_SEC;
printf("计算用时%lf\n",d);
}
void main()
{
int m,n,t1;
printf("请输入两个数:");
scanf("%d%d",&m,&n);
while(m==0||n==0)
{
printf("输入的数必须是非0的\n请重新输入这两个数:");
scanf("%d%d",&m,&n);
}
t1=gcd(m,n);
printf("最大公约数为%d\n",t1);/*最大公约数*/
printf("最小公倍数为 %d\n",m*n/t1);/*最小公倍数*/
test();
}
4.Stein算法
1、如果An=Bn,那么An(或Bn)*Cn是最大公约数,算法结束
2、如果An=0,Bn是最大公约数,算法结束
3、如果Bn=0,An是最大公约数,算法结束
4、设置A1=A、B1=B和C1=1
5、如果An和Bn都是偶数,则An+1=An/2,Bn+1=Bn/2,Cn+1=Cn*2(注意,乘2只要把整数左移一位即可,除2只要把整数右移一位即可)
6、如果An是偶数,Bn不是偶数,则An+1=An/2,Bn+1=Bn,Cn+1=Cn(很显然啦,2不是奇数的约数)
7、如果Bn是偶数,An不是偶数,则Bn+1=Bn/2,An+1=An,Cn+1=Cn(很显然啦,2不是奇数的约数)
8、如果An和Bn都不是偶数,则An+1=|An-Bn|/2,Bn+1=min(An,Bn),Cn+1=Cn
9、n加1,转1
#include "stdio.h"
#include <stdlib.h>
#include <time.h>
int Stein( int x, int 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 ) //x&1意为x的值二进制(0x为十六进制)最后一位是否为1
{ //若是1的话 则表明x为奇数
if ( y & 0x1 ) //y也为奇数
{
y = ( x - y ) >> 1; //>> 1 右移一位 相当于除以2
x -= y; //x=x-y
}
else //当x为奇数 y不是奇数时
{
y >>= 1; // y/2
}
}
else //当x不是奇数 而y时奇数
{
if ( y & 0x1 )
{
x >>= 1;
if ( x < y )
{
temp = x;
x = y;
y = temp;
}
}
else //x y 都不为奇数时
{
x >>= 1;
y >>= 1;
++factor;
}
}
//x二进制左移factor位 相当于x*2的factor次方
}
return ( x << factor );
}
int gcd(int u,int v)
{
if (u == 0) return v;
if (v == 0) return u;
if (~u & 1) // ~作用是将u的二进制1变为0,0变为1, ~u&1即是判定u为偶数
{
if (v & 1) // v的二进制末尾是否为1 来判断v是否为奇数
return gcd(u >> 1, v); //递归调用将u除以2,v
else // 两个都为偶数
return gcd(u >> 1, v >> 1) << 1; //在最后返回的值再左移一位也就是乘2
}
if (~v & 1) // u是奇数,v是偶数时
return gcd(u, v >> 1); //递归调用u,v/2
if (u > v)
return gcd((u - v) >> 1, v); //u>v时递归调用 (u-v)/2,v
return gcd((v - u) >> 1, u); //最后返回(v-u)/2,u
}
void test() //测试代码
{
srand(time(NULL));
int i,j;
j=0;
int x,y;
clock_t start,stop;
double d;
start=clock();
while(j<100)
{
for (i = 0; i < 2; i++)
{
int a,b;
printf("随机生成两个随机数\n");
a=1+rand()%100;
b=1+rand()%100;
x=a;
y=b;
printf("x=%d y=%d\n",x,y);
printf("这两个数的最大公约数为\n");
printf("%d\n",gcd(x,y));
}
j++;
}
stop=clock();
d=(double)(stop-start)/CLOCKS_PER_SEC;
printf("计算用时%lf\n",d);
}
int main()
{
int x,y;
printf("请输入两个数:\n");
scanf("%d%d",&x,&y);
while(x==0||y==0) //除数为0导致错误
{
printf("输入的数必须是非0的\n请重新输入这两个数:");
scanf("%d%d",&x,&y);
}
printf("这俩数的最大公约数为:\n");
printf("%d\n",Stein(x,y));
printf("%d\n",gcd(x,y));
test();
}