我正在过滤一个Set,基于同一个Set的其他值,更确切地说,过滤掉完全包含在另一个集合中的集合
Set(Set(1, 2), Set(1, 3), Set(1, 4), Set(1, 2, 3))
这将导致:
Set(Set(1, 4), Set(1, 2, 3))
1,2和2,3完全包含在最后一组中(基本上,我只想要该批次中最大的子集)
我已经提出了这段代码,它可以解决这个问题:
def filterIncluded(data: Set[Set[Int]]): Set[Set[Int]] = {
data.filterNot{ s =>
data.exists(x => x.size > s.size && s.forall(x.contains))
}
}
除了一个问题外,效果很好:效率非常低,我的实际数据集将拥有数百万套,每套都有2到100个值
有没有办法让它更快? (使用其他类型的集合,不同的方法调用,更改循环方式等)。
答案 0 :(得分:2)
一般来说,你不能做得比N ^ 2更好,因为你正在一个更大的空间中寻找碰撞,而这个空间并没有以任何常规的方式受到约束。
但是你可能不解决这个问题。您的数据可能有一个特定的结构。
例如,如果数字大致是随机的,您可以计算每个数字的出现次数;如果数字只出现一次,则包含它的集合不能是严格的子集。如果您只有一个小数字,只需像上面那样强行搜索,您就会知道哪些是唯一的。如果您开始使用该区别号码开始获取大量集合(如果数字大致是随机的,则不太可能,但假设您这样做),您可以再次根据第二个数字进行细分。使用您的示例:
data.toList.flatMap(_.toList).groupBy(identity).map{
case (k,vs) => k -> vs.length
}
// Gives counts: 1 -> 4, 2 -> 2, 3 -> 2, 4 -> 1
// Pick out the set with a 4: it is unique
// Pick out sets with a 2: Set(1, 2), Set(1, 2, 3)
// Run your algorithm to discard Set(1,2)
// Pick out sets with a 3: Set(1, 3), Set(1, 2, 3)
// Run your algorithm to discard Set(1,3)
// Pick out sets with a 1: only Set(1, 2, 3) remains, keep it
或者,如果你可以拥有任何Int,但实际上往往有一堆相似的数字,你可以构建相当于后缀树的集合。从一个集合开始,这是所有数字的联合。然后,对于每个元素,列出具有该元素的每个集合。然后,在该列表下,再次通过第二个元素将其分解。只要你达到一个实际拥有全套的级别,并且列表是非空的,你就可以丢弃整套。
1 -> Set(1, 2, 3), Set(1, 2), Set(1, 3), Set(1, 4)
2 -> Set(1, 2, 3), Set(1, 2)
But we're _at_ 1,2 so
throw away Set(1, 2)
only Set(1, 2, 3) is left--keep it
3 -> Set(1, 2, 3); Set(1, 3)
We're at 1,3 so
throw away Set(1, 3)
the other set is already kept
4 -> Set(1, 4)
Oh, there's only one. Keep it.
答案 1 :(得分:1)
我能想到的第一个改进是:
def filterIncluded(data: Set[Set[Int]]): Set[Set[Int]] = {
val undecided = data.toList.sortBy(_.size).reverse
undecided.foldLeft(List.empty[Set[Int]]){ case (goodSets, s) =>
if(goodSets.forall(goodSet => !s.forall(goodSet contains _))) s :: goodSets
else goodSets
}.toSet
}
排序是NLogN,但是您只需将每个元素与已经证明良好的元素进行比较,因为您只能是较大或相同大小的集合的适当子集。它仍然是N ^ 2,但是我认为它的效率稍高一些。
或者你可以做这个更复杂的事情,实际上听起来像其他人的答案,你维护一个元素的地图到包括它的好集。然后在检查一个新集时,您可以获取包含第一个元素的集合,然后为每个后续元素获取哪些集合具有该集合并获取交集,直到您有一个空交集(没有任何超集)或者您运行元素之外(剩下的一切都是超集)。这可能是一个丑陋的实现:
def filterIncluded(data: Set[Set[Int]]): Set[Set[Int]] = {
def isGood(s: Set[Int], goodSets: Map[Int, Set[Set[Int]]]): Boolean = goodSets.get(s.head) match {
case None => true
case Some(sets) => _isGood(s.tail, sets, goodSets)
}
def _isGood(s: Set[Int], potentialSupersets: Set[Set[Int]], goodSets: Map[Int, Set[Set[Int]]]): Boolean = {
// println(s"s($s)\npotentialSupersets($potentialSupersets)\ngoodSets($goodSets)\n")
goodSets.get(s.head) match {
case None => true
case Some(sets) =>
(s.tail.isEmpty, potentialSupersets & sets) match {
case (true, remaining) if remaining.nonEmpty => false
case (false, remaining) if remaining.nonEmpty => _isGood(s.tail, remaining, goodSets)
case _ => true
}
}
}
def addToGoodSets(s: Set[Int], goodSets: Map[Int, Set[Set[Int]]]): Map[Int, Set[Set[Int]]] = {
s.foldLeft(goodSets){case (g, i) => g + (i -> (g.getOrElse(i, Set.empty)+s))}
}
val undecided = data.toList.sortBy(_.size).reverse
// println("UNDECIDED: "+undecided)
undecided.foldLeft(Map.empty[Int, Set[Set[Int]]]){ case (goodSets, s) =>
if(isGood(s, goodSets)) addToGoodSets( s, goodSets)
else goodSets
}.values.flatten.toSet
}
老实说,分析什么时候比其他任何东西都要好,我有点问题,但是你去了。你能告诉我无聊吗?