运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。

一、题目分析
运行最大公约数的常用算法,并进行程序的调式与测试,要求程序设计风格良好,并添加异常处理模块(如输入非法等)。
1.辗转相除法
辗转相除法(又名欧几里德法)C语言中用于计算两个正整数a,b的最大公约数和最小公倍数,实质它依赖于下面的定理:
b=0时,gcd(a,b)=a;
b!=0时,gcd(a,b)=gcd(b,a mod b).
根据这一定理可以采用函数嵌套调用和递归调用形式进行求两个数的最大公约数和最小公倍数,现分别叙述如下:
①函数嵌套调用
其算法过程为: 前提:设两数为a,b设其中a 做被除数,b做除数,temp为余数
1、大数放a中、小数放b中;
2、求a/b的余数;
3、若temp=0则b为最大公约数;
4、如果temp!=0则把b的值给a、temp的值给a;
5、返回第二步;

2.穷举法(利用数学定义)
穷举法(也叫枚举法)穷举法求两个正整数的最大公约数的解题步骤:从两个数中较小数开始由大到小列举,直到找到公约数立即中断列举,得到的公约数便是最大公约数 。

  1. 更相减损法
    第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
    第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
    则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
    其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。

4.Stein算法
Stein算法是计算两个数的最大公约数。研究最大公约数的性质,有 gcd( kx,ky ) = kgcd( x,y ) 。试取 k=2,则有 gcd( 2x,2y ) = 2 * gcd( x,y )。先看一奇一偶的情况: 设有2x和y两个数,其中y为奇数。因为y的所有约数都是奇数,所以 a = gcd( 2x,y ) 是奇数。2x是个偶数,所以a应该是x的约数。证明:(2x)%a=0,设2x=na,因为a是奇数,2x是偶数,则必有n是偶数。又因为 x=(n/2)*a,所以 x%a=0,即a是x的约数。因为a也是y的约数,所以a是x和y的公约数,有 gcd( 2x,y ) <= gcd( x,y )。因为gcd( x,y )明显是2x和y的公约数,又有gcd( x,y ) <= gcd( 2x,y ),所以 gcd( 2x,y ) = gcd( x,y )。至此,我们得出了一奇一偶时化小的方法。
再看两个奇数的情况:设有两个奇数x和y,设x>y,注意到x+y和x-y是两个偶数,则有 gcd( x+y,x-y ) = 2 * gcd( (x+y)/2,(x-y)/2 ),那么 gcd( x,y ) 与 gcd( x+y,x-y ) 以及 gcd( (x+y)/2,(x-y)/2 ) 之间有某种联系,设 m=(x+y)/2 ,n=(x-y)/2 ,发现 m+n=x ,m-n=y 。设 a = gcd( m,n ),则 m%a=0,n%a=0 ,所以 (m+n)%a=0,(m-n)%a=0 ,即 x%a=0 ,y%a=0 ,所以a是x和y的公约数,有 gcd( m,n )<= gcd(x,y)。再设 b = gcd( x,y )为奇数,则 x%b=0,y%b=0 ,所以 (x+y)%b=0 ,(x-y)%b=0 ,又因为x+y和x-y都是偶数,跟前面一奇一偶时证明a是x的约数的方法相同,有 ((x+y)/2)%b=0,((x-y)/2)%b=0 ,即 m%b=0 ,n%b=0 ,所以b是m和n的公约数,有 gcd( x,y ) <= gcd( m,n )。所以 gcd( x,y ) = gcd( m,n ) = gcd( (x+y)/2,(x-y)/2 )。
整理一下,对两个正整数 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 ,就用这个。

二、算法构造

辗转相除法嵌套在这里插入图片描述辗转相除法递归在这里插入图片描述穷举法在这里插入图片描述更相减损法在这里插入图片描述Stein算法在这里插入图片描述

三、算法实现(程序源代码)

//输入输出类头文件
#include <stdio.h> 
//产生随机数的函数头文件       
#include <stdlib.h>
//clock()的头文件
#include <time.h>
//pow()的头文件
#include <math.h>


//辗转相除法嵌套
//自定义函数求两数的最大公约数
int divisor (int a,int b)    
{
  int  temp; 
//比较求出最大值和最小值
  if(a<b)            
    { temp=a;a=b;b=temp;}
//通过循环求两数的余数,直到余数为0 
   while(b!=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);
}


//辗转相除法递归
int gcd (int a,int b)
{  if(a%b==0)
       return b;   
else  
       return gcd(b,a%b);
  }


//穷举法
int divisorr (int a,int b) 
{
    int  temp;          
    temp=(a>b)?b:a;    
    while(temp>0)     
    {
       if (a%temp==0&&b%temp==0) 
          break;    
       temp--;      
    }
  return (temp); 
}


//更相减损法
int gcdd(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;
}


