`

Java组合算法(m个n选1)

 
阅读更多

摘自:http://blog.csdn.net/xht555/article/details/43278807

一、模型:

①    现有8个小球,对小球进行编号,依次为a、b、c、……、g、h。

②    将编号后的8个小球分成三组,分组情况如下:

  ■    第一组:[a, b, c]

  ■    第二组:[d, e]

  ■    第三组:[f, g, h]

③    从每组中选出一个小球,对选出的三个小球进行组合

问题:问一个有多少种不重复的组合方式,并列出详细的组合方式。

以上是一个典型的数学组合问题,因为是从每组中选出一个小球,所以每组的选法就有组元素个数种选法,所以组合种数应为18=3×2×3。具体的组合如下:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. 01: a d f  
  2. 02: a d g  
  3. 03: a d h  
  4. 04: a e f  
  5. 05: a e g  
  6. 06: a e h  
  7. 07: b d f  
  8. 08: b d g  
  9. 09: b d h  
  10. 10: b e f  
  11. 11: b e g  
  12. 12: b e h  
  13. 13: c d f  
  14. 14: c d g  
  15. 15: c d h  
  16. 16: c e f  
  17. 17: c e g  
  18. 18: c e h  

 

上面是纯数学、纯人工组合出来的,效率太低下了。如果使用Java语言进行编程,打印出这18组组合结果,又该如何实现呢?

二、循环迭代式的组合

可能很多程序员立马会想到,这个简单,不就三个数字(或List)吗,三个嵌套循环不就出来了!那么就来看看具体的实现。

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. @Test  
  2. public void testCompositeUseIteration() {  
  3.     List<String> listA = new ArrayList<String>();  
  4.     listA.add("a");  
  5.     listA.add("b");  
  6.     listA.add("c");  
  7.           
  8.     List<String> listB = new ArrayList<String>();  
  9.     listB.add("d");  
  10.     listB.add("e");  
  11.           
  12.     List<String> listC = new ArrayList<String>();  
  13.     listC.add("f");  
  14.     listC.add("g");  
  15.     listC.add("h");  
  16.           
  17.     int index = 0;  
  18.     for (String itemA : listA) {  
  19.         for (String itemB : listB) {  
  20.             for (String itemC : listC) {  
  21.                 index++;  
  22.                 String str = index + ": \t" + itemA + " " + itemB + " " + itemC;  
  23.                 System.out.println(str);  
  24.             }  
  25.         }  
  26.     }  
  27. }  

 

上面这段代码可以正确的打印出18种不重复的组合方式。

这种方法解决简单的m个n选1是没有任何问题的,但在实际应用中,m值并不是一直是3(m值即嵌套for循环的个数),有可能会更大,甚至m值会经常变化,比如m=10或m=20,难道就要写10个或20个for嵌套循环吗?显然,for嵌套循环方法肯定不能满足实现应用的需求,更为致命的是,当m值发生变化时,必须要修改代码,然后重新编译、发布,针对已经上线的生产系统,这也是不允许的。

三、可变组数的高级迭代组合

再来分析下前面的18组组合结果,其实是有规律可循的。

首先是要算出总的组合种数,这个很容易;然后按照从左到右、不重复的组合原则,就会得到一个元素迭代更换频率,这个数很重要,从左至右,每组的迭代更换频率是不一样的,但同组里的每个元素的迭代更换频率是一样的。

说实话,用文字来描述这个规律还真是有些困难,我在纸上画了画,就看图来领会吧!

找到了规律,那么写代码就不是问题了,具体实现如下(有兴趣的朋友可以将关键代码封装成方法,传入一个List<List<E>>的参数即可返回组合结果):

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. /** 
  2. * 组合记号辅助类 
  3. * @author xht555 
  4.  * @Create 2015-1-29 17:14:12 
  5.  */  
  6. private class Sign {  
  7.     /** 
  8.      * 每组元素更换频率,即迭代多少次换下一个元素 */  
  9.     public int whenChg;  
  10.     /** 
  11.      * 每组元素的元素索引位置 */  
  12.     public int index;  
  13. }  
  14.   
  15. @Test  
  16. public void testComposite(){  
  17.     List<String> listA = new ArrayList<String>();  
  18.     listA.add("a");  
  19.     listA.add("b");  
  20.     listA.add("c");  
  21.       
  22.     List<String> listB = new ArrayList<String>();  
  23.     listB.add("d");  
  24.     listB.add("e");  
  25.       
  26.     List<String> listC = new ArrayList<String>();  
  27.     listC.add("f");  
  28.     listC.add("g");  
  29.     listC.add("h");  
  30.       
  31.     // 这个list可以任意扩展多个  
  32.     List<List<String>> list = new ArrayList<List<String>>();  
  33.     list.add(listA);    // 3  
  34.     list.add(listB);    // 2  
  35.     list.add(listC);    // 3  
  36.     //list.add(listD);  
  37.     //list.add(listE);  
  38.     //list.add(listF);  
  39.       
  40.     int iterateSize = 1;// 总迭代次数,即组合总种数  
  41.     for (int i = 0; i < list.size(); i++) {  
  42.         // 每个List的n选1选法种数  
  43.         // 有兴趣的话可以扩展n选2,n选3,... n选x  
  44.         iterateSize *= list.get(i).size();  
  45.     }  
  46.       
  47.     int median = 1// 当前元素与左边已定元素的组合种数  
  48.     Map<Integer, Sign> indexMap = new HashMap<Integer, Sign>();  
  49.     for (int i = 0; i < list.size(); i++) {  
  50.         median *= list.get(i).size();  
  51.         Sign sign = new Sign();  
  52.         sign.index = 0;  
  53.         sign.whenChg = iterateSize/median;  
  54.         indexMap.put(i, sign);  
  55.     }  
  56.       
  57.     System.out.println("条目总数: " + iterateSize);  
  58.     Set<String> sets = new HashSet<String>();  
  59.       
  60.     int i = 1;  // 组合编号  
  61.           
  62.     long t1 = System.currentTimeMillis();  
  63.     while (i <= iterateSize) {  
  64.         String s = "i: " + i + "\t";  
  65.           
  66.         // m值可变  
  67.         for (int m = 0; m < list.size(); m++) {  
  68.             int whenChg = indexMap.get(m).whenChg;  // 组元素更换频率  
  69.             int index = indexMap.get(m).index;      // 组元素索引位置  
  70.   
  71.             s += list.get(m).get(index) + "[" + m + "," + index + "]" + " ";  
  72.               
  73.             if (i%whenChg == 0) {  
  74.                 index++;  
  75.                 // 该组中的元素组合完了,按照元素索引顺序重新取出再组合  
  76.                 if (index >= list.get(m).size()) {  
  77.                     index = 0;  
  78.                 }  
  79.                       
  80.                 indexMap.get(m).index = index;  
  81.             }  
  82.         }  
  83.               
  84.         System.out.println(s);  
  85.         sets.add(s);  
  86.         i++;  
  87.     }  
  88.       
  89.     System.out.println("Set条目总数: " + sets.size());  
  90.     long t2 = System.currentTimeMillis();  
  91.     System.err.println(String.format("%s ms", t2 - t1));  
  92. }  

 

运行结果如下:

 

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. 条目总数: 18  
  2. i: 1    a[0,0] d[1,0] f[2,0]   
  3. i: 2    a[0,0] d[1,0] g[2,1]   
  4. i: 3    a[0,0] d[1,0] h[2,2]   
  5. i: 4    a[0,0] e[1,1] f[2,0]   
  6. i: 5    a[0,0] e[1,1] g[2,1]   
  7. i: 6    a[0,0] e[1,1] h[2,2]   
  8. i: 7    b[0,1] d[1,0] f[2,0]   
  9. i: 8    b[0,1] d[1,0] g[2,1]   
  10. i: 9    b[0,1] d[1,0] h[2,2]   
  11. i: 10   b[0,1] e[1,1] f[2,0]   
  12. i: 11   b[0,1] e[1,1] g[2,1]   
  13. i: 12   b[0,1] e[1,1] h[2,2]   
  14. i: 13   c[0,2] d[1,0] f[2,0]   
  15. i: 14   c[0,2] d[1,0] g[2,1]   
  16. i: 15   c[0,2] d[1,0] h[2,2]   
  17. i: 16   c[0,2] e[1,1] f[2,0]   
  18. i: 17   c[0,2] e[1,1] g[2,1]   
  19. i: 18   c[0,2] e[1,1] h[2,2]   
  20. Set条目总数: 18  
  21. 3 ms  

 

四、兴趣扩展

有兴趣的朋友可以做下述尝试:

① m个n选x的组合实现;

② m个n选1的排列实现(先组后排);

排列会关注元素所在的位置(顺序),例如,三个元素“a d f”的排列大概如下:

■    a d f

■    a f d

■    d a f

■    d f a

■    f a d

■    f d a

分享到:
评论

相关推荐

    java排列组合算法

    在Java中实现排列组合算法可以帮助我们解决很多实际问题,比如数据排序、数据筛选等。下面将详细介绍排列和组合的基本概念以及在Java中的实现方法。 **排列** 是指从n个不同元素中取出m(m≤n)个元素,按照一定的...

    java m取n 重复 不重复 排列组合 for循环嵌套递归

    根据给定文件的信息,我们可以总结出以下关于Java中m取n排列组合的实现方式,包括重复与不重复的情况,以及如何使用for循环嵌套和递归来实现这些算法。 ### Java中m取n排列组合实现 #### 一、背景介绍 在计算机...

    Java排列组合算法分析和代码实现

    总之,这个资源包提供了一个很好的平台,让你能够深入理解并实践Java中的排列组合算法。通过学习和理解这些代码,你不仅可以增强算法设计能力,还能提高解决实际编程问题的能力。记得动手实践,结合文档和代码,将...

    Java排列组合_组合算法

    排列是指从n个不同元素中取出m(m≤n)个元素,按照一定的顺序排成一列,而组合则是指从n个不同元素中不考虑顺序取出m个元素。排列与组合的区别在于是否考虑选取元素的顺序。 在Java中实现组合算法,通常会用到递归...

    输出一个C(m,n)组合的所有结果 - VC-MFC - 图形处理-算法

    在计算机科学中,C(m, n)通常代表组合的计算,它是从m个不同元素中不重复地选择n个元素的数目。这个问题涉及到数学的组合论和编程的递归或循环算法实现。在这个主题中,我们将深入探讨如何使用VC++的MFC(Microsoft ...

    java从n个数组中取出所有的组合

    在给定的代码中,`MainSrc.java`可能是包含主程序的源文件,`Attr.java`可能定义了数组的类或结构,而`InnerAttr.java`可能包含了实现组合算法的辅助类或方法。这些文件的具体内容需要查看才能进一步分析。 例如,`...

    实现了排列组合算法的类(JAVA).rar

    这个"实现了排列组合算法的类(JAVA).rar"文件提供了一种高效的JAVA实现,可以处理任意类型数组的排列和组合。下面将详细讨论排列组合的基本概念,以及在JAVA中实现这些算法的关键点。 排列是指从n个不同元素中...

    实现数学公式C(n,m)的java程序

    该代码实现功能为数学中的C(n,m),n为下标,m为上标。

    从n个数组中取出所有排列组合(Java实现)

    总结来说,从n个数组中取出所有排列组合的Java实现涉及到递归算法、回溯法以及数据结构的操作。理解这些概念并能够熟练运用是成为一名优秀程序员的关键。通过这个例子,我们可以看到如何利用Java的灵活性和表达力来...

    组合数学全排列算法(转)

    - **原理**:递归算法的基本思想是从n个元素中选择第一个元素,然后对剩下的n-1个元素进行递归求解,直到只剩下一个元素为止。 - **优点**:实现简单,易于理解。 - **缺点**:效率较低,尤其是当n较大时,递归...

    Shamir密钥分享算法java.rar

    在这个Java实现的项目中,"Shamir密钥分享算法java.rar"包含了一个带有GUI图形界面的应用程序。用户可以输入要加密的字符串,设置共享值m(即总共生成的子密钥数量)和门限值n。这些参数决定了密钥的分散方式和重新...

    最全房贷计算器 java 代码 含算法

    计算公式为:M=P*[(r/12)*(1+r/12)^n]/[(1+r/12)^n-1],其中M是每月应还金额,r是年利率除以12(月利率),n是贷款月数。 2. **等额本金还款法**:每个月还款的本金保持不变,利息逐月递减。每月还款金额计算公式为...

    java代码-从n个值里取m个值的全部组合方式(不重复)

    在Java编程中,从n个不同的元素中取出m个元素的所有不重复的组合是一个常见的算法问题,这通常涉及到组合数学和递归或回溯的概念。本文将深入探讨如何使用Java来实现这一功能。 首先,我们需要了解组合的基本概念。...

    Java for combinatorial number algorithm.zip_4MJN_M?n_enterbl4_组合

    本资料包“Java for combinatorial number algorithm.zip”包含了关于如何在Java中计算从M个不同元素中选择N个元素的组合数的详细教程,主要涉及的标签有“4mjn”,“m?n”,“enterbl4”以及“组合数算法”。 组合...

    组合数学中的生成排列算法java代码

    总之,这个Java程序结合了组合数学的排列概念和递归算法,通过深度优先搜索策略生成排列,并可能提供用户友好的界面展示。理解和掌握这些知识对于提升编程能力,特别是在算法设计和问题求解方面,有着显著的帮助。

    JAVA算法题目集合.pdf

    3. **组合问题**:找出所有可能的组合,可以使用递归或回溯算法解决,如计算从1到n中取出r个数的所有组合。 4. **质数判断**:判断一个数是否为质数,通常通过循环检查该数能否被2到其平方根之间的数整除。 5. **...

    高效的java版排列组合算法

    排列组合算法是指从n个元素中选择m个元素的所有可能的组合。例如,从5个元素中选择3个元素的排列组合有10种可能的组合:{1,2,3}、{1,2,4}、{1,3,4}、{1,2,5}、{1,3,5}、{1,4,5}、{2,3,4}、{2,3,5}、{2,4,5}、{3,4,5}...

    组合排列组合排列组合排列组合排列

    例如,创建一个递归函数,当m为0时返回1,否则返回n * C(n-1, m-1)。递归方法虽然直观,但可能会有重复计算的问题,效率较低。动态规划则能避免这个问题,通过一个二维数组存储已计算的组合值。 排列是指从n个不同...

    Java写的一个数组的中,m个元素的组合问题

    每次递归调用,函数可以选择数组中的下一个元素添加到当前组合,然后对剩余元素继续递归,直到达到目标组合数m或者没有更多元素可选。 下面是一个简单的Java代码示例,展示了如何生成一个数组中所有m个元素的组合:...

    典型合并算法可运行Java实例

    这个Java实例展示了如何将理论上的合并排序算法转化为实际的代码实现,通过理解并实践这个代码,可以深入理解合并排序的工作原理和实现细节。同时,这也是一个可运行的实例,可以直接在Java环境中编译和执行,验证...

Global site tag (gtag.js) - Google Analytics