<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
# Combination - 组合
--------
#### 问题
从拥有$$ n $$个元素的集合$$ A = \{a_0, a_1, a_2, \dots ,a_{n-1} \} $$中任意取$$ m $$个元素($$ m \leq n $$,$$ m $$和$$ n $$都是自然数),求所有组合。
#### 解法
本文末尾列了很多关于组合算法的文献。本文介绍一种简单易记的算法。
设置数组\(s = [s_0,s_1,s_2, \dots ,s_{n-1}]\)表示对集合\(A\)的选择,第\(i\)个数字\(s_i = 1\)表示选择数字\(a_i\),\(s_i = 0\)表示不选择数字\(a_i\)。假设初始状态下从集合\(A = \{a_0,a_1,a_2, \dots ,a_{n-1} \}\)中取出\(0\)个元素组成组合,即\(s = [0_0,0_1,0_2, \dots ,0_{n-1}]\),得到\(1\)个组合\(\{\}\)。</p>
$$ (1) $$从集合$$ A = \{a_0,a_1,a_2, \dots ,a_{n-1} \} $$中取出$$ 1 $$个元素作为组合。则数组$$ s $$中只有一个元素为$$ 1 $$,其他全是$$ 0 $$,唯一的$$ 1 $$在数组$$ s $$中可以选择任意位置,得到$$ C_1^n = n $$个组合:
\[
\begin{array}{lcr}
[1_0,0_1,0_2, \dots ,0_{n-1}] && (1-1) \\
[0_0,1_1,0_2, \dots ,0_{n-1}] && (1-2) \\
[0_0,0_1,1_2, \dots ,0_{n-1}] && (1-3) \\
& \cdots & \\
[0_0,0_1,0_2, \dots ,1_{n-1}] && (1-n)
\end{array}
\]
<p id="i">\((2)\)从集合A中取出2个元素作为组合,可以看作是在第1轮操作数组s后得到的n个组合的基础上增加一个1。 </p>
<p id="i">对于第\(1\)轮中的数组\((1-1)\quad [1_0,0_1,0_2, \dots ,0_{n-1}]\)增加一个\(1\)后得到数组\([1_0,1_1,0_2, \dots ,0_{n-1}]\)。原本的\(1_0\)保持不变,新增的\(1_1\)可以选择后面等于\(0\)的\(n-1\)个位置,生成\(n-1\)个组合: </p>
\[
\begin{array}{lcr}
[1_0,1_1,0_2,0_3, \dots ,0_{n-1}] && (2-1-1) \\
[1_0,0_1,1_2,0_3, \dots ,0_{n-1}] && (2-1-2) \\
[1_0,0_1,0_2,1_3, \dots ,0_{n-1}] && (2-1-3) \\
& \cdots & \\
[1_0,0_1,0_2,0_3, \dots ,1_{n-1}] && (2-1-(n-1))
\end{array}
\]
<p id="i">需要注意的是,新增的\(1\)必须在原数组中所有的\(1\)的后面。对于数组\((1-2) \quad [0_0,1_1,0_2, \dots ,0_{n-1}]\),新增的\(1\)只能选择后面等于\(0\)的\(n-1\)个位置,生成\(n-2\)个组合: </p>
\[
\begin{array}{lcr}
[0_0,1_1,1_2,0_3, \dots ,0_{n-1}] && (2-2-1) \\
[0_0,1_1,0_2,1_3, \dots ,0_{n-1}] && (2-2-2) \\
& \cdots & \\
[0_0,1_1,0_2,0_3, \dots ,1_{n-1}] && (2-2-(n-2))
\end{array}
\]
<p id="i">如果不注意,让新增的\(1\)在原数组的任意的\(1\)的前面,则会产生重复的组合,仍然以数组\((1-2) \quad [0_0,1_1,0_2, \dots ,0_{n-1}]\)为例,如果新增的\(1\)可以选择任意等于\(0\)的位置,会生成\(n-1\)个组合: </p>
\[
\begin{array}{lcr}
[1_0,1_1,0_2,0_3, \dots ,0_{n-1}] && (2-2-0) \\
[0_0,1_1,1_2,0_3, \dots ,0_{n-1}] && (2-2-1) \\
[0_0,1_1,0_2,1_3, \dots ,0_{n-1}] && (2-2-2) \\
& \cdots & \\
[0_0,1_1,0_2,0_3, \dots ,1_{n-1}] && (2-2-(n-2))
\end{array}
\]
<p id="i">但其中\((2-2-0) \quad [1_0,1_1,0_2,0_3, \dots ,0_{n-1}]\)与第\(1\)个数组产生的组合重复了。对第\(1\)轮中所有的数组重复该操作,即可得到选取\(2\)个元素的所有组合,共有\(C_2^n = \frac{n \times (n-1)}{2} \)个。 </p>
<p id="i">\((3)\)从集合\(A\)中取出\(3\)个元素作为组合,可以看作是在第\((2)\)轮操作的\((n-1)!\)个组合基础上增加一个\(1\),对于之前的每个组合,保持之前两个\(1\)不变,新的\(1\)可以选择原数组中最后一个\(1\)之后的任意等于\(0\)的位置。注意新增的\(1\)不能比原数组中的任意的\(1\)更靠前,必须在所有的\(1\)之后的位置进行选择。 </p>
<p id="i">重复上述的操作,直到选取\(m\)个元素,即可得到所有的组合,算法结束。然后根据\(s\)的全排列生成集合\(A\)的所有组合即可。该算法时间复杂度为\(C_m^n = \frac{n!}{m! \cdot (n-m)!} \)。 </p>
</div>
<br>
StackOverflow上关于组合产生算法的问题:
* [http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n](http://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n)
二项式系数:
* [https://en.wikipedia.org/wiki/Binomial_coefficient](https://en.wikipedia.org/wiki/Binomial_coefficient)
Chase’s Twiddle - Algorithm 382: Combinations of M out of N Objects:
* [http://dl.acm.org/citation.cfm?id=362502](http://dl.acm.org/citation.cfm?id=362502)
* [http://www.netlib.no/netlib/toms/382](http://www.netlib.no/netlib/toms/382)
Buckles - Algorithm 515: Generation of a Vector from the Lexicographical Index:
* [http://dl.acm.org/citation.cfm?id=355739](http://dl.acm.org/citation.cfm?id=355739)
* [https://www.researchgate.net/profile/Bill_Buckles/publication/220492658_Algorithm_515_Generation_of_a_Vector_from_the_Lexicographical_Index_G6/links/5716d7ad08ae497c1a5706ec.pdf](https://www.researchgate.net/profile/Bill_Buckles/publication/220492658_Algorithm_515_Generation_of_a_Vector_from_the_Lexicographical_Index_G6/links/5716d7ad08ae497c1a5706ec.pdf)
Remark on algorithm 515: Generation of a vector from the lexicographical index combinations:
* [http://dl.acm.org/citation.cfm?id=1236470](http://dl.acm.org/citation.cfm?id=1236470)
--------
#### 源码
[import, lang:"c_cpp"](../../../src/CombinatorialMathematics/Combination.hpp)
#### 测试
[import, lang:"c_cpp"](../../../src/CombinatorialMathematics/Combination.cpp)
- Content 目录
- Preface 前言
- Chapter-1 Sort 第1章 排序
- InsertSort 插入排序
- BubbleSort 冒泡排序
- QuickSort 快速排序
- MergeSort 归并排序
- Chapter-2 Search 第2章 搜索
- BinarySearch 二分查找法(折半查找法)
- BruteForce 暴力枚举
- Recursion 递归
- BreadthFirstSearch 广度优先搜索
- BidirectionalBreadthSearch 双向广度搜索
- AStarSearch A*搜索
- DancingLink 舞蹈链
- Chapter-3 DataStructure 第3章 数据结构
- DisjointSet 并查集
- PrefixTree(TrieTree) 前缀树
- LeftistTree(LeftistHeap) 左偏树(左偏堆)
- SegmentTree 线段树
- FenwickTree(BinaryIndexedTree) 树状数组
- BinarySearchTree 二叉查找树
- AVLTree AVL平衡树
- RedBlackTree 红黑树
- Chapter-4 DynamicProgramming 第4章 动态规划
- Chapter-5 GraphTheory 第5章 图论
- Chapter-6 Calculation 第6章 计算
- LargeNumber 大数字
- Exponentiation 求幂运算
- Chapter-7 CombinatorialMathematics 第7章 组合数学
- FullPermutation 全排列
- UniqueFullPermutation 唯一的全排列
- Combination 组合
- DuplicableCombination (元素)可重复的组合
- Subset 子集
- UniqueSubset 唯一的子集
- Permutation 排列
- PermutationGroup 置换群
- Catalan 卡特兰数
- Chapter-8 NumberTheory 第8章 数论
- Sieve 筛选算法
- Euclid 欧几里得
- EuclidExtension 欧几里得扩展
- ModularLinearEquation 模线性方程
- ChineseRemainerTheorem 中国剩余定理
- ModularExponentiation 模幂运算
- Chapter-9 LinearAlgebra 第9章 线性代数
- Chapter-10 AnalyticGeometry 第10章 解析几何
- Chapter-11 TextMatch 第11章 文本匹配
- SimpleMatch 简单匹配
- AhoCorasickAutomata AC自动机
- KnuthMorrisPratt KMP匹配算法
- RabinKarp RabinKarp算法
- BoyerMoore BoyerMoore算法
- Chapter-12 GameTheory 第12章 博弈论
- BashGame 巴什博弈
- WythoffGame 威佐夫博弈
- NimGame 尼姆博弈