使用Java 8枚举K个元素的组合

时间:2015-02-14 12:13:03

标签: java functional-programming java-8 combinations

给定List<E>的实例,使用Java 8特性,如何构建List<List<E>>枚举原始List的k个元素的所有可能组合?

2 个答案:

答案 0 :(得分:13)

这是我为解决一些欧拉项目问题而写的算法:

public static <E> Stream<List<E>> combinations(List<E> l, int size) {
    if (size == 0) {
        return Stream.of(Collections.emptyList()); 
    } else {
        return IntStream.range(0, l.size()).boxed().
            <List<E>> flatMap(i -> combinations(l.subList(i+1, l.size()), size - 1).map(t -> pipe(l.get(i), t)));
    }
}

private static <E> List<E> pipe(E head, List<E> tail) {
    List<E> newList = new ArrayList<>(tail);
    newList.add(0, head);
    return newList;
}

它需要List<E>和一个尺寸,并返回尺寸size列表的所有组合,作为Stream<List<E>>

这是一个递归算法:我们的想法是计算列表中除第一个之外的所有元素的大小size - 1的组合。当你拥有它们时,你只需在每个组合的开头添加你删除的第一个元素。然后,您继续查找列表中除第一个和第二个之外的所有元素的所有大小size - 1的组合,并且当您拥有它们时,再添加第二个元素。这一直持续到size = 0,您只需返回空组合。请注意,此方法确实返回“重复”(如果返回组合(a, b, c),则不返回(b, c, a)。)

您没有提及是否应包含重复项。修改此算法以包含重复项并不困难,逻辑略有不同。

public static <E> Stream<List<E>> combinationsDupl(List<E> l, int size) {
    if (size == 0) {
        return Stream.of(Collections.emptyList()); 
    } else {
        return l.stream().<List<E>> flatMap(h -> combinationsDupl(subtract(l, h), size - 1).map(t -> pipe(h, t)));
    }
}

private static <E> List<E> pipe(E head, List<E> tail) {
    List<E> newList = new ArrayList<>(tail);
    newList.add(0, head);
    return newList;
}

private static <E> List<E> subtract(List<E> list, E e) {
    List<E> newList = new ArrayList<>(list);
    newList.remove(e);
    return newList;
}

这一次,您遍历输入列表中的所有元素。对于它们中的每一个,您计算删除此元素的列表的组合。然后,当你拥有它们时,再次将它添加到每个组合中。

答案 1 :(得分:2)

这个解决方案不是递归的(这使得它更清晰)和懒惰,但是返回的组合不是通过增加长度来排序的:

public class Combinations {

    public static void main(String[] args) {
        System.out.println(
                getCombinationsStream(Arrays.asList(1, 2, 3))
                        .collect(Collectors.toList()));
        // prints: [[1], [2], [1, 2], [3], [1, 3], [2, 3], [1, 2, 3]]
    }

    public static <T> Stream<List<T>> getCombinationsStream(List<T> list) {
        // there are 2 ^ list.size() possible combinations
        // stream through them and map the number of the combination to the combination
        return LongStream.range(1 , 1 << list.size())
                .mapToObj(l -> bitMapToList(l, list));
    }

    public static <T> List<T> bitMapToList(long bitmap, List<T> list) {
        // use the number of the combination (bitmap) as a bitmap to filter the input list
        return IntStream.range(0, list.size())
                .filter(i -> 0 != ((1 << i) & bitmap))
                .mapToObj(list::get)
                .collect(Collectors.toList());
    }
}

编辑:要只获取特定长度的组合,请在某处添加.filter()。