【问题描述】
基本要求: 求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,再加上数学推导出的判定条件,问题就可以解决了。但是虽然是这么说,自己写代码起来还是有点懵,感谢同学们给我的帮助,不然这作业就差点完不成了,希望自己以后也能是这么厉害吧~从这次问题中我还知道了一些数学重要常识的重要性,这有时候对解决问题能提供一个直指问题关键的思路,以后要加强数学思维的锻炼,当然代码部分也要好好加强。