【问题标题】:Find all possible combinations of of a particular size for a set of numbers查找一组数字的特定大小的所有可能组合
【发布时间】:2016-09-04 16:08:47
【问题描述】:

我正在寻找解决以下问题:

给定两个整数 n 和 k,返回 1 2 3 ... n 中 k 个数的所有可能组合。

确保对组合进行排序。

详细说明,

  1. 在每个条目中,元素都应该被排序。 [1, 4] 是有效条目,而 [4, 1] 不是。
  2. 条目应在其内部进行排序。

示例: 如果 n = 4 且 k = 2,则解为:

[
   [1,2],
   [1,3],
   [1,4],
   [2,3],
   [2,4],
   [3,4],
]

这是我想出的解决方案:

public ArrayList<ArrayList<Integer>> combine(int n, int k) {

    //variable where the resultant sets of of numbers are to be stored
    ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();

    //finding all the subsets from 1-n of size k and storing them in result
    subset(n,result,k);

    //sorting the resultant set of subsets lexicographically
    Collections.sort(result,new Comparator<ArrayList<Integer>>(){

        @Override
        public int compare(ArrayList<Integer> a,ArrayList<Integer> b){

            int aSize = a.size();
            int bSize = b.size();

            for(int i=0;i<(int)Math.min(aSize,bSize);i++){

                int comparison = Integer.compare(a.get(i),b.get(i));
                if(comparison!=0) return comparison;
            }
               return Integer.compare(aSize,bSize);
        }
    });

    return result;
}


void subset(int n,ArrayList<ArrayList<Integer>> result,int size){
    for(int i=0;i<(1<<n);++i){

        //the arraylist to be added to the result
        ArrayList<Integer> element = new ArrayList<Integer>();

        //iterating 2^n times since those are the total number of possible subsets
        for(int j=0;j<n;++j)
            if((i&(1<<j))>0)
                element.add(j+1);


        //only adding the resultant subset to the result if it matches the size criteria
        if(element.size() == size)
            result.add(element);
    }
}

我正在查看这个答案,我不禁认为必须有一个更优化的方法来做到这一点。

从表面上看,这个程序的时间复杂度为 O(nlogn * 2^n)。这很糟糕。 在检查它们是否符合大小标准之前,我试图计算每个子集。有什么方法可以通过仅迭代 nCk 次来找到子集的数量?

nCk 是我们希望找到的组合数。其中 nCk = n!/(k!*(n-k)!)

【问题讨论】:

    标签: java performance subset combinatorics lexicographic


    【解决方案1】:

    您可以直接按正确的顺序生成它们(伪代码):

    for(i1 =  0 + 1; i1 <= n-k+1; ++i1)
    for(i2 = i1 + 1; i2 <= n-k+2; ++i2)
    for(i3 = i2 + 1; i3 <= n-k+3; ++i3)
    for(i4 = i3 + 1; i4 <= n-k+4; ++i4)
    ....
    for(ik = i? + 1; ik <= n-k+k; ++ik){
        output = [i1, i2, i3, ..., ik];
    }
    

    由于您不是动态创建代码,您可以通过递归来实现,如下所示:

    private static void Iterate(int[] outp, int n, int k, int actIndex, int lastVal)
    {
        if (actIndex > k)
        {
            System.out.println(Arrays.toString(outp));
            return;
        }
    
        for (int i = lastVal + 1; i <= n - k + actIndex; ++i)
        {
            outp[actIndex - 1] = i;
            Iterate(outp, n, k, actIndex + 1, i);
        }
    }
    

    然后调用它:

    int n = 4;
    int k = 2;
    Iterate(new int[k], n, k, 1, 0);
    

    输出:

    [1, 2]
    [1, 3]
    [1, 4]
    [2, 3]
    [2, 4]
    [3, 4]
    

    【讨论】:

    • 据我所知,您实际上是在每次递归调用中设置最左边的元素。一旦达到大小限制,就将集合添加到某个持有数据结构并返回递归树。是这样吗?
    • @AdityaSatyavada 我不确定我是否理解正确。我添加了实现,如果您遇到麻烦,它应该会有所帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-06
    • 2011-05-20
    相关资源
    最近更新 更多