我这里只写了组合的算法。

        假设现有 M=4 个数据 a,b,c,d。从中随机抽取n个数,n为1—4个数据进行组合。那么数学中的计算组合方式为C(4,1) + C(4,2) + C(4,3) + C(4,4)  = 4 + 6 + 4 + 1 = 15。那么共有15种组合方式。

方案一:此方法容易理解但是效率慢

        我的做法是,按顺序循环组合,数据分为已组合的数据和未组合(未组合数据指的是已组合数据往后剩余的数据),然后把未参与组合的进行循环与已组合再次组合,循环进行,直到最后。
        如下示例,规律

      已组合数据    剩余未参与组合的数据

1       a                            b,c,d               //a 后面还有b,c,d未参与
2       b                             c,d                 //b 后面还有c,d未参与
3       c                             d                   //c 后面还有d未参与
4       d                                                 //d后面没有了其他数据
//开始把上述第1行a —  b,c,d 进行二次循环组合
5       a,b                          c,d               //a,b 后面还有c,d未参与         
6       a,c                           d                 //a,c 后面还有d未参与          
7       a,d                                                                                                                                          
//开始把上面第5行a,b —  c,d 行进行循环组合      
8       a,b,c                        d                //a,b,c 后面还有d未参与
9       a,b,c,d
//开始把上面第2行b  —  c,d 行进行循环组合
10     b,c                          d                //b,c 后面还有c,d未参与
11     b,d                          
//开始把上面第3行c  —  d 行进行循环组合
12      c,d                                             
//开始把上面第6行a,c  —  d 行进行循环组合
13      a,c,d                                         
//开始把上面第8行a,b,c  —  d 行进行循环组合
14      a,b,c,d                                      
//开始把上面第10行b,c  —  d 行进行循环组合
15      b,c,d                                         
..............................依据上述规则,循环把每一行未组合的与当前已组合的再次进行组合,然后计算剩余未组合数据。至到未参与组合的数据个数为0

上述思路基本明了,按顺序依次组合,只是根据每行上的未参与组合数据,进行再次组合直到全部组合完成。代码实现如下:

public static void main(String[] args) {
        //结果集
        List<String> resList = new ArrayList<>();
        //初始化需要组合的数据
        String[] arr = new String[]{"a","b","c","d"};
        List<String> initList = new LinkedList(Arrays.asList(arr));
        //map中 key:组合数据的前缀,value:未参与组合的数据
        Map<String,List<String>> map = new HashMap<>();
        for (int i = 0; i < initList.size(); i++) {
            String pj = initList.get(i);
            resList.add(pj);
            System.out.println(pj);
            //当剩余未组合的数据个数为0时 不再继续添加
            if(i+1 < initList.size()){
                //按顺序排列 下标为i后面未参与组合的数据
                List<String> syList = initList.subList(i+1,initList.size());
                map.put(pj,syList);
            }
        }
        combinLoop(map,resList);
        System.out.println(resList.size());
    }

    public static void combinLoop(Map<String,List<String>> map,List<String> resList){
        for (Map.Entry<String, List<String>> entry : map.entrySet()) {
            String prefix = entry.getKey();
            List<String> list = entry.getValue();
            Map<String,List<String>> map2 = new HashMap<>();
            //循环未参与组合的数据,与之前的prefix进行组合
            for (int i = 0; i < list.size(); i++) {
                String newPre = prefix+list.get(i);
                System.out.println(newPre);
                resList.add(newPre);
                if(i+1 < list.size()){
                    //按顺序排列,下标为i后面未参与组合的数据集合
                    List<String> syList = list.subList(i+1,list.size());
                    map2.put(newPre,syList);
                }
            }
            combinLoop(map2,resList);
        }
    }

方案2:效率更快

此方法,对初始化数据initList进行循环,把前一次的结果resultList与当前参与循环的数据进行一一组合,得到新的结果加入到已有的组合结果resultList中,initList依次循环,resultList不断加入新的数据,重复进行直到最后。如下示例:

对initList = {"a","b","c","d"} 进行循环,初始化resultList为空。

              当前参与循环数据   前一次resultList集    结束resultList集

第一次循环      a                        null                                    a
第二次循环      b                         a                                    a,b,ab
第三次循环      c                      a,b,ab                             a,b,ab,c,ac,bc,abc
第四次循环      d                a,b,ab,c,ac,bc,abc    a,b,ab,c,ac,bc,abc,d,ad,bd,abd,cd,acd,bcd,abcd   

通过上述规律可看出,当前参与循环的数据与已有的resultList集进行新的组合,可得到黄色部分组合后的结果,再加上resultList中原来已有的数据,组成新的resultList与下一次参与循环的数据组合,依次进行直到所有数据循环完成。

代码如下:

    public static void combine2(){
        String[] arr = new String[]{"a","b","c","d"};
        //初始化数据
        List<String> initList = Arrays.asList(arr);
        //结果集
        List<String> resultList = new ArrayList<>();
        for (String init : initList) {
            //重新赋值上次循环组合后得到的resultList结果集
            List<String> list = new ArrayList<>(resultList);
            //resultList添加初始数据
            resultList.add(init);
            for (String pr : list) {
                //把前一次得到的resultList 与当前数据重新进行组合
                resultList.add(pr + init);
            }
        }
    }

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