【问题标题】:Finding all partitions of a set in Java在Java中查找集合的所有分区
【发布时间】:2016-03-17 06:15:01
【问题描述】:

我有以下 Python 函数来递归查找集合的所有分区:

def partitions(set_):
    if not set_:
        yield []
        return
    for i in xrange(2**len(set_)/2):
        parts = [set(), set()]
        for item in set_:
            parts[i&1].add(item)
            i >>= 1
        for b in partitions(parts[1]):
            yield [parts[0]]+b

for p in partitions(["a", "b", "c", "d"]):
    print(p)

谁能帮我把它翻译成Java?这是我目前所拥有的:

private static List<List<List<String>>> partitions(List<String> inputSet) {
    List<List<List<String>>> res = Lists.newArrayList();
    if (inputSet.size() == 0) {
        List<List<String>> empty = Lists.newArrayList();
        res.add(empty);
        return res;
    }
    int limit = (int)(Math.pow(2, inputSet.size())/2);
    for (int i = 0; i<limit; i++) {
        List<List<String>> parts = Lists.newArrayList();
        List<String> part1 = Lists.newArrayList();
        List<String> part2 = Lists.newArrayList();
        parts.add(part1);
        parts.add(part2);
        for (String item: inputSet) {
            parts.get(i&1).add(item);
            i >>= 1;
        }
        for (List<List<String>> b: partitions(parts.get(1))) {
            List<List<String>> set = Lists.newArrayList();
            set.add(parts.get(0));
            set.addAll(b);
            res.add(set);
        }
    }
    return res;
}

当使用多个元素执行它时,我得到一个无限递归。

可以在here 找到与此类似的帖子(使用 Ruby)。原始 Python 代码可以在 herehere 找到。

【问题讨论】:

  • 您真的想将输出作为列表列表的列表吗?我认为包含所有超集元素的单个列表可能更容易理解和编码。

标签: java python algorithm


【解决方案1】:

您非常接近正确答案。你说你得到了无限递归,但实际上程序是在最外层循环中无限循环运行的。

与 Python 代码的主要区别在于,在 Python 版本中,i 变量始终在外循环中前进,但在您的 Java 版本中,内循环中的 i &gt;&gt;= 1 语句始终将 i 返回到零。解决这个问题的简单方法是简单地为内部和外部循环使用单独的变量。

一般来说,这就是为什么尝试直接将程序从一种语言翻译成另一种语言是个坏主意。几乎每个程序都有一些在原始语言中有意义的习语在目标语言中会变得奇怪或毫无意义。特别是,Python 代码的正确性依赖于对任意精度整数的隐式提升。这在 Java 中不能很好地工作,因此如果输入集大于 31 个元素,则下面的实现会出现整数溢出。您的示例只有 4 个元素,因此对于这种特定情况,它将产生正确的答案。

这是一个更正的 Java 版本:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Partition {
    private static List<List<List<String>>> partitions(List<String> inputSet) {
        List<List<List<String>>> res = new ArrayList<>();
        if (inputSet.isEmpty()) {
            List<List<String>> empty = new ArrayList<>();
            res.add(empty);
            return res;
        }
        // Note that this algorithm only works if inputSet.size() < 31
        // since you overflow int space beyond that. This is true even
        // if you use Math.pow and cast back to int. The original
        // Python code does not have this limitation because Python
        // will implicitly promote to a long, which in Python terms is
        // an arbitrary precision integer similar to Java's BigInteger.
        int limit = 1 << (inputSet.size() - 1);
        // Note the separate variable to avoid resetting
        // the loop variable on each iteration.
        for (int j = 0; j < limit; ++j) {
            List<List<String>> parts = new ArrayList<>();
            List<String> part1 = new ArrayList<>();
            List<String> part2 = new ArrayList<>();
            parts.add(part1);
            parts.add(part2);
            int i = j;
            for (String item : inputSet) {
                parts.get(i&1).add(item);
                i >>= 1;
            }
            for (List<List<String>> b : partitions(part2)) {
                List<List<String>> holder = new ArrayList<>();
                holder.add(part1);
                holder.addAll(b);
                res.add(holder);
            }
        }
        return res;
    }

    public static void main(String[] args) {
        for (List<List<String>> partitions :
                 partitions(Arrays.asList("a", "b", "c", "d"))) {
            System.out.println(partitions);
        }
    }
}

这是我的 Java 版本的输出:

[[a, b, c, d]]
[[b, c, d], [a]]
[[a, c, d], [b]]
[[c, d], [a, b]]
[[c, d], [b], [a]]
[[a, b, d], [c]]
[[b, d], [a, c]]
[[b, d], [c], [a]]
[[a, d], [b, c]]
[[a, d], [c], [b]]
[[d], [a, b, c]]
[[d], [b, c], [a]]
[[d], [a, c], [b]]
[[d], [c], [a, b]]
[[d], [c], [b], [a]]

【讨论】:

  • 深度有什么用?
  • 我想我是在调试原始代码以查找错误时添加的。它目前的形式似乎未被使用。我将编辑答案以删除额外的参数。
猜你喜欢
  • 2020-05-14
  • 2013-12-30
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-27
相关资源
最近更新 更多