【问题标题】:k-combinations of a set of integers in ascending size order按大小升序排列的一组整数的 k 组合
【发布时间】:2010-04-09 16:19:20
【问题描述】:

编程挑战:给定一组整数 [1, 2, 3, 4, 5] 我想在 Java 中以 升序 生成所有可能的 k 组合强>;例如

[1], [2], [3], [4], [5], [1, 2], [1, 3] ... [1, 2, 3, 4, 5]

生成所有组合然后对它们进行排序的递归解决方案相当容易,但我想有一种更有效的方法可以消除对额外排序的需求。

【问题讨论】:

  • 发布一些代码?根据您的实现方式,它可能只需要对您已有的内容进行少量修改。例如,您正在使用堆栈,对吗?在每个递归步骤中,从 stack[recursive step - 1] + 1 而不是从 0 开始迭代。
  • 如果我发布一些代码,它将破坏编程挑战:-)
  • 很抱歉,但这并不是什么大挑战 :)。我已经为高中 CS 写了数百次。
  • 如果您使用堆栈(如您所建议的那样),结果将不会按大小升序排列。
  • @IVlad,当你这样说时,听起来你每次都必须从头开始:P

标签: java algorithm


【解决方案1】:

只要iterative deepening search

void comb(int... items) {
    Arrays.sort(items);
    for (int k = 1; k <= items.length; k++) {
        kcomb(items, 0, k, new int[k]);
    }
}
public void kcomb(int[] items, int n, int k, int[] arr) {
    if (k == 0) {
        System.out.println(Arrays.toString(arr));
    } else {
        for (int i = n; i <= items.length - k; i++) {
            arr[arr.length - k] = items[i];
            kcomb(items, i + 1, k - 1, arr);
        }
    }
}

然后打电话给comb(10,20,30)。它将打印:

[10]
[20]
[30]
[10, 20]
[10, 30]
[20, 30]
[10, 20, 30]

【讨论】:

  • 酷 - 我以前从未听说过这种搜索技术。
  • 它是 BFS 和 DFS 的混蛋。
  • 这是一个很好的起点,我意识到我可以通过记住一代人以前的状态来改进它(见我的回答)。
【解决方案2】:

有两种解释“升序”要求的方法。更宽松的解释是“在每个列表中,整数应该按升序出现”。更严格的解释是“列表需要按顺序给出”。我想这就是你想要的,但我想出了一种简单的迭代方法来满足更宽松的要求。

对于 n 个项目,计算所有 n 位数字。如果与某个项目对应的位存在,那么它就在结果列表中。

public static void displaySubsets(List<Integer> sortedInts) {
    int n=sortedInts.size();
    long combinations = 1 << n;
    for (int setNumber=0; setNumber<combinations; setNumber++) {
      List<Integer> aResult = new ArrayList<Integer>();
      for (int digit=0; digit<n; digit++) {
        if ((setNumber & (1<<digit)) > 0) {
          aResult.add(sortedInts.get(digit));
        }
      }
      System.out.println(aResult.toString()+", ");
    }
  }

1,2,3,4,5 的结果是: [], [1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3], [4], [1, 4], [2, 4], [1, 2, 4], [3, 4], [1, 3, 4], [2, 3, 4], [1, 2, 3, 4], [5], [1, 5], [2, 5], [1, 2, 5], [3, 5], [1, 3, 5], [2, 3, 5], [1, 2, 3, 5], [4, 5], [1, 4, 5], [2, 4, 5], [1, 2, 4, 5], [3, 4, 5], [1, 3, 4, 5], [2, 3, 4, 5], [1、2、3、4、5]

是的,我知道,我因为不使用递归而失分。

【讨论】:

  • 不错的尝试,但我实际上是按大小升序查找输出列表。
【解决方案3】:

我对此进行了更多思考,并意识到使用动态编程方法可以有效地完成它。下面是我生成的迭代解决方案,它使用Queue 来保持状态(尽管可以使用Stack 代替)。

我相信这比递归迭代深化搜索效率更高,因为它不会涉及在生成过程中重新访问现有状态;它使用队列记住以前的状态,并使用这些状态来生成连续的状态。

性能比较

Algorithm                    | 5 elems | 10 elems | 20 elems
--------------------------------------------------------------------------
Recursive (#recursions)      | 62      | 2046     | 2097150
Dynamic   (#loop iterations) | 32      | 1024     | 1048576

代码

public class Test {
    private static class Pair {
        private final List<Integer> result;
        private final int index;

        private Pair(List<Integer> result, int index) {
            this.result = result;
            this.index = index;
        }

        public List<Integer> getResult() {
            return result;
        }

        public int getIndex() {
            return index;
        }
    }

    public static void main(String[] args) {
        List<Integer> items = Arrays.asList(1, 2, 3, 4, 5);
        foo(items);
    }

    private static void foo(List<Integer> items) {
        Queue<Pair> queue = new LinkedList<Pair>();
        queue.add(new Pair(Collections.<Integer>emptyList(), 0));

        while (!queue.isEmpty()) {
            Pair pair = queue.poll();

            System.err.println(pair.getResult());

            if (pair.getResult().size() < items.size()) {
                for (int i=pair.getIndex(); i<items.size(); ++i) {
                    List<Integer> copy = new LinkedList<Integer>(pair.getResult());
                    copy.add(items.get(i));
                    queue.add(new Pair(copy, i + 1));
                }
            }
        }
    }
}

【讨论】:

  • 这是 BFS,因此使用指数空间(即非常低效)。 DFS 使用线性空间(高效),但不按大小升序打印。这就是创建 IDDFS 的原因:它具有两者的特性,因为它有效地利用了空间,但仍然以“级别顺序”访问(即在您的场景中以升序大小顺序打印)。另外,请阅读有关 IDDFS 复杂性的维基百科文章。它只是 BFS 的两倍(这也是您的数字所暗示的),可以更有效地利用空间。这是 BFS 和 DFS 之间的经典时间/空间权衡,中间是 IDDFS。
【解决方案4】:

生成组合比排序花费的时间要长得多,并且在给定 n*log(n) 排序时间的情况下对 100,000 个数字进行排序并不需要那么长时间。您正在预优化。这很糟糕。

【讨论】:

  • 100,000 是一个四舍五入的数字。如果数字可以重复,则您将受到 5**5 ~= 3125 的限制,这是微不足道的数量。
  • 同意过早的优化是浪费时间,但这与其说是现实世界的问题,不如说是产生更高效算法的挑战。
  • 不同意。对 n 个项目进行排序可能需要 n*log(n) 时间。我上面的回答恰好在那个时间生成它们(以错误的顺序),而不是“更长”。请记住,生成 n 个项目的组合可能需要 2^n 时间,但您必须确定 n 是原始列表中的项目数,还是结果中的列表总数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-21
  • 1970-01-01
  • 2019-04-14
  • 1970-01-01
  • 1970-01-01
  • 2013-06-21
相关资源
最近更新 更多