//Stein算法
int Stein( unsigned int x, unsigned 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 )
                {
                        if ( y & 0x1 )
                        {
                                y = ( x - y ) >> 1;
                                x -= y;
                        }
                        else
                        {
                                y >>= 1;
                        }
                }
                else
                {
                        if ( y & 0x1 )
                        {
                                x >>= 1;
                                if ( x < y )
                                {
                                        temp = x;
                                        x = y;
                                        y = temp;
                                }
                        }
                        else
                        {
                                x >>= 1;
                                y >>= 1;
                                ++factor;
                        }
                }
        }
        return ( x << factor );
}


void main()  
{
 int t1,t2,i,p,q,choose=1; 
 int a[1000],b[1000];
 clock_t start, finish;
 double  totaltime; 
 printf("请输入你想要的数据数量:");
 scanf("%d",&q);
 
 //产生随机种子
 srand((unsigned)time(NULL));
//随机产生五十组数据
 for(i=0;i<=q;i++)
 {
	 a[i]=rand()%250;
	 b[i]=rand()%250;
 }
 printf("****************目录*****************\n");
 printf("1.辗转相除法嵌套\n");
 printf("2.辗转相除法递归\n");
 printf("3.穷举法\n");
 printf("4.更相减损法\n");
 printf("5.Stein算法\n");
 while(choose)
 {
 printf("请输入你想要的算法:");
 scanf("%d",&p);
 switch(p)
   {
		case 1:	   
//计算开始时间
           start = clock();
           for(i=0;i<=q;i++)
		   {
            t1=divisor(a[i],b[i]);                    
            t2=multiple(a[i],b[i]);
			printf("随机产生的两个数为:%d %d\n",a[i],b[i]);
//输出最大公约数和最小公倍数              
            printf("最大公约数是 %d\n",t1);  
            printf("最小公倍数是%d\n", t2);  
		   } 
//计算结束时间
           finish = clock(); 
//计算并输出总时间
           totaltime = (double)(finish - start) / CLOCKS_PER_SEC;		   
		   printf( "辗转相除法嵌套需要时间为%f seconds\n", totaltime );break;
	   
   case 2:
	   start = clock();
	   for(i=0;i<=q;i++)
	   {
	   t1=gcd(a[i],b[i]);
	   printf("最大公约数是 %d\n",t1);
       printf("最小公倍数是 %d\n",a[i]*b[i]/t1);
	   }
       finish = clock(); 
       totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	   printf( "辗转相除法递归需要时间为%f seconds\n", totaltime );break;
   case 3:
	   start = clock();
	   for(i=0;i<=q;i++)
	   {
	   t1=divisorr(a[i],b[i]);
	   printf("最大公约数是 %d\n",t1);
	   }
	   finish = clock(); 
       totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	   printf( "穷举法需要时间为%f seconds\n", totaltime );break;
   case 4:
	   start = clock();
	   for(i=0;i<=q;i++)
	   {
	   t1=gcdd(a[i],b[i]);
	   printf("最大公约数是 %d\n",t1);
	   }
	   finish = clock(); 
       totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	   printf( "更相减损法需要时间为%f seconds\n", totaltime );break;
   case 5: 
	   start = clock();
	   for(i=0;i<=q;i++)
	   {
	   t1=Stein(a[i],b[i]);
	   printf("最大公约数是 %d\n",t1);
	   }
	   finish = clock(); 
       totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
	   printf( "Stein算法需要时间为%f seconds\n", totaltime );break;
 }
 printf("\n继续查找选1,退出系统选0:");
 scanf("%d",&choose);
 if(choose==0)
	 break;
 }
}

四、运行结果
二十组数据:

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
五十组数据:

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
八十组数据:
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

五、调试
光标处有错误导致结果有错误
在这里插入图片描述
经过思考删去\n
在这里插入图片描述
六、测试
测试随机数是否生成,给定一个输出
在这里插入图片描述在这里插入图片描述
七、经验归纳
在这次实践中发现的问题:对C语言的语法基础有点模糊,例如用键盘为数值赋值时,scanf语句中不能有\n的存在,否则会变成需要输入两个数才能得出运行结果,而且出现这个错误,系统不会运行错误,所以以后要加强训练。在用switch选择语句时,经常忘记加break;,导致程序运行不出结果,以后要注意。还有对程序的排版和注释的写法,还不是那么的规范,要多多训练加强。
同样在这次实践中了解了clock()的用法,虽然不是很精通,但是有了进步。在这些算法中有很多的函数调用,是一次很好的锻炼过程。还有随机数的产生,开始的时候我没用数组,导致产生的数据不同,算法运行时间也就不同,就不能很好的进行比较,后来设定了两个数组实现了数据相同。总的来说,这次实践很有价值,让我对计算机编程有了新的理解。

猜你喜欢

转载自blog.csdn.net/TuringZNN/article/details/88325132