简介
在前面的文章里我们讨论了排列,全排列和可重复排列的几种实现。这里我们接着讨论组合的几种情况以及对应的实现。这些问题衍生出来的其他问题非常多,不过都有一定的套路可以遵循。这里针对这些情况一一讨论。
问题分析
具体组合的情况也有不同种。比如说我们最常见的,针对不重复的n个元素的集合a,从其中取出k个元素来(k <= n), 所有取出这些元素和位置无关,所以它们构成一个组合C(n, k)。还有一些是针对可以重复的集合取出k个数的情况。另外,针对纯组合问题,我们可能会考虑一些针对集合元素里所有可能的组合,包括的元素个数从1个到n个。还有一些若干个元素之和等于某些元素的问题,也可以从这里找到相关的思路。
不可重复取组合
针对这种情况,我们先来看一个示例,假设我们有数据集合{1, 2, 3},那么如果从里面取两个元素的组合则有(12), (13), (23)。其中每个元素只能在被选择的集合里出现一次。这里是假设我们原有的数据集合里也没有重复的元素。
现在,我们来考虑一种实现组合的思路。以前面的数据集合{1, 2, 3}为例。首先,我们在目的数据集里取第一个元素1, 那么后面一个位置可以取的元素就是从{2, 3}里挑了。原来选定的1不能取。在前面的一个元素选择了之后,我们后面要选择或者能选择的范围应该被前面的给限定了。所以,如果我们用递归的函数来描述的话,必然有一个类似与槛一样的参数,它指定后面的元素只能从某个范围开始来取。以数列{1, 2, 3, 4, 5}为例来看。首先第一个位置取1, 然后第二个取2, 于是得到(12)。如果有第三个项的话,需要取的是3。后面可以取的是(124),(125)。所以,从这里可以看到,每个位置所取的元素是从前面指定位置后的所有可以取的位置。
我们再考虑一下递归结束的条件。当递归的层次到给定取的元素个数时,才能输出给定的组合,并退出。
于是,按照前面的这个思路,我们可以得到一个大致的伪码实现:
void combine(源数据集合a, 目的数据集合b, 当前迭代起始点begin, 当前目标数据集合位置cur, int n) { if(cur == n) //输出数组b for(集合a中间从begin到end的元素i) { b[cur] = a[i]; combine(a, b, i + 1, cur + 1, n); } }
从这部分伪码来进一步细化一下代码实现。这里传递的begin其实就是从源数组里开始搜索的位置。于是,详细的实现如下:
public static void newCombine(int[] a, int[] b, int begin, int cur, int n) { if(cur == n) { printList(b); return; } for(int i = begin; i < a.length; i++) { b[cur] = a[i]; newCombine(a, b, i + 1, cur + 1, n); } }
我们需要注意的就是在实现里,if(cur == n)里设定了return语句。因为当这部分输出数组的工作结束就该返回了。我们也可以通过将后面的for循环包含在else语句中来省略掉return语句。调用这个函数的示例代码如下:
public static void main(String[] args) { int[] a = {1, 2, 3, 4}; int n = 2; int[] b = new int[n]; newCombine(a, b, 0, 0, n); }
可重复取组合
假设源数组里有数字{1, 2, 3, 4},要求取两个数字的组合。那么可重复取的组合可以有(11), (12), (22)等。和前面取组合元素里一个最大的差异就是,前面的取法里要求后面能取的元素只能是前面给定的开始范围。而因为这里可以最小取一个和前面给定元素相同的。比如说当前元素所在的索引为i,那么后面需要取的元素只要是大于或者等于b[i - 1]的。这样,这种取法的一个特点就是不需要根据当前取到的元素去给后面的元素指定开始取的位置了。
在原来的基础上,我们修改后的代码实现如下:
public static void repCombine(int[] a, int[] b, int cur, int n) { if(cur == n) { printList(b); return; } for(int i = 0; i < a.length; i++) { if(cur == 0 || a[i] >= b[cur - 1]) { b[cur] = a[i]; repCombine(a, b, cur + 1, n); } } }
这里的要点也在于这个if(cur == 0 || a[i] > b[cur - 1])这个判断。这里根据是否为开始的索引0或者判断取到的元素是否比前面已经取的元素大来确定下一个元素。相对来说,调用方法参数更少一些。调用的方法代码如下:
public static void main(String[] args) { int[] a = {1, 2, 3, 4}; int n = 2; int[] b = new int[2]; repCombine(a, b, 0, n); }
总结
这里结合不能重复取和可以重复取的两种情形进行了分析。重点在于组合的选取里,我们首先保证源数据是有序的,这样每次取的元素可以根据顺序来判断获取。而后面取的元素基于一个递归迭代的关系,要么根据当前的位置来指定下一次搜索源数据的位置,要么按照一定的条件从源数据和当前目标数据列里进行判断筛选。思路比较有意思,值得好好体会。我们以前讨论过的部分排列其实用到了组合和排列技术的结合。它无非就是首先取得一个组合,然后再对组合的元素进行全排列。另外,本文前面提到的一些其他的应用也会在后续的文章里讨论。
参考材料
http://blog.csdn.net/zmazon/article/details/8315418
相关推荐
排列组合问题的算法设计是指如何高效地生成所有可能的排列或组合。今天,我们将讨论基于C语言的排列组合算法实现。 一、全排列算法 全排列算法是指生成所有可能的排列的算法。常见的全排列算法有递归算法、分治...
本资源主要讨论排列组合问题,特别是具有重复元素的排列组合问题,并使用回溯算法来解决。下面是对该资源的详细解释和知识点总结。 一、排列组合的定义 在数学和计算科学中,排列组合是指从一个集合中选择一些元素...
在编程领域,排列组合算法是解决许多问题的关键,特别是在数据处理、数据分析以及各种优化问题中。PHP作为一种流行的服务器端脚本语言,虽然不是为高性能计算而设计,但其丰富的库和简洁的语法使得实现这些算法变得...
排列组合计算器是一款专门针对这些问题设计的软件工具,如标题所示,这里我们讨论的是“排列组合计算器 v2.0”。 首先,我们需要理解排列和组合的基本定义。在给定的一组元素中: 1. **排列(Permutation)**:是...
通过递归方法,我们可以轻松地生成给定集合的所有排列组合。这种技术在解决许多实际问题时非常有用,例如在搜索最优解、模拟随机事件等方面。理解排列组合的基本原理及其实现方法对于学习高级算法和数据结构非常重要...
通过学习和理解这种VBA组合生成算法,你可以提升Excel的自动化能力,解决实际问题,比如在市场调查中分析潜在客户群组,或者在项目管理中研究不同资源分配方案的效果。 总结来说,这个VBA项目提供了一个实用工具,...
总的来说,排列生成算法是组合优化和算法设计中的基础工具,对理解和掌握计算机科学中的问题求解技巧至关重要。通过深入学习和实践这两种方法,不仅可以提升编程能力,还能培养解决问题的逻辑思维。
例如,优化算法的选择、测试用例的生成、网络路由的规划等都涉及到排列组合的计算。学习排列组合有助于开发者设计出更高效、更全面的解决方案,避免因考虑不周导致的问题。通过这种寓教于乐的方式,学生不仅能掌握...
此外,对于树的组合生成,Knuth可能会介绍组合数学的概念,如排列、组合和递推关系,这些都是生成所有可能树的关键。读者还将学习如何利用这些数学工具来优化算法,减少计算复杂性。 最后,书中可能包含大量实例和...
在编程领域,排列组合是解决问题时经常会遇到的一种数学...同时,这个问题的讨论也关联到了八皇后问题,展示了排列组合在解决实际问题中的应用。理解和掌握这些知识,对于提升编程能力和解决复杂问题有着极大的帮助。
在EXCEL中,排列组合是一个常见的问题,今天我们来讨论如何使用EXCEL来生成排列组合。排列组合是指从一个集合中选取若干元素,并将他们排列成不同的顺序。例如,从数字1到4中选取8个数字,并将他们排列成不同的顺序...
下面将详细讨论排列组合的基本概念,以及在JAVA中实现这些算法的关键点。 排列是指从n个不同元素中取出m(m≤n)个元素,按照一定的顺序排成一列,这样的方法数目称为排列数,用P(n,m)表示。组合则是指不考虑顺序,...
本话题关注的是“33选6”和“36选7”的全组合速度试验,这是一个涉及排列组合计算和性能测试的议题。我们将深入探讨这些概念,并结合RtlMoveMemory函数在实际编程中的应用。 首先,让我们解释一下“33选6”和“36选...
第7章可能涉及生成函数,这是一种将组合问题转化为代数问题的技巧。通过解代数方程,可以找到组合数的递推关系。习题可能要求构造并求解特定类型的生成函数。 第8章可能涵盖了概率和统计的基本概念,这些在组合数学...
《计算机程序设计与艺术》是计算机科学领域的一部经典之作,由Donald E....通过阅读这本书,你可以了解到如何有效地生成和处理元组和排列,这对解决各种实际问题,如组合搜索、数据建模和优化,都将大有裨益。
9. **图论与组合优化**:结合图论的概念,如树、图的生成函数、染色问题,以及旅行商问题等组合优化问题,展示了组合数学在实际问题中的应用。 提供的习题解答PDF文件可以帮助读者巩固理论知识,通过解决具体问题...
本问题的核心是生成一个集合的所有可能排列,即使该集合中的某些元素可能完全相同。以下是这个问题的详细解析。 首先,我们需要理解“排列”这一概念。排列是指从一个有限的、非空的元素集合中,取出一部分或全部...
总的来说,这个项目结合了MATLAB的图像处理能力、随机数生成、排列组合计算以及脚本编程,是一个很好的学习和实践MATLAB技术的例子。通过研究这些脚本,我们可以深入了解如何在MATLAB中实现这些功能。
组合数学是离散数学的一个重要分支,主要研究有限集合中元素的不同排列组合方式以及与之相关的性质和计数问题。这个学科在计算机科学、信息论、编码理论、统计学、博弈论等多个领域都有广泛的应用。这份名为“组合...
组合数学是离散数学的一个重要分支,主要研究的是在有限集合中的组合问题,涉及排列、组合、二项式定理、鸽巢原理、容斥原理等核心概念。在这个"组合数学第五版2-7章课后答案"中,我们可以深入探讨这些章节涵盖的...