我希望这是这个问题的正确位置。
我编写了一些代码来改进2D模拟的碰撞检测。该算法的一部分是将对象列表与它们所属的特定2D区域相关联。函数f: (Seq[Box], Seq[T]) => Map[Box, Seq[T]]
。
我首先使用原生Scala函数实现它,例如map
,groupBy
,...但是我还需要它来返回空Seq[T]
和groupBy
的框不这样做,所以我写了一个递归函数,在我看来应该比前一个更快(这可能是我不知道更好的方法,但这不是主要问题)。
事实证明,我的尾递归实现在计算上比使用Scala库的实现慢,它的执行时间比另一个(基准)更快。我不明白为什么,有人能指出我的原因吗?
def spreadAcrossFast[T](
nodes: Seq[Box],
objects: Seq[T]
) = {
// I create the pairs (reference box, objects that intersects the box)
// intersects() is called nodes.size * object.size times
val assigned = for (
b ← nodes;
s ← objects if intersects( b, s )
) yield ( b, s )
// Group by Box and clean the format of the association above
assigned
// Should be O(n*Log n)
.groupBy( _._1 )
// Maximum nodes.size iterations
.map { x ⇒
// Can consider objects.size / nodes.size iterations
( x._1, x._2.map( _._2 ) )
}
}
def spreadAcrossSlow[T](
nodes: Seq[Box],
objects: Seq[T],
compact: Boolean = true // If compact == true, don't include in the output Box that
) = { // have empty content
@tailrec
def loop( boxes: Seq[Box], acc: Map[Box, Seq[T]] ): Map[Box, Seq[T]] = boxes match {
// End of the boxes, return the accumulator
case Nil ⇒ acc
// Get the objects that intersect the box and add them to the accumulator
case b +: bs ⇒
// Every call it goes through objects.size items
// intersects() is called nodes.size * object.size times
val objInBox = objects.filter( intersects( b, _ ) )
val newAcc = if ( objInBox.isEmpty && compact ) acc else acc + ( ( b, objInBox ) )
loop( bs, newAcc )
}
// nodes.size iterations
loop( nodes, Map.empty[Box, Seq[T]] )
}
Seq
是List
的一个实例。
从我的观点来看,spreadAcrossFast
的迭代次数多于spreadAcrossSlow
,而最昂贵的操作intersects()
被称为相同的次数,nodes.size * objects.size < / p>
更新:即使使用也没有运气(但代码更清晰):
def spreadAcrossSlow[T: SpatialIndexable](
nodes: Seq[Box], objects: Seq[T], compact: Boolean
): Map[Box, Seq[T]] = {
val acc = scala.collection.mutable.HashMap[Box, Seq[T]]()
for ( b ← nodes ) {
val objInBox = objects.filter( intersects( b, _ ) )
if ( objInBox.nonEmpty || !compact ) acc += ( ( b, objInBox ) )
}
acc.toMap
}