见下文进行优化
我认为你把事情复杂化了。蛮力方法会将每个点与其他点进行比较。最坏的情况是 O(r*(s+r)),其中 r 是随机点的数量,s 是起点的数量。
在所有(或大部分)点都可到达的预期情况下,您可以使用队列来降低复杂性。这个想法是,一旦你确定一个点是可到达的,你就不必再检查它是否可以从其他点到达。但是,您必须检查是否可以从它到达其他点。
当你开始时,你所有的随机点都是“未知的”。也就是说,他们从未被访问过。但是一旦访问了一个点,它就不再是未知的:我们知道它可以到达。因此,当您第一次访问某个点时,您会将其从未知领域移至前沿。然后你穿过边界,在未知中寻找触手可及的点。
大体思路是:
unknown = list of random points
frontier = new queue()
add all source cells to frontier
while (!unknown.isEmpty() && !frontier.isEmpty())
{
point = frontier.dequeue()
for each unknown_point
{
if (dist(point, unknown_point) < distance)
{
remove unknown_point from unknown list,
and add to frontier queue
}
}
}
if (!unknown.IsEmpty())
{
// there are still points in the unknown,
// which means that not all points are reachable.
}
在最坏的情况下,该算法将针对每个随机点测试每个起点,并针对每个其他随机点测试每个随机点,因此复杂度为 O(r*(s + r)),其中 r 是随机数点和s是起点的数量。但请理解,最坏的情况只会出现在非常稀疏的图形中,或者无法到达大量点时。
请注意,如果unknown 是一个常见的列表数据结构或数组,“从未知列表中删除未知点”本身可能是一个 O(r) 操作。一个有用的优化是使unknown 成为一个队列,并像这样修改你的内部循环:
point = frontier.dequeue()
unknown_count = unknown.count()
while (unknown_count > 0)
{
unknown_point = unknown.dequeue()
--unknown_count
if (dist(point, unknown_point) < distance)
{
// within range, add to frontier
frontier.enqueue(unknown_point)
}
else
{
// not reachable. Put it back in the unknown.
unqnown.enqueue(unknown_point)
}
}
优化
您可以通过合并 Peter de Rivaz 推荐的“分箱”优化来降低预期情况的复杂性。这通过将搜索限制在相邻的 bin 中来限制您必须为每个边界点检查的点数:这是唯一可能到达未知点的地方。基本上,您创建网格以覆盖所有随机点。比如:
0 1 2 3 4 5
-------------------------------------------------------
| .. | | . . | | | |
A | . . | | . . | . | | . . |
| . | | . | . . | | . |
-------------------------------------------------------
| | . .| | . . . | |. |
B | | . | . | . . | | |
| | . | | . | | .|
-------------------------------------------------------
| . . | | . | | | . |
C | . | | . | | | . |
| | | . | | | . |
-------------------------------------------------------
| . | | . . | . | . . | . . |
D | | | . | . | . | . . . |
| . | | . | | . | . |
-------------------------------------------------------
| |. . | . . | | | |
E | | . | . | | | |
| | . | . . | | | |
-------------------------------------------------------
| | . | | . . | . . | . . |
F | | . | | . . | . . | . . |
| | . . | | . | . | . |
-------------------------------------------------------
如果您的距离阈值是dist,那么每个方格的每边都是dist 个单位。
那么,我们知道,网格 B3 中的一个点只能在九个相邻方格中的dist 个点的单位内。所以我们不必针对网格 F5 中的点进行测试。请注意,并非 A3 中的所有点都一定可以从 B3 中的某个点到达,但它们可能。事实上,我们不能保证 B3 中的每个点都与 B3 中的每个其他点相邻。考虑一个只包含两个点的网格:一个在最左上角,一个在最右下角。这两点之间的距离将大于dist。
根据点的密度,您可能需要某种稀疏数据结构来存储箱。
您要做的第一件事是将随机点装箱。穿过随机点以找到最顶部和最左侧的坐标。这成为你的原点。 Bin A0 的左上角位于 (topmost, leftmost)。然后,您可以遍历所有随机点并将它们添加到 bin。
之后,算法专注于 bin,而不是随机点数组:
frontier = new queue()
add source points to frontier
while (!allBinsAreEmpty() && !frontier.IsEmpty())
{
point = frontier.dequeue()
sourceBin = determine bin that point is in
adjacentBins = getAdjacentBins(sourceBin.x, sourceBin.y)
for each adjacent bin
{
for each binPoint in bin
{
if distance(point, binPoint) <= dist
{
frontier.enqueue(binPoint)
bin.Remove(binPoint)
}
}
if (bin is empty)
remove bin
}
}
if (!allBinsAreEmpty())
{
// there are unreachable points
}
获取相邻的垃圾箱非常简单:
getAdjacentBins(binx, biny)
{
adjacentBins[] = [bins[binx, biny]]
if (bins[binx-1, biny-1] != null) adjacentBins += bin[binx-1, biny-1]
if (bins[binx-1, biny] != null) adjacentBins += bin[binx-1, biny]
if (bins[binx-1, biny+1] != null) adjacentBins += bin[binx-1, biny+1]
if (bins[binx, biny+1] != null) adjacentBins += bin[binx, biny+1]
....
return adjacentBins
}