一、什么是排列,什么是组合?
排列
从 n 个不同元素中,任取 m(m≤n) 个元素,按照一定的顺序排成一列,叫做从 n 个不同元素中取出 m 个元素的一个排列。
组合
从 n 个不同元素中,任取 m(m≤n)个元素并成一组,叫做从 n 个不同元素中取出 m 个元素的一个组合。
通俗地讲
- A (排列):指把几个不但选出来,还要进行排列 如 A 4 2 A_4^2 A42 是指从 4 个物品中选出 2 个来,而且对它们的顺序是有要求的,顺序不一样,结果则不一样。 A 4 2 = 4 × 3 = 12 A_4^2 =4×3=12 A42=4×3=12
- C (组合):是指从 n 个数中选取几个出来,不排列,只组合。如 C 4 2 C_4^2 C42 是指从 4 个中选 2 个,不管它们的内部的顺序。 C 4 2 = C_4^2 = C42= 4 × 3 2 × 1 = 6 \cfrac{4×3}{2×1} = 6 2×14×3=6.
- 总结:排列与元素的顺序有关,组合与顺序无关。如 231 与 213 是两个排列;2+3+1 的和与 2+1+3 的和是一个组合。
![](https://i-blog.csdnimg.cn/blog_migrate/bc26b2b03fb658b0ef69f5bdf221b288.png)
由上可得,排列数和组合数的求法代码如下:
(1) 排列数
m 就是需要减 1 的次数。
int A(int n, int m) {
int res = 1;
for (int i = m; i >= 1; i--) {
res *= n; //n × n-1 × n-2 × ... n-m,m就是需要减1的次数
n--;
}
}
(2) 组合数
与求排列数一样,求组合数也是对公式的实现,不过组合这里有个小技巧可以对代码进行优化,那就是利用组合的互补率: C n m = C n n − m C_n^m = C_n^{ n - m} Cnm=Cnn−m 来减少循环执行次数。
版本一
由上可得 C n m = C_n^m= Cnm= A n m m ! \cfrac{A_n^m}{m!} m!Anm,而 m ! m! m! = A m m =A_m^m =Amm,故代码可表达为:
int C(int n, int m) {
m = Math.min(m, n-m);
int numerator = A(n, m); //分子
int denominator = A(m, m); //分母
return numerator / denominator;
}
版本二
根据 C n m = C_n^m= Cnm= A n m m ! \cfrac{A_n^m}{m!} m!Anm,但我们注意到, A n m A_n^m Anm 的代码实现中,m 一直遍历到 1,所以在方法 A() 的代码实现中我们简单的加入对 m ! m! m! 的求解即可,不需要为了求组合数还得多写一个求排列数的函数。
int A(int n, int m) {
int up = 1; //分子
int down = 1; //分母
m = Math.min(m, n-m);
for (int i = m; i >= 1; i--) {
up *= n; //累乘得到分子
n--;
down *= i; //累乘得到分母
}
return up / down;
}