求N个数最大公约数和最小公倍数&&Hankson

【问题描述】
基本要求: 求N个数的最大公约数和最小公倍数。用C或C++或java或python语言实现程序解决问题。
提高要求:
Hanks博士是BT(Bio-Tech,生物技术)领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。
今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1,设某未知正整数x满足:
1、 x和a0的最大公约数是a1;
2、 x和b0的最小公倍数是b1。
Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。
输入格式
输入第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。
输出格式
输出共n行。每组输入数据的输出结果占一行,为一个整数。
对于每组数据:若不存在这样的x,请输出0;
若存在这样的x,请输出满足条件的x的个数;
样例输入
2
41 1 96 288
95 1 37 1776
样例输出
6
2
【基本要求算法设计思路】
这次的题目要求是求多个数的最大公约数,我们可以知道的是四个计算两个数的最大公约数的算法,因此我就想着运用循环,将输入的数字的前两个相比,求出最大公因数,再用这个最大公因数去和其它的输入的数字一一计算,得出结果。
【程序代码】

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<math.h>
//辗转相除法 
int divisor1 (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 multiple1 (int a,int b)  /*自定义函数求两数的最小公倍数*/
{
	int divisor1 (int a,int b); /*自定义函数返回值类型*/
    int temp;
    temp=divisor1(a,b);  /*再次调用自定义函数,求出最大公约数*/
    return  (a*b/temp); /*返回最小公倍数到主调函数处进行输出*/
}
//穷举法 
int divisor2 (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 multiple2 (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);
}
//更相减损法 
int gcd(int m,int n)
{
	int i=0,temp,x;
	if(m==n)
        return m;
	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;//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);
}
int main()
{
	int m=1; //m为选择标识
	int a[10000]; //定义足够大的空间
	while(m==1)
	{
		int x,y,p;
		int j;
		int maxDiv1,maxDiv2,maxDiv3,maxDiv4;
		int n; //输入个数标识
		printf("请选择,求最大公因数还是最小公倍数:1.最大公因数 2.最小公倍数:");
		scanf("%d",&p);
		if(p==1) //求最大公因数
		{
			printf("输入的整数的个数:");
			scanf("%d",&n);
			printf("输入数字:");
			for(j=0;j<n;j++) //输入
			{
				scanf("%d",&a[j]);
			}
			//前两个数进行计算求最大公约数 
			maxDiv1 = divisor1(a[0],a[1]);
			maxDiv2 = divisor2(a[0],a[1]); 
			maxDiv3 = gcd(a[0],a[1]); 
			maxDiv4 = Stein(a[0],a[1]);
			printf("\n");
			for(j=2;j<n;j++) //前两个数的最大公因数与后面的数进行比较
			{
				maxDiv1 = divisor1(maxDiv1,a[j]); 
				maxDiv2 = divisor2(maxDiv2,a[j]); 
				maxDiv3 = gcd(maxDiv3,a[j]); 
				maxDiv4 = Stein(maxDiv4,a[j]);
			} 
			printf("\n");
			printf("最大公因数是(辗转相除法):%d\n",maxDiv1);
			printf("最大公因数是(穷举法):%d\n",maxDiv2);
			printf("最大公因数是(更相减损法):%d\n",maxDiv3);
			printf("最大公因数是(Stein算法):%d\n",maxDiv4);
			printf("\n");
		printf("请选择:1.继续计算 2.结束:",m); //选择结束或继续
		scanf("%d",&m);
		printf("\n");
		}
		else//求最小公倍数 
		{
			int s;
			int minMul1,minMul2; //最小公倍数
			int a[10000]; //定义足够大的空间
	 		int n; //输入个数标识
	 		printf("输入的整数的个数:");
	 		scanf("%d",&n);
	 		printf("输入数字:");
			for(j=0;j<n;j++) //输入
			{
				scanf("%d",&a[j]);
			}
			minMul1 = multiple1(a[0],a[1]);//开始计算前两个数的最小公倍数 
			minMul2 = multiple2(a[0],a[1]); 
			printf("\n");
			for(j=2;j<n;j++) //将两个数所得最小公倍数与后两个进行比较
			{
				minMul1 = multiple1(minMul1,a[j]); 
				minMul2 = multiple2(minMul2,a[j]); 
			}
			printf("最大公因数是(辗转相除法):%d\n",minMul1);
			printf("最大公因数是(穷举法):%d\n",minMul2);
			printf("请选择:1.继续计算 2.结束:",m); //选择结束或继续
		    scanf("%d",&m);
		}
	}
	system("pause");
	return 0;
}

【调试截图】
(1)N个数的最大公约数
在这里插入图片描述
(2)N个数的最小公倍数
在这里插入图片描述
【测试截图】
在这里插入图片描述
【运行结果】
在这里插入图片描述
【提高要求算法设计思路】
根据题给条件,我们可以得出lcm(x,a0)=a1,lcm(x0,b0)=b1这两个等式,
再经过一系列是数学公式推导,我们可以得到gcd(a0,x)=a1,gcd(b1/b0,ba/x)=1的等式,我们可以靠这个等式去得出x的取值。
【程序代码】

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int gcd(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 main()
{	int n,a0,a1,b0,b1,x,ans;
	printf("请输入要比对的组数:");       //输入要对比的组数 
	scanf("%d",&n);
	while (n<=0)                       //判断组数的输入是否正确 
	{
		printf("您输入的数字不正确,请重新输入:");
		scanf("%d",&n);
	}
	while (n>0)
	{
		ans=0;
		printf("请按照a0,a1,b0,b1的顺序输入数字(要求a0能被a1整除,b1能被b0整除):") ; 
		scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
		while(a0%a1!=0||b1%b0!=0)        //判断输入数据的准确性 
		{
			printf("输入格式错误,请重新输入:");
			scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
		}		
		for (int i=1;i<=b1;++i)    //计算符合条件的x共有多少个 
		if (b1%i==0)
		{
		 	x=i;
		 	if (x%a1==0&&gcd(a0,x)==a1&&gcd(b1/b0,b1/x)==1)   //计算所得出来的判断条件 
		 	{
					ans++;                                        //计数 
		 			x=b1/i;
		    }
		}
		printf("满足条件的x有%d个\n",ans);
		n--;
	}
	return 0;
	system("pause");
}

【调试截图】
在这里插入图片描述
【运行结果】
在这里插入图片描述
【经验归纳】
这次的作业,无论是基本要求还是提高要求,都很锻炼我的数学能力还有思考问题的能力,基本要求使我更新了对for循环的使用:先把N个数中的两个先求出最大公因数,再用for循环把后面的数与求出的最大公因数计算(最大公因数是不断更新着的),而提高要求这个题我在刚看时就没咋看懂。。看了好几遍,思考了很久,觉得这次问题的解决步骤在于如何判断满足条件的值,而其中最为关键的便是求出x的取值范围,由数学上的定理很容易知道如若x和b0的最小公倍数是b1,那么x的取值就并不大于b1,再加上数学推导出的判定条件,问题就可以解决了。但是虽然是这么说,自己写代码起来还是有点懵,感谢同学们给我的帮助,不然这作业就差点完不成了,希望自己以后也能是这么厉害吧~从这次问题中我还知道了一些数学重要常识的重要性,这有时候对解决问题能提供一个直指问题关键的思路,以后要加强数学思维的锻炼,当然代码部分也要好好加强。

猜你喜欢

转载自blog.csdn.net/qq_44246262/article/details/88762955