10. 最大公约数&最小公倍数&加法原理&乘法原理&排列组合
10. 最大公约数&最小公倍数&加法原理&乘法原理&排列组合
约数&倍数
约数,又称因数或因子。
整数 a 除以整数 b(b≠0) 除得的商正好是整数而没有余数,我们就说 a 能被 b 整除,或 b 能整除 a,记作:b|a。
a 称为 b 的倍数,b 称为 a 的约数。
在自然数(0和正整数)的范围内,任何正整数都是 0 的约数。
注意:一个数的约数必然包括1及其本身。
问:24的约数有哪些?
1 2 3 4 6 8 12 24
在大学之前,"约数"一词所指的一般只限于正约数。
约数和倍数都是二元关系的概念,不能孤立地说某个整数是约数或倍数。
一个整数的约数是有限的。同时,它可以在特定情况下成为公约数。
如果一个数 c 既是数 a 的因数,又是数 b 的因数,那么 c 叫做 a 与 b 的公因数/公约数。
两个数的公约数中最大的一个,叫做这两个数的最大公约数,如:
12 的约数有:1 2 3 4 6 12
24 的约数有:1 2 3 4 6 8 12 24
则 12 与 24 的公约数有:1 2 3 4 6 12。
其中 12 是最大的,我们就称 12 为两者的最大公约数。
倍数:当 a/b=q...r(r=0)时,称 a 为 b 的倍数,记做: b|a。
公倍数:两个数的公共倍数,如:
12 的倍数有:12 24 36 48 ...
24 的倍数有:24 48 72 96 ...
显而易见,12 与24 的倍数都是无限的,且具有无限的公倍数:24 48 ...
其中 24 是最小的,我们就称 24 为两者的最小公倍数。
那么如果让你来分析 任意两个数的最大公约数与最小公倍数,有信心吗?
【题目描述】输入 a,b, 求他们的最大公约数与最小公倍数。
【输入样例】12 20
【输出样例】4 60
先自己思考吧!
相信你可以想到不止一种解法!
求最大公约数的三种方法:
- 最小递减法
先找 a,b 的最小值,判断该值能否同时被 a,b 整除,如果可以该数就是答案,否则每次-1,继续判断,直到找到答案。
int gcd(int a, int b){
for(int i=min(a,b); i>=1; i--){
if(a%i==0 && b%i==0) return i;
}
}
- 更相减损法
a-b=c,则 a,b 的最大公约数就是 b,c 的最大公约数,如果 c=0,a 就是答案。
int gcd(int a, int b){
if(a<b) swap(a, b);
if(b==0) return a;
return gcd2(b,a-b);
}
- 辗转相除法
a/b=q...r,则 a,b 的最大公约数就是 b,r 的最大公约数,如果 r=0,则 b 就是答案。
int gcd(int a, int b){
if(b==0) return a;
return gcd(b, a%b);
}
求最小公倍数的两种方法:
- 最大递增法
先找 a,b 的最大值,判断该值能否同时整除 a,b,如果可以该数就是答案,否则每次+1,继续判断,直到找到答案。
int lcm(int a, int b){
for(int i=max(a,b); i<=a*b; i++){
if(i%a==0 && i%b==0) return i;
}
}
- 定理法:两个数的乘积等于这两个数的最大公约数与最小公倍数的乘积。
int lcm(int a, int b){
return a*b/gcd(a,b);
}
#include<cstdio>
//最小递减法求最大公约数
int gcd1(int a, int b){
for(int i=min(a,b); i>=1; i--){
if(a%i==0 && b%i==0) return i;
}
return 1;
}
//更相减损法求最大公约数
int gcd2(int a, int b){
if(a<b) swap(a, b);
if(b==0) return a;
return gcd2(b,a-b);
}
//辗转相除法求最大公约数
int gcd3(int a, int b){
if(b==0) return a;
return gcd3(b, a%b);
}
//最大递增法求最小公倍数
int lcm1(int a, int b){
for(int i=max(a,b); ; i++){
if(i%a==0 && i%b==0) return i;
}
}
//定理法:两个数的乘积等于这两个数的最大公约数与最小公倍数的乘积
int lcm2(int a, int b){
return a*b/gcd3(a,b);
}
int main(){
int a,b; scanf("%d%d", &a, &b);
printf("gcd1(%d,%d) = %d\n", a, b, gcd1(a,b));
printf("gcd2(%d,%d) = %d\n", a, b, gcd2(a,b));
printf("gcd3(%d,%d) = %d\n", a, b, gcd3(a,b));
printf("lcm1(%d,%d) = %d\n", a, b, lcm1(a,b));
printf("lcm2(%d,%d) = %d\n", a, b, lcm2(a,b));
return 0;
}
输入:12 20
输出:
gcd1(12,20) = 4
gcd2(12,20) = 4
gcd3(12,20) = 4
lcm1(12,20) = 60
lcm2(12,20) = 60
问题描述:a/b + c/d = e/f (0<a,b,c,d<100).
输入格式:a b c d
输出格式:e/f 的最简式,如果 f=1, 则输出 e.
输入样例:1 2 3 4
输出样例:5/4
输入样例:2 2 4 2
输出样例:3
- 参考程序
#include<iostream>
using namespace std;
int gcd(int a,int b){
if(b==0) return a;
return gcd(b, a%b);
}
int main(){
int a,b,c,d; cin>>a>>b>>c>>d;
int e=a*d+b*c, f=b*d;
int temp = gcd(e, f);
e = e/temp, f = f/temp;
if(f!=1) cout<<e<<"/"<<f<<endl;
else cout<<e<<endl;
return 0;
}
加法原理&乘法原理
- 加法原理
做一件事,完成它有 N 类办法,在第一类办法中有 M1 种不同的方法,在第二类办法中有 M2 中不同的办法,……,在第 n 类办法中有 Mn 种不同的方法,那么完成这件事共有 M1+M2+..+Mn 种不同方法,每一种方法都能达成目标。
例如:Hacker 一家人外出旅游,可以乘火车,也可以乘汽车,还可以坐飞机。
经过网上查询,出发那一天中火车有4班,汽车有3班,飞机有2班
根据加法原理,任意选择其中一个班次的方法为4+3+2=9(种)。
- 乘法原理:
做一件事,完成它需要分成 n 个步骤,做第一步有 M1 中不同的方法,做第二步有 M2 中不同的方法,……,做第 n 步有 Mn 中不同的方法,那么完成这件事共有 M1*M2*…*Mn 种不同的方法。
例如:从甲地到乙地有 2 条路,从乙地到丙地有 3 条路,从丙路到丁地有 2 条路。
问:从甲地到丁地,共有多少种不同的走法?
根据乘法原理,共有2*3*2=12种走法。
排列组合
排列:是指按座位顺序排列,有先后顺序之分,比如:
现要 5 名同学坐在 3 个椅子上,每名同学各不相同,各个椅子不同,问有多少种落座方案?
可以想到:第 1 个椅子可以落座 5 人,第 2 个椅子可以落座 4 人,第 3 个椅子可以落座 3 人;
根据乘法原理可以计算落座方案数 N = 5*4*3 = 60。
那么这样的算法我们可以使用排列公式 \(A(n,m) = A_n^m = \frac{n!}{(n-m)!}\),表示从 n 个人中选择 m 个人进行排列,有先后顺序之分。
A(5,3) = 5!/(5-3)! = 5*4*3=60
组合:是指按座位数量任意落座,无先后顺序之分,比如:
现要 5 名同学坐在 3 个椅子上,每名同学各不相同,各个椅子相同,问有多少种落座方案?
可以想到:由于只有 3 个椅子,那么问题转化为 从 5 个同学中选择 3 个同学进行组合。
那么如果先任选 3 个同学,A(5, 3) = 60,但是这样会有重复情况,于是除去重复次数 3! 即可。
那么这样的算法我们可以使用组合公式 \(C(n,m) = C_n^m = \frac{n!}{m!*(n-m)!}\),表示从 n 个人中选择 m 个人进行组合,无先后顺序之分。
C(5,3) = A(5,3)/A(3,3) = 5!/2!/3! = 10
- 公式
A(n, m) = n!/(n-m)!
A(n, 1) = n
C(n, m) = A(n, m)/A(m, m) = n!/(n-m)!/m!
C(n, m) = C(n, n-m)
C(n, 0) = C(n, n) = 1
可以把从n个数中选m个数的组合分为 2 类
1. 不含第n个数的组合,方案数为:C(n-1, m)
2. 含第n个数的组合,方案数为:C(n-1, m-1)
所以C(n, m) = C(n-1, m)+C(n-1, m-1)
参考文章: