我需要帮助找到一个有效的算法来解决这个问题:
给定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个集合的交集。不过,我担心这种方法的时间效率。
如果你能找到比我天真的方法更好的东西,伪代码的答案将是最有帮助的。
答案 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)
简短:首先收集每个元素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]