計算一維組合數的java實現


背景很簡單,就是從給定的m個不同的元素中選出n個,輸出所有的組合情況!

 

例如:從1到m的自然數中,選擇n(n<=m)個數,有多少種選擇的組合,將其輸出!

 

本方案的代碼實現邏輯是比較成熟的方案:

* 一個bit位(boolean)一維數組中,初始化全為0(false), 然后給左邊的n個位初始化為1(true)。
* <1> 從左向右找第一個10的位置,將10換位程01,然后將這個01左邊的所有的1全都移位到數組的最左邊,此時得到的1所在位置下標對應序列即為一個組合數。 
* <2> 循環重復上面的<1>步驟的操作,直到所有的1都移動到最右邊為止。

 

先不多說其他的,直接將代碼貼在這里,以供有需要的伙伴借鑒:

  1 /**
  2  * @author "shihuc"
  3  * @date   2016年12月1日
  4  */
  5   6 
  7 import java.util.ArrayList;
  8 import java.util.Arrays;
  9 
 10 /**
 11  * @author chengsh05
 12  * 
 13  * 組合算法實現,支持產品列表頁的篩選模塊實現全靜態化。
 14  * 
 15  * 給定m個不同的數,從中選擇出n個數的不同選擇方法有多少種?
 16  * 答案:一共有 n!/(n-m)!*m!
 17  * 
 18  * 存儲計算出來的組合結構,組合元素之間用逗號分隔
 19  *         例如1,2,3的全組合:
 20  *         "1,", "2,", "3,","1,2,", "1,3,", "2,3,", "1,2,3,"
 21  */
 22 public class Combination {
 23     
 24     /**
 25      * 從m個元素中取出n個元素,獲取排序好了的組合數列表,同一個組合中的元素按從小到大排序。
 26      * 
 27      * @param m 組合的元素基數
 28      * @param n 組合的被選元素個數
 29      * @return 排序好后的組合列表
 30      * @throws Exception 
 31      */
 32     public ArrayList<String> getCombinations(int m, int n) throws Exception{
 33         
 34         if(m < n){
 35             throw new IllegalCombinationInputException();
 36         }
 37         
 38         ArrayList<String> resCom = calcCombination(m, n);
 39         
 40         return resCom;
 41     }
 42     
 43     /**
 44      * 通過移位方式,計算給定m個數中取出n個數的組合列表。
 45      * 
 46      * 具體思路:
 47      * 一個bit位(boolean)數組中,初始化全為0(false), 然后給左邊的n個位初始化為1(true)。
 48      * <1> 從左向右找第一個10的位置,將10換位程01,然后將這個01左邊的所有的1全都 移位到數組的最左邊,此時得到的1所在位置下標對應的序列即為一個組合數。 
 49      * <2> 循環重復上面的<1>步驟的操作,直到所有的1都移動到最右邊為止。
 50      * 
 51      * @param m 輸入的基數個數
 52      * @param n 組合被選元素格式
 53      * @return 原始組合數列表,即沒有排序的組合
 54      */
 55     private ArrayList<String> calcCombination(int m, int n){
 56         boolean base[] = new boolean[m];
 57         Arrays.fill(base, false);
 58         for(int i=0; i<n; i++){
 59             base[i] = true;
 60         }
 61         return combination(base,m,n);
 62     }
 63     
 64     private ArrayList<String> combination(boolean a[], int m, int n){
 65         ArrayList<String> combination = new ArrayList<String>();        
 66         while(!isAllZeroLeft(a, m, n)){
 67             for(int i=0; i<m-1; i++){
 68                 if(a[i] == true && a[i+1] == false){
 69                     String elem = calcElement(a);    //計算出一個組合元素
 70                     //System.out.println(elem);
 71                     combination.add(elem);
 72                     oneZeroSwap(a, i, i+1);          //完成10/01換位                    
 73                     moveFrontOneToLeft(a, i);        //完成剩余左邊的1全向最左邊搬移操作                    
 74                     break;
 75                 }
 76             }
 77         }
 78         
 79         //最后一個元素也是組合的一個,即所有的1(true)都到了數組的最右邊
 80         combination.add(calcElement(a));
 81         return combination;
 82     }
 83     
 84     /**
 85      * 異或操作實現不開辟新的存儲空間完成兩個數的位置交換。
 86      * 
 87      * @param a 待操作數所在的數組
 88      * @param x 待交換位置的第一個數在數組中的下標
 89      * @param y 待交換位置的第二個數在數組中的下標
 90      */
 91     private void oneZeroSwap(boolean a[], int x, int y){
 92         a[x] = a[x] ^ a[y];
 93         a[y] = a[y] ^ a[x];
 94         a[x] = a[x] ^ a[y];
 95     }
 96     
 97     /**
 98      * 判斷10作01交換位置后,是否實現了數組a中右端的n個元素全為1(true),這個結果作為10/01換位結束標識
 99      * 
100      * @param a 10/01換位的輸入數組
101      * @param m 計算組合的元數據個數
102      * @param n 計算組合的被選取元素個數
103      * @return true表示10/01換位結束,false表示還可以繼續
104      */
105     private boolean isAllZeroLeft(boolean a[], int m, int n){
106         int gap = m - n;
107         for(int i=0; i<gap; i++){
108             if(a[i]){
109                 return false;
110             }
111         }
112         return true;
113     }
114     
115     /**
116      * 將10/01換位之后數組左邊的全部1都搬移到數組的最左邊。
117      * 
118      * @param a 待操作的組合數組
119      * @param end 指明搬移操作的范圍,在end數組下標左邊的進行搬移, 這個end的值小於數組的長度
120      */
121     private void moveFrontOneToLeft(boolean a[], int end){
122         int oneCnt = 0;
123         for(int i=0; i<end; i++){
124             if(a[i]){
125                 oneCnt++;
126                 a[i] = false;
127             }
128         }
129         for(int i=0; i<oneCnt; i++){
130             a[i] = true;
131         }
132     }
133     
134     /**
135      * 計算當前數組中的組合元素的值,數組元素為1(true)的對應的下標的全集,即為所需的一個組合元素值
136      * 
137      * @param a 待操作的組合數組
138      * @return 一個組合的值, 去掉了最后的一個逗號分隔符
139      */
140     private String calcElement(boolean a[]){        
141         String elem = "";
142         for(int i=0; i<a.length; i++){
143             if(a[i]){
144                 elem += (i+1) + ",";
145             }
146         }
147         return elem.substring(0, elem.length() - 1);
148     }
149         
150     
151     /**
152      * @param args
153      * @throws Exception 
154      */
155     public static void main(String[] args) throws Exception {
156         int m = 5, n = 2;
157         Combination combination = new Combination();
158         ArrayList<String> coms = combination.getCombinations(m, n);
159         for(int i = 0; i<coms.size(); i++){
160             System.out.println(coms.get(i));
161         }
162     }
163 }

 代碼中定義的Exception的類:

 1 /**
 2  * @author "shihuc"
 3  * @date   2016年12月1日
 4  */
 5 
 6 /**
 7  * @author chengsh05
 8  *
 9  */
10 public class IllegalCombinationInputException extends Exception{
11 
12     private static final long serialVersionUID = 678024281707796100L;
13     
14     public IllegalCombinationInputException(){
15         super("The combination base number should be not less than the selection number.");  
16     }
17 
18 }

 

此算法思路,在很多場景下還是值得借鑒的!

 

算法是計算機科學的靈魂,堅持算法學習和應用......


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM