n组的所有组合的交集

时间:2014-09-24 23:27:49

标签: algorithm data-structures set big-o

我需要帮助找到一个有效的算法来解决这个问题:

给定n个未排序的整数集,找到n及其交叉点的所有可能组合。

例如:

输入(n = 3):

设置1 = 1,10,6,11,14,3 设置2 = 3,7,11,9,5 设置3 = 11,6,9,1,4

输出:

Set 1& 2:3,11
设置1& 3:1,6 设置2& 3:9,11
设置1,2和& 3:11

我在考虑首先找到所有可能的集合组合,然后使用算法查找找到here的n个集合的交集。不过,我担心这种方法的时间效率。

如果你能找到比我天真的方法更好的东西,伪代码的答案将是最有帮助的。

3 个答案:

答案 0 :(得分:16)

这是一个受http://research.google.com/archive/mapreduce.html启发的解决方案(如果需要,可以以分布式方式编写)。

将集合中的所有元素映射到对[element, set]。按元素对此列表进行分组。 (你可以只排序和拉出元素。或者你可以创建一个数组的散列,其键是元素,值是找到元素的集合列表。)然后,对于每个元素,发出[组合的集合] ,元素]。通过组合分组。现在,对于每个非空组合,您只需读取其中的元素即可。

如果您希望使用真实的map-reduce分配计算,则第一个映射将映射到元素的键和set的值。第一个reduce只是按元素存储它所在的集合列表。第二个映射将为每个元素为每个元素组合发出一个键,其中元素为值。而第二个减少将存储您的最终答案。

详细介绍您的示例可能会有所帮助。你从:

开始
Set 1 = 1, 10, 6, 11, 14, 3
Set 2 = 3, 7, 11, 9, 5
Set 3 = 11, 6, 9, 1, 4

您将其映射到列表:

[1, Set 1], [10, Set 1], [6, Set 1], [11, Set 1], [14, Set 1], [3, Set 1],
[3, Set 2], [7, Set 2], [11, Set 2], [9, Set 2], [5, Set 2],
[11, Set 3], [6, Set 3], [9, Set 3], [1, Set 3], [4, Set 3],

现在逐个元素(我通过排序手工完成)得到:

1: Set 1, Set 3
3: Set 1, Set 2
4: Set 3
5: Set 2
6: Set 1, Set 3
7: Set 2
9: Set 2, Set 3
10: Set 1
11: Set 1, Set 2, Set 3
14: Set 1

现在我们进行第二次映射(跳过仅在一组中的元素)得到:

[(Set 1, Set 3), 1],
[(Set 1, Set 2), 3],
[(Set 1, Set 3), 6],
[(Set 2, Set 3), 9],
[(Set 1, Set 2), 11], [(Set 1, Set 3), 11], [(Set 2, Set 3), 11], [(Set 1, Set 2, Set 3), 11]

通过组合组合我们得到:

(Set 1, Set 2): 3, 11
(Set 1, Set 3): 1, 6, 11
(Set 2, Set 3): 9, 11
(Set 1, Set 2, Set 3): 11

(这与你建议的答案不同,因为你错过了11在集合1和3的交集中。)

答案 1 :(得分:1)

使用您的示例,请注意

  

1 n 2 n 3

也是

  

(1 n 2)n 3

因此,如果你缓存你的(1 n 2)解决方案,你可以重复使用它,这样计算1 n 2 n 3只需要一个额外的交集计算。通常情况下,如果您的示例中有四种可能的集合组合,那么如果您保存并重复使用先前的结果,则最终只需要完成四个交叉点。

答案 2 :(得分:0)

Java 10 中的实现

简短:首先收集每个元素Map<Integer,Set<Integer>>的交点图,然后为此图收集交点Map<Set<Integer>,Set<Integer>>的交点图,然后将较大的交点集附加到较小的交点集,如果它们相交。

