并行计算集合的所有排列

时间:2016-12-08 13:49:11

标签: java recursion parallel-processing combinations java-stream


我需要计算一个集合的所有排列,我有一个代码,但问题是它是线性的,需要很多时间。

public static <E> Set<Set<E>> getAllCombinations(Collection<E> inputSet) {
    List<E> input = new ArrayList<>(inputSet);
    Set<Set<E>> ret = new HashSet<>();
    int len = inputSet.size();
    // run over all numbers between 1 and 2^length (one number per subset). each bit represents an object
    // include the object in the set if the corresponding bit is 1
    for (int i = (1 << len) - 1; i > 0; i--) {
        Set<E> comb = new HashSet<>();
        for (int j = 0; j < len; j++) {
            if ((i & 1 << j) != 0) {
                comb.add(input.get(j));
            }
        }
        ret.add(comb);
    }
    return ret;
}

我试图让计算并行运行。

我可以选择使用递归来编写逻辑,然后并行执行递归调用,但我不确定如何做到这一点。

感谢任何帮助。

1 个答案:

答案 0 :(得分:4)

事实上,没有必要使用递归,这可能适得其反。由于每个组合的创建可以独立于其他组合执行,因此可以使用并行流来完成。请注意,您甚至不需要手动执行位操作:

Data

内部流操作,即迭代比特,太小而不能从并行操作中受益,特别是因为它必须将结果合并为单个public static <E> Set<Set<E>> getAllCombinations(Collection<E> inputSet) { // use inputSet.stream().distinct().collect(Collectors.toList()); // to get only distinct combinations // (in case source contains duplicates, i.e. is not a Set) List<E> input = new ArrayList<>(inputSet); final int size = input.size(); // sort out input that is too large. In fact, even lower numbers might // be way too large. But using <63 bits allows to use long values if(size>=63) throw new OutOfMemoryError("not enough memory for " +BigInteger.ONE.shiftLeft(input.size()).subtract(BigInteger.ONE)+" permutations"); // the actual operation is quite compact when using the Stream API return LongStream.range(1, 1L<<size) /* .parallel() */ .mapToObj(l -> BitSet.valueOf(new long[] {l}).stream() .mapToObj(input::get).collect(Collectors.toSet())) .collect(Collectors.toSet()); } 。但是,如果要生成的组合数量足够大,则并行运行外部流将已经使用了所有CPU内核。

替代方法不是使用并行流,而是返回Set本身而不是收集到Stream<Set<E>>,以允许调用者直接链接使用操作。

顺便说一句,散列整个Set<Set<E>>(或许多)可能非常昂贵,因此最终合并步骤的成本可能会主导性能。返回Set可以显着提高性能。这同样适用于在没有收集组合的情况下返回List<Set<E>>的替代方法,因为这也可以在不对Stream<Set<E>>进行散列的情况下工作。