我有一个变量如下:
var L_s = collection.mutable.Set[collection.mutable.Set[Int]]()
我希望找到不同大小的集合的联合。
请注意:当我们想要找到大小为k的联合时,L_s中的所有集合将具有与k-1相同的大小。
截至目前,我正在做以下事情:
for(i <- L_s){
for (j <- L_s){
if((i.union(j)).size == k)
{
res.+=(i.union(j))
}
}
}
如果L_s
有多套,此操作会耗费大量时间。我想知道进行此操作的最有效方法是什么。
答案 0 :(得分:1)
由于union
是可交换的(a union b == b union a
),因此您需要执行两倍的操作,并且在找到目标大小时再次重复union
操作,并且每个Set
都会获得union
。可以消除这些低效率。
L_s.toVector.combinations(2).map(x => x(0) union x(1)).filter(_.size == k).toSet
答案 1 :(得分:0)
加速算法基本上有两种机会:
加快两套
减少所需的比较总数
通过比较,我的意思是检查两个集合的并集是否具有大小k
。
第一个是更简单的部分,我在之前的回答中提到过。您不必实际计算联合并检查其大小。知道交叉口的大小(不是实际的交叉口,只有它的大小)就足够了。运行第一组的元素并计算第二组中的元素数量(非常有效的查找)。如果交叉点的大小为k - 2
,则会发现一对符合您要求的集合。每当找到不在第二组中的两个元素时,你应该打破循环,因为你永远不会从那里到达交叉点大小k - 2
。
这是有效的,因为具有required属性的两个集合具有共同的所有元素,除了每个集合中的一个。这意味着集合1恰好有一个元素不在集合2中,反之亦然。
您还可以利用此属性来限制比较次数。这个想法如下。您的集合包含可以订购的整数。如果一对集合符合条件并且您只考虑每个集合中最低的两个整数,那么我们称之为前缀,前缀至少有一个共同的整数。其他任何事情都与您的要求相矛盾。
您现在可以提前将所有集合的最低和第二低整数编入索引。即,您构建了两个Map[Int, Set[Set[Int]]
类型的地图,我们称之为setsByLowestMember
和setsBySecondLowestMember
。当你现在循环遍历所有集合以进行测试时,不是针对每个其他集合测试集合,而是仅针对那些具有最低或第二低成员等于当前集合的最低或第二低的集合进行测试。
示例:正在考虑的当前(有序)集合为{ 3, 7, 9, ...}
。您检查所有setsByLowestMember(3)
,setsByLowestMember(7)
,setsBySecondLowestMember(3)
和setsBySecondLowestMember(7)
。
根据您的数据,这可能会显着加快您的算法速度。但是,如果您的集合通常具有非常大的交叉点,则可能没有多大帮助。如果有帮助,可以进一步改进(使用上述方法仍然进行了两次检查)。