// List<Set<Integer>>
var setList = List.of(
        Set.of(1, 10, 6, 11, 14, 3),
        Set.of(3, 7, 11, 9, 5),
        Set.of(11, 6, 9, 1, 4));
// TreeMap<Integer,List<Map.Entry<TreeSet<Integer>,TreeSet<Integer>>>>
var map = IntStream
        // iterate over indices of
        // the list elements, i.e. sets
        .range(0, setList.size())
        // for each set iterate over its elements
        // and map pairs 'element=set'
        .mapToObj(i -> setList.get(i).stream()
                // key - element, value - index
                // of the set starting from '1'
                .map(e -> Map.entry(e, i + 1)))
        // Stream<Map.Entry<Integer,Integer>>
        .flatMap(Function.identity())
        // group indices of the sets by their elements,
        // i.e. accumulate the intersections
        .collect(Collectors.toMap(
                // key - element
                Map.Entry::getKey,
                // value - set of the indices of the sets
                e -> new TreeSet<>(List.of(e.getValue())),
                // accumulate the indices of the sets
                (e1, e2) -> {
                    e1.addAll(e2);
                    return e1;
                }))
        // Stream<Map.Entry<Integer,TreeSet<Integer>>>
        .entrySet().stream()
        // filter out unique elements
        // without intersections
        .filter(e -> e.getValue().size() > 1)
        // intermediate output
        //1=[1, 3]
        //3=[1, 2]
        //6=[1, 3]
        //9=[2, 3]
        //11=[1, 2, 3]
        .peek(System.out::println)
        // group the elements of the
        // sets by their intersections
        .collect(Collectors.toMap(
                // key - set of the indices of the sets
                Map.Entry::getValue,
                // value - set of the intersecting elements
                e -> new TreeSet<>(Set.of(e.getKey())),
                // accumulate the intersecting elements
                (e1, e2) -> {
                    e1.addAll(e2);
                    return e1;
                }))
        // Stream<Map.Entry<TreeSet<Integer>,TreeSet<Integer>>>
        .entrySet().stream()
        // intermediate output
        //[1, 2]=[3]
        //[1, 3]=[1, 6]
        //[2, 3]=[9]
        //[1, 2, 3]=[11]
        .peek(System.out::println)
        // group by the number of intersections
        .collect(Collectors.groupingBy(
                // size of the set of indices
                e -> e.getKey().size(),
                // sort by number of intersections in reverse order
                () -> new TreeMap<>(Comparator.<Integer>reverseOrder()),
                // list of map entries
                Collectors.toList()));
// intermediate output
map.forEach((k, v) -> System.out.println(k + "=" + v));
//3=[[1, 2, 3]=[11]]
//2=[[1, 2]=[3], [1, 3]=[1, 6], [2, 3]=[9]]
// process the lists of intersections, i.e. map entries
map.forEach((key, value) -> value
        // for each entry process the values of other
        // entries with less number of intersections
        .forEach(entry -> map.tailMap(key, false)
                // Stream<List<Map.Entry<TreeSet<Integer>,TreeSet<Integer>>>>
                .values().stream()
                // Stream<Map.Entry<TreeSet<Integer>,TreeSet<Integer>>>
                .flatMap(List::stream)
                // if the intersection set of the current entry contains
                // all intersections from the set of another entry
                .filter(other -> entry.getKey().containsAll(other.getKey()))
                // then add all intersecting elements of
                // the current entry to another entry
                .forEach(other -> other.getValue().addAll(entry.getValue()))));

// final output
map.forEach((k, v) -> v.forEach(entry -> System.out.println(
        "Sets: " + entry.getKey() + " contains values: " + entry.getValue())));

//Sets: [1, 2, 3] contains values: [11]
//Sets: [1, 2] contains values: [3, 11]
//Sets: [1, 3] contains values: [1, 6, 11]
//Sets: [2, 3] contains values: [9, 11]