检查大量圆的碰撞的最佳方法是什么? 检测两个圆之间的碰撞非常容易,但是如果我们检查每个组合,则 O(n 2)绝对不是最佳解决方案。
我们可以假设圆对象具有以下属性:
速度是恒定的,但是方向可以改变。
我提出了两种解决方案,但也许有一些更好的解决方案。
解决方案1将 整个空间分成重叠的正方形,并仅检查与同一正方形中的圆是否发生碰撞。正方形需要重叠,因此当圆从一个正方形移动到另一个正方形时不会有问题。
解决方案2 在开始时,需要计算每对圆之间的距离。 如果距离很小,那么这些对将存储在某个列表中,并且我们需要在每次更新中检查冲突。 如果距离很大,则我们存储更新,之后可能会发生冲突(可以计算出来,因为我们知道距离和速度)。它需要存储在某种优先级队列中。在需要重新计算先前计算的更新数量之后,然后执行相同的步骤- 将其放在列表中或再次放入优先级队列中。
回答马克·拜尔斯(Mark Byers)问题
是为了游戏吗? 它是用于模拟,但也可以视为游戏
您是否要每n毫秒重新计算一次新位置,并且此时还要检查是否有碰撞? 是的,两次更新之间的时间是恒定的。
您是否要查找第一次/每次碰撞发生的时间? 不,我想查找每一次碰撞并在发生碰撞时进行“处理”。
准确性有多重要? 这取决于您所说的准确性。我需要检测所有碰撞。
如果很小的快速移动的圆圈偶尔会穿过彼此,这是一个大问题吗? 可以假设速度太小以至于不会发生。
我假设您正在执行简单的硬球分子动力学模拟,对吗?我在蒙特卡洛(Monte Carlo)和分子动力学模拟中多次遇到相同的问题。关于模拟的文献中经常提到这两种解决方案。我个人更喜欢 解决方案1 ,但稍加修改。
解决方案1 将您的空间划分为不重叠的矩形单元。因此,当您检查一个圆是否发生碰撞时,您会在第一个圆所在的单元格中查找所有圆,并在每个方向上查找X个单元格。我尝试了许多X值,发现X = 1是最快的解决方案。因此,您必须在每个方向上将空间划分为等于:
Divisor = SimulationBoxSize / MaximumCircleDiameter; CellSize = SimulationBoxSize / Divisor;
除数应大于3,否则将导致错误(如果值太小,则应扩大仿真框)。 然后您的算法将如下所示:
如果正确书写,则可能会带来 O(N) 复杂性,因为9个像元(在2D中)或27个像元(在3D中)内的最大圆数对于任何总数的圆都是恒定的。
解决方案2 通常,这是这样完成的:
R < R_max
T_update = R_max / V_max
T_update
通常,可以通过添加另一个列表R_max_2 > R_max以及带有其自身的T_2到期时间的列表来改进此解决方案。在此解决方案中,此第二列表用于更新第一列表。当然,在T_2您必须更新所有列表 O(N ^ 2)之后 。还要注意这一点T和T_2时间,因为如果碰撞可以改变速度,那么那些时间就会改变。同样,如果在系统中引入一些先兆,那么也会引起速度变化。
R_max_2 > R_max
T_2
T
解决方案1 + 2 您可以将列表用于冲突检测,将单元格用于更新列表。一本书中写道,这是最好的解决方案,但是我认为,如果您创建小单元格(如我的示例),那么 解决方案1 会更好。但这是我的意见。
其他内容 您还可以执行其他操作以提高仿真速度:
r = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + ...)
r^2
(x1-x2)*(x1-x2)
x*x
r_collision^2
y*y
对于硬球,还有一种有效的算法,它不及时执行步长,而是在时间上寻找最近的碰撞并跳转到该时间并更新所有位置。对于不太可能发生碰撞的不密集系统,这可能会很好。