我需要计算一个集合的所有排列,我有一个代码,但问题是它是线性的,需要很多时间。
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;
}
我试图让计算并行运行。
我可以选择使用递归来编写逻辑,然后并行执行递归调用,但我不确定如何做到这一点。
感谢任何帮助。
答案 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>>
进行散列的情况下工作。