美文网首页程序员
Java/安卓的 排列选择 组合选择算法及Set交集指定过滤

Java/安卓的 排列选择 组合选择算法及Set交集指定过滤

作者: 阿敏其人 | 来源:发表于2018-08-23 19:04 被阅读78次

    文/阿敏其人
    本文出自“阿敏其人”简书博客,转载请注明出处和链接。


    “所谓创意,只是把原有的元素重新组合而已。”美国广告大师詹姆斯。韦伯扬在《创意的生成》一书中如是说。

    离开书本,换个角度一看,这个世界也许从来就不存在新的东西,全部就是原有的元素的排列组合罢了。

    人类的情感世界太复杂,已知的物理和数学相对靠谱,为了人类整出了 排列数公式 和 组合数公式 。
    方便我们排列组合,方便我们研究,方便我们进行“创新”。

    排列 和 组合

    公式初探

    排列数公式:


    image.png

    组合数公式:


    image.png

    排列数公式
    从n个不同元素中,任取m(m≤n)个元素(被取出的元素各不相同),按照一定的顺序排成一列,叫做从n个不同元素中取出m个元素的一个排列。

    组合数公式
    组合数公式是指从m个不同元素中,任取n(n≤m)个元素并成一组,叫做从m个不同元素中取出n个元素的一个组合;从m个不同元素中取出n(n≤m)个元素的所有组合的个数,叫做从m个不同元素中取出n个元素的组合数。用符号c(m,n) 表示。

    咬文嚼字时间结束,向数学大家致敬的同时,看看如何把公式转成代码:

    public class TestOrg {
        public static void main(String[] args) {
            // 排列算法
            long pl = arrangement(3, 2);
             System.out.println("排列:"+pl); // 排列:6
             
             //组合算法
             long zh =  combination(3,2);
             System.out.println("组合:"+zh); // 组合:3
        }
        
        /** 
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1 
         * 排列个数
         * @param n 
         * @return 
         */  
        private static long factorial(int n) {  
            return (n > 1) ? n * factorial(n - 1) : 1;  
        }  
          
        /** 
         * 计算排列数,即A(n, m) = n!/(n-m)! 
         * 
         * @param n 
         * @param m 
         * @return 
         */  
        public static long arrangement(int n, int m) {  
            return (n >= m) ? factorial(n) / factorial(n - m) : 0;  
        }  
          
        /** 
         * 计算组合数,即C(n, m) = n!/((n-m)! * m!) 
         * @param n 
         * @param m 
         * @return 
         */  
        public static long combination(int n, int m) {  
            return (n >= m) ? factorial(n) / factorial(n - m) / factorial(m) : 0;  
        } 
    }
    

    举个栗子

    排列选择 和 组合选择

    排列选择demo

    public class TestArra{
        public static void main(String[] args) {
              arrangementSelect(new String[] {  
                        "1", "2", "3", "4"  
                }, 2);  
        }
        
        /** 
         * 排列选择(从列表中选择n个排列) 
         * @param dataList 待选列表 
         * @param n 选择个数 
         */  
        public static void arrangementSelect(String[] dataList, int n) {  
            System.out.println(String.format("A(%d, %d) = %d", dataList.length, n, arrangement(dataList.length, n)));  
            arrangementSelect(dataList, new String[n], 0);  
        }  
      
        /** 
         * 排列选择 
         * @param dataList 待选列表 
         * @param resultList 前面(resultIndex-1)个的排列结果 
         * @param resultIndex 选择索引,从0开始 
         */  
        private static void arrangementSelect(String[] dataList, String[] resultList, int resultIndex) {  
            int resultLen = resultList.length;  
            if (resultIndex >= resultLen) { // 全部选择完时,输出排列结果  
                System.out.println(Arrays.asList(resultList));  
                return;  
            }  
      
            // 递归选择下一个  
            for (int i = 0; i < dataList.length; i++) {  
                // 判断待选项是否存在于排列结果中  
                boolean exists = false;  
                for (int j = 0; j < resultIndex; j++) {  
                    if (dataList[i].equals(resultList[j])) {  
                        exists = true;  
                        break;  
                    }  
                }  
                if (!exists) { // 排列结果不存在该项,才可选择  
                    resultList[resultIndex] = dataList[i];  
                    arrangementSelect(dataList, resultList, resultIndex + 1);  
                }  
            }  
        } 
        
        /** 
         * 计算排列数,即A(n, m) = n!/(n-m)! 
         * @param n 
         * @param m 
         * @return 
         */  
        public static long arrangement(int n, int m) {  
            return (n >= m) ? factorial(n) / factorial(n - m) : 0;  
        }  
        
        /** 
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1 
         * @param n 
         * @return 
         */  
        public static long factorial(int n) {  
            return (n > 1) ? n * factorial(n - 1) : 1;  
        }   
    }
    

    输出:

    A(4, 2) = 12
    [1, 2]
    [1, 3]
    [1, 4]
    [2, 1]
    [2, 3]
    [2, 4]
    [3, 1]
    [3, 2]
    [3, 4]
    [4, 1]
    [4, 2]
    [4, 3]
    

    组合选择 DEMO

    public class TestComb {
        
        public static void main(String[] args) {        
              combinationSelect(new String[] {  
                        "1", "2", "3", "4", "5"  
                }, 2); 
        }
        
        /** 
         * 计算组合数,即C(n, m) = n!/((n-m)! * m!) 
         * @param n 
         * @param m 
         * @return 
         */  
        public static long combination(int n, int m) {  
            return (n >= m) ? factorial(n) / factorial(n - m) / factorial(m) : 0;  
        }
        
    
        /** 
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1 
         * @param n 
         * @return 
         */  
        public static long factorial(int n) {  
            return (n > 1) ? n * factorial(n - 1) : 1;  
        } 
        
        
           /** 
         * 组合选择(从列表中选择n个组合) 
         * @param dataList 待选列表 
         * @param n 选择个数 
         */  
        public static void combinationSelect(String[] dataList, int n) {  
            System.out.println(String.format("C(%d, %d) = %d", dataList.length, n, combination(dataList.length, n)));  
            combinationSelect(dataList, 0, new String[n], 0);  
        }  
      
        /** 
         * 组合选择 
         * @param dataList 待选列表 
         * @param dataIndex 待选开始索引 
         * @param resultList 前面(resultIndex-1)个的组合结果 
         * @param resultIndex 选择索引,从0开始 
         */  
        private static void combinationSelect(String[] dataList, int dataIndex, String[] resultList, int resultIndex) {  
            int resultLen = resultList.length;  
            int resultCount = resultIndex + 1;  
            if (resultCount > resultLen) { // 全部选择完时,输出组合结果  
                System.out.println(Arrays.asList(resultList));  
                return;  
            }  
      
            // 递归选择下一个  
            for (int i = dataIndex; i < dataList.length + resultCount - resultLen; i++) {  
                resultList[resultIndex] = dataList[i];  
                combinationSelect(dataList, i + 1, resultList, resultIndex + 1);  
            }  
        }  
    }
    

    输出:

    C(5, 2) = 10
    [1, 2]
    [1, 3]
    [1, 4]
    [1, 5]
    [2, 3]
    [2, 4]
    [2, 5]
    [3, 4]
    [3, 5]
    [4, 5]
    

    排列选择和组合选择,都是罗列选择结果。
    区别是,排列选择是全排列,一个选择内部选择交换了位置,也会认为是一个新的选择。
    组合选择里,当一个内部元素的位置发生了变化,会过滤掉,不会认为这是一个新选择。

    排列组合乱斗

    路飞成长史

    public class TestFun {
        public static void main(String[] args) {
    
            // 组合选择算法
            combinationSelect(new String[] { "路飞打怪", "怪打路飞", "路飞输了开始训练", "路飞赢了开心吃肉"}, 3);
    
            // 排列选择算法
            arrangementSelect(new String[] {"路飞打怪", "怪打路飞", "路飞输了开始训练", "路飞赢了开心吃肉" }, 3);
        }
    
        /**
         * 排列选择(从列表中选择n个排列)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void arrangementSelect(String[] dataList, int n) {
            System.out.println(String.format("A(%d, %d) = %d", dataList.length, n, arrangement(dataList.length, n)));
            arrangementSelect(dataList, new String[n], 0);
        }
    
        /**
         * 排列选择
         * 
         * @param dataList
         *            待选列表
         * @param resultList
         *            前面(resultIndex-1)个的排列结果
         * @param resultIndex
         *            选择索引,从0开始
         */
        private static void arrangementSelect(String[] dataList, String[] resultList, int resultIndex) {
            int resultLen = resultList.length;
            if (resultIndex >= resultLen) { // 全部选择完时,输出排列结果
                System.out.println(Arrays.asList(resultList));
                return;
            }
    
            // 递归选择下一个
            for (int i = 0; i < dataList.length; i++) {
                // 判断待选项是否存在于排列结果中
                boolean exists = false;
                for (int j = 0; j < resultIndex; j++) {
                    if (dataList[i].equals(resultList[j])) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) { // 排列结果不存在该项,才可选择
                    resultList[resultIndex] = dataList[i];
                    arrangementSelect(dataList, resultList, resultIndex + 1);
                }
            }
        }
    
        /**
         * 计算排列数,即A(n, m) = n!/(n-m)!
         * 
         * @param n
         * @param m
         * @return
         */
        public static long arrangement(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) : 0;
        }
    
        /**
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
         * 
         * @param n
         * @return
         */
        public static long factorial(int n) {
            return (n > 1) ? n * factorial(n - 1) : 1;
        }
    
        // ===
        /**
         * 计算组合数,即C(n, m) = n!/((n-m)! * m!)
         * 
         * @param n
         * @param m
         * @return
         */
        public static long combination(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) / factorial(m) : 0;
        }
    
        // /**
        // * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
        // * @param n
        // * @return
        // */
        // public static long factorial(int n) {
        // return (n > 1) ? n * factorial(n - 1) : 1;
        // }
    
        /**
         * 组合选择(从列表中选择n个组合)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void combinationSelect(String[] dataList, int n) {
            System.out.println(String.format("C(%d, %d) = %d", dataList.length, n, combination(dataList.length, n)));
            combinationSelect(dataList, 0, new String[n], 0);
        }
    
        /**
         * 组合选择
         * 
         * @param dataList
         *            待选列表
         * @param dataIndex
         *            待选开始索引
         * @param resultList
         *            前面(resultIndex-1)个的组合结果
         * @param resultIndex
         *            选择索引,从0开始
         */
        private static void combinationSelect(String[] dataList, int dataIndex, String[] resultList, int resultIndex) {
            int resultLen = resultList.length;
            int resultCount = resultIndex + 1;
            if (resultCount > resultLen) { // 全部选择完时,输出组合结果
                System.out.println(Arrays.asList(resultList));
                return;
            }
    
            // 递归选择下一个
            for (int i = dataIndex; i < dataList.length + resultCount - resultLen; i++) {
                resultList[resultIndex] = dataList[i];
                combinationSelect(dataList, i + 1, resultList, resultIndex + 1);
            }
        }
    }
    

    输出:

    C(4, 3) = 4
    [路飞打怪, 怪打路飞, 路飞输了开始训练]
    [路飞打怪, 怪打路飞, 路飞赢了开心吃肉]
    [路飞打怪, 路飞输了开始训练, 路飞赢了开心吃肉]
    [怪打路飞, 路飞输了开始训练, 路飞赢了开心吃肉]
    A(4, 3) = 24
    [路飞打怪, 怪打路飞, 路飞输了开始训练]
    [路飞打怪, 怪打路飞, 路飞赢了开心吃肉]
    [路飞打怪, 路飞输了开始训练, 怪打路飞]
    [路飞打怪, 路飞输了开始训练, 路飞赢了开心吃肉]
    [路飞打怪, 路飞赢了开心吃肉, 怪打路飞]
    [路飞打怪, 路飞赢了开心吃肉, 路飞输了开始训练]
    [怪打路飞, 路飞打怪, 路飞输了开始训练]
    [怪打路飞, 路飞打怪, 路飞赢了开心吃肉]
    [怪打路飞, 路飞输了开始训练, 路飞打怪]
    [怪打路飞, 路飞输了开始训练, 路飞赢了开心吃肉]
    [怪打路飞, 路飞赢了开心吃肉, 路飞打怪]
    [怪打路飞, 路飞赢了开心吃肉, 路飞输了开始训练]
    [路飞输了开始训练, 路飞打怪, 怪打路飞]
    [路飞输了开始训练, 路飞打怪, 路飞赢了开心吃肉]
    [路飞输了开始训练, 怪打路飞, 路飞打怪]
    [路飞输了开始训练, 怪打路飞, 路飞赢了开心吃肉]
    [路飞输了开始训练, 路飞赢了开心吃肉, 路飞打怪]
    [路飞输了开始训练, 路飞赢了开心吃肉, 怪打路飞]
    [路飞赢了开心吃肉, 路飞打怪, 怪打路飞]
    [路飞赢了开心吃肉, 路飞打怪, 路飞输了开始训练]
    [路飞赢了开心吃肉, 怪打路飞, 路飞打怪]
    [路飞赢了开心吃肉, 怪打路飞, 路飞输了开始训练]
    [路飞赢了开心吃肉, 路飞输了开始训练, 路飞打怪]
    [路飞赢了开心吃肉, 路飞输了开始训练, 怪打路飞]
    
    

    mac升级计划演示组合选择

    2018款的15寸的mbp,在官方配置的基础上的,提供了几项升级,比如

    • 升级1T固态
    • 升级32G内存
    • 升级i9处理器

    假设我们选择其中两项目升级,那么显然应该采用组合选择。

    public class TestFun {
        public static void main(String[] args) {
            // 排列选择算法
            System.out.println("排列 选择");
            arrangementSelect(new String[] {"升级1T固态", "升级32G内存", "升级i9处理器" }, 2);
        
            
            // 组合选择算法
            System.out.println("组合 选择");
            combinationSelect(new String[] { "升级1T固态", "升级32G内存", "升级i9处理器"}, 2);
    }
    
        /**
         * 排列选择(从列表中选择n个排列)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void arrangementSelect(String[] dataList, int n) {
            System.out.println(String.format("A(%d, %d) = %d", dataList.length, n, arrangement(dataList.length, n)));
            arrangementSelect(dataList, new String[n], 0);
        }
    
        /**
         * 排列选择
         * 
         * @param dataList
         *            待选列表
         * @param resultList
         *            前面(resultIndex-1)个的排列结果
         * @param resultIndex
         *            选择索引,从0开始
         */
        private static void arrangementSelect(String[] dataList, String[] resultList, int resultIndex) {
            int resultLen = resultList.length;
            if (resultIndex >= resultLen) { // 全部选择完时,输出排列结果
                System.out.println(Arrays.asList(resultList));
                return;
            }
    
            // 递归选择下一个
            for (int i = 0; i < dataList.length; i++) {
                // 判断待选项是否存在于排列结果中
                boolean exists = false;
                for (int j = 0; j < resultIndex; j++) {
                    if (dataList[i].equals(resultList[j])) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) { // 排列结果不存在该项,才可选择
                    resultList[resultIndex] = dataList[i];
                    arrangementSelect(dataList, resultList, resultIndex + 1);
                }
            }
        }
    
        /**
         * 计算排列数,即A(n, m) = n!/(n-m)!
         * 
         * @param n
         * @param m
         * @return
         */
        public static long arrangement(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) : 0;
        }
    
        /**
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
         * 
         * @param n
         * @return
         */
        public static long factorial(int n) {
            return (n > 1) ? n * factorial(n - 1) : 1;
        }
    
        // ===
        /**
         * 计算组合数,即C(n, m) = n!/((n-m)! * m!)
         * 
         * @param n
         * @param m
         * @return
         */
        public static long combination(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) / factorial(m) : 0;
        }
    
        // /**
        // * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
        // * @param n
        // * @return
        // */
        // public static long factorial(int n) {
        // return (n > 1) ? n * factorial(n - 1) : 1;
        // }
    
        /**
         * 组合选择(从列表中选择n个组合)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void combinationSelect(String[] dataList, int n) {
            System.out.println(String.format("C(%d, %d) = %d", dataList.length, n, combination(dataList.length, n)));
            combinationSelect(dataList, 0, new String[n], 0);
        }
    
        /**
         * 组合选择
         * 
         * @param dataList
         *            待选列表
         * @param dataIndex
         *            待选开始索引
         * @param resultList
         *            前面(resultIndex-1)个的组合结果
         * @param resultIndex
         *            选择索引,从0开始
         */
        private static void combinationSelect(String[] dataList, int dataIndex, String[] resultList, int resultIndex) {
            int resultLen = resultList.length;
            int resultCount = resultIndex + 1;
            if (resultCount > resultLen) { // 全部选择完时,输出组合结果
                System.out.println(Arrays.asList(resultList));
                return;
            }
    
            // 递归选择下一个
            for (int i = dataIndex; i < dataList.length + resultCount - resultLen; i++) {
                resultList[resultIndex] = dataList[i];
                combinationSelect(dataList, i + 1, resultList, resultIndex + 1);
            }
        }
    }
    

    输出:

    排列 选择
    A(3, 2) = 6
    [升级1T固态, 升级32G内存]
    [升级1T固态, 升级i9处理器]
    [升级32G内存, 升级1T固态]
    [升级32G内存, 升级i9处理器]
    [升级i9处理器, 升级1T固态]
    [升级i9处理器, 升级32G内存]
    组合 选择
    C(3, 2) = 3
    [升级1T固态, 升级32G内存]
    [升级1T固态, 升级i9处理器]
    [升级32G内存, 升级i9处理器]
    
    

    .
    .

    升级版 单个方向多选,指定过滤元素

    在上面的例子中,我们每个升级方向只提供了一个选项,那么是每个方向多个选择呢?

    比如,硬盘方向有3个选择:

    • SSD-1T版
    • SSD-2T版
    • SSD-3T版

    内存也有三个选择:

    • 内存32G
    • 内存64G
    • 内存128G
      (请忽略显示是否配到这么大的容量,一切只是为了演示)

    那么,按照原有的算法,结果有15种:

    C(6, 2) = 15
    [SSD-3T版, SSD-2T版]
    [SSD-3T版, SSD-1T版]
    [SSD-3T版, 内存128G]
    [SSD-3T版, 内存32G]
    [SSD-3T版, 内存64G]
    [SSD-2T版, SSD-1T版]
    [SSD-2T版, 内存128G]
    [SSD-2T版, 内存32G]
    [SSD-2T版, 内存64G]
    [SSD-1T版, 内存128G]
    [SSD-1T版, 内存32G]
    [SSD-1T版, 内存64G]
    [内存128G, 内存32G]
    [内存128G, 内存64G]
    [内存32G, 内存64G]
    

    显然,这不是我们想要的结果。
    重复地 加内存和内存之类的是什么鬼?现加一个3T的,再加一个2T的?开什么玩笑。
    我们应该做到每一个方向都只能有一个选项出现在最后的筛选结果。

    解决办法

    1、取出组合选择结果,这步不变
    2、筛选数据时,传入原有的方向集,几个小的Set,每个Set代表一个方向
    3、将每一个 组合选择 的结果与每个方向的Set做交集运算,如果交集大于2,则放弃。

    3步做完,即可。

    public class TestFun {
        public static void main(String[] args) {
            // 排列选择算法
            // System.out.println("排列 选择");
            // arrangementSelect(new String[] {"1", "2", "3","4", "5", "6" }, 2);
    
            // 方向一
            Set<String> set1 = new HashSet<String>();
            set1.add("SSD-1T版");// SSD-1T版
            set1.add("SSD-2T版");// SSD-2T版
            set1.add("SSD-3T版");// SSD-3T版
    
            // 方向二
            Set<String> set2 = new HashSet<String>();
            set2.add("内存32G"); // 内存32G
            set2.add("内存64G"); // 内存64G
            set2.add("内存128G"); // 内存128G
        
            Set<String> allDataSet = new HashSet<String>();
            allDataSet.addAll(set1);
            allDataSet.addAll(set2);
    
            // 总的元素
            String[] dataSetAsArray = allDataSet.toArray(new String[allDataSet.size()]);
    
            List<Set<String>> noPepeatingList = new ArrayList<>();
            noPepeatingList.add(set1);
            noPepeatingList.add(set2);
    
            // 组合选择算法
            System.out.println("组合 选择");
            // 有多少个方法,就应该有多少种组合。
            // 所以的combinationSelect第二个参数我们传 noPepeatingList.size()
            // 第三个传每个方向原有的元素集,方便后面比较交集过滤
            combinationSelect(dataSetAsArray, noPepeatingList.size(), noPepeatingList);
            //combinationSelect(dataSetAsArray, 2,null);
        }
    
        /**
         * 排列选择(从列表中选择n个排列)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void arrangementSelect(String[] dataList, int n) {
            System.out.println(String.format("A(%d, %d) = %d", dataList.length, n, arrangement(dataList.length, n)));
            arrangementSelect(dataList, new String[n], 0);
        }
    
        /**
         * 排列选择
         * 
         * @param dataList
         *            待选列表
         * @param resultList
         *            前面(resultIndex-1)个的排列结果
         * @param resultIndex
         *            选择索引,从0开始
         */
        private static void arrangementSelect(String[] dataList, String[] resultList, int resultIndex) {
            int resultLen = resultList.length;
            if (resultIndex >= resultLen) { // 全部选择完时,输出排列结果
                System.out.println(Arrays.asList(resultList));
    
                List<String> strings = Arrays.asList(resultList);
                strings.size();
    
                return;
            }
    
            // 递归选择下一个
            for (int i = 0; i < dataList.length; i++) {
                // 判断待选项是否存在于排列结果中
                boolean exists = false;
                for (int j = 0; j < resultIndex; j++) {
                    if (dataList[i].equals(resultList[j])) {
                        exists = true;
                        break;
                    }
                }
                if (!exists) { // 排列结果不存在该项,才可选择
                    resultList[resultIndex] = dataList[i];
                    arrangementSelect(dataList, resultList, resultIndex + 1);
                }
            }
        }
    
        /**
         * 计算排列数,即A(n, m) = n!/(n-m)!
         * 
         * @param n
         * @param m
         * @return
         */
        public static long arrangement(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) : 0;
        }
    
        /**
         * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
         * 
         * @param n
         * @return
         */
        public static long factorial(int n) {
            return (n > 1) ? n * factorial(n - 1) : 1;
        }
    
        // ===
        /**
         * 计算组合数,即C(n, m) = n!/((n-m)! * m!)
         * 
         * @param n
         * @param m
         * @return
         */
        public static long combination(int n, int m) {
            return (n >= m) ? factorial(n) / factorial(n - m) / factorial(m) : 0;
        }
    
        // /**
        // * 计算阶乘数,即n! = n * (n-1) * ... * 2 * 1
        // * @param n
        // * @return
        // */
        // public static long factorial(int n) {
        // return (n > 1) ? n * factorial(n - 1) : 1;
        // }
    
        /**
         * 组合选择(从列表中选择n个组合)
         * 
         * @param dataList
         *            待选列表
         * @param n
         *            选择个数
         */
        public static void combinationSelect(String[] dataList, int n, List<Set<String>> noPepeatingList) {
            System.out.println(String.format("C(%d, %d) = %d", dataList.length, n, combination(dataList.length, n)));
            combinationSelect(dataList, 0, new String[n], 0, noPepeatingList);
        }
    
        /**
         * 组合选择
         * 
         * @param dataList
         *            待选列表
         * @param dataIndex
         *            待选开始索引
         * @param resultList
         *            前面(resultIndex-1)个的组合结果
         * @param resultIndex
         *            选择索引,从0开始
         * @param noPepeatingList
         *            可变参数,自身不允许重复的数据
         */
        private static void combinationSelect(String[] dataList, int dataIndex, String[] resultList, int resultIndex,
                List<Set<String>> noPepeatingList) {
            int resultLen = resultList.length;
            int resultCount = resultIndex + 1;
            if (resultCount > resultLen) { // 全部选择完时,输出组合结果
                Set<String> staffsSet = new HashSet<String>(Arrays.asList(resultList));
                Set<String> reSultString = new HashSet<String>(Arrays.asList(resultList));
                //List<String> reSultString = Arrays.asList(resultList);
                if (noPepeatingList != null) {
                    boolean isContain = false;
                    for (Set<String> tempSet : noPepeatingList) {
                        // 交集
                        Set<String> intersectionSet = new HashSet<String>();
                        intersectionSet.addAll(tempSet);
                        // 拿出交集,关键点。
                        intersectionSet.retainAll(reSultString);
                        //如果交集的数量大于2,那么代表和某个子集重复。
                        // 排除数据,不允许存在跟自身原有数据集重复的元素,比如 1,2 在 1,2,3里面,应该排除掉
                        if (intersectionSet.size()>=2) {
                            isContain = true;
                            break;
                        }
                    }
                    if (!isContain) {
                        System.out.println(reSultString);
                    }
    
                } else {
                    System.out.println(reSultString);
                }
    
                return;
            }
    
            // 递归选择下一个
            for (int i = dataIndex; i < dataList.length + resultCount - resultLen; i++) {
                resultList[resultIndex] = dataList[i];
                combinationSelect(dataList, i + 1, resultList, resultIndex + 1, noPepeatingList);
            }
        }
    }
    
    

    输出:

    组合 选择
    C(6, 2) = 15
    [SSD-3T版, 内存128G]
    [SSD-3T版, 内存32G]
    [SSD-3T版, 内存64G]
    [SSD-2T版, 内存128G]
    [SSD-2T版, 内存32G]
    [SSD-2T版, 内存64G]
    [SSD-1T版, 内存128G]
    [SSD-1T版, 内存32G]
    [SSD-1T版, 内存64G]
    

    没错,才是我们想要的结果。

    那么如果我们有3个方向呢

    试一下:

                    // 方向一
            Set<String> set1 = new HashSet<String>();
            set1.add("SSD-1T版");// SSD-1T版
            set1.add("SSD-2T版");// SSD-2T版
            set1.add("SSD-3T版");// SSD-3T版
    
            // 方向二
            Set<String> set2 = new HashSet<String>();
            set2.add("内存32G"); // 内存32G
            set2.add("内存64G"); // 内存64G
            set2.add("内存128G"); // 内存128G
        
            // 方向三
            Set<String> set3 = new HashSet<>();
            set3.add("处理器i9版"); // 处理器i9
            set3.add("处理器未来i10版"); // 处理器未来i10
            set3.add("处理器未来i11版"); // 处理器未来i11
    
            Set<String> allDataSet = new HashSet<String>();
            allDataSet.addAll(set1);
            allDataSet.addAll(set2);
            allDataSet.addAll(set3);
            // 总的元素
            String[] dataSetAsArray = allDataSet.toArray(new String[allDataSet.size()]);
    
            List<Set<String>> noPepeatingList = new ArrayList<>();
            noPepeatingList.add(set1);
            noPepeatingList.add(set2);
            noPepeatingList.add(set3);
    
            // 组合选择算法
            System.out.println("组合 选择");
            // 有多少个方法,就应该有多少种组合。
            // 所以的combinationSelect第二个参数我们传 noPepeatingList.size()
            // 第三个传每个方向原有的元素集,方便后面比较交集过滤
            combinationSelect(dataSetAsArray, noPepeatingList.size(), noPepeatingList);
    

    输出:

    组合 选择
    C(9, 3) = 84
    [处理器i9版, SSD-3T版, 内存128G]
    [处理器i9版, SSD-3T版, 内存32G]
    [处理器i9版, SSD-3T版, 内存64G]
    [处理器i9版, SSD-2T版, 内存128G]
    [处理器i9版, SSD-2T版, 内存32G]
    [处理器i9版, SSD-2T版, 内存64G]
    [处理器i9版, SSD-1T版, 内存128G]
    [处理器i9版, SSD-1T版, 内存32G]
    [处理器i9版, SSD-1T版, 内存64G]
    [SSD-3T版, 内存128G, 处理器未来i10版]
    [SSD-3T版, 内存128G, 处理器未来i11版]
    [SSD-3T版, 处理器未来i10版, 内存32G]
    [SSD-3T版, 处理器未来i10版, 内存64G]
    [SSD-3T版, 内存32G, 处理器未来i11版]
    [SSD-3T版, 处理器未来i11版, 内存64G]
    [SSD-2T版, 内存128G, 处理器未来i10版]
    [SSD-2T版, 内存128G, 处理器未来i11版]
    [SSD-2T版, 处理器未来i10版, 内存32G]
    [SSD-2T版, 处理器未来i10版, 内存64G]
    [SSD-2T版, 内存32G, 处理器未来i11版]
    [SSD-2T版, 处理器未来i11版, 内存64G]
    [SSD-1T版, 内存128G, 处理器未来i10版]
    [SSD-1T版, 内存128G, 处理器未来i11版]
    [SSD-1T版, 处理器未来i10版, 内存32G]
    [SSD-1T版, 处理器未来i10版, 内存64G]
    [SSD-1T版, 内存32G, 处理器未来i11版]
    [SSD-1T版, 处理器未来i11版, 内存64G]
    

    没错,27种结果。

    每一种结果都是 一个硬盘 + 一个内存 + 一个处理器选择。
    绝无重复。

    废话终于说完了。

    最后,感谢用cgs1999Java实现排列、组合算法

    本文完。

    相关文章

      网友评论

        本文标题:Java/安卓的 排列选择 组合选择算法及Set交集指定过滤

        本文链接:https://www.haomeiwen.com/subject/yjpmiftx.html