什么是最简单,最简单的算法,用于查找10 ^ 5阶的完整图的EMST

问题描述 投票:2回答:2

我只想清楚EMST代表欧几里得最小生成树。

基本上,我给了一个带有100k 4D顶点的文件(每行一个顶点)。目标是访问文件中的每个顶点,同时最小化行进的总距离。从一个点到另一个点的距离就是欧几里德距离(如果你在两点之间绘制一条直线,则为距离)。

我已经知道这几乎是旅行推销员问题,这是NP完全,所以我希望近似解决方案。

我想到的第一个近似算法是从文件构造的图中找到MST ......但是,如果它是一个事实,那么即使只是构造文件中的所有边,也需要O(N ^ 2)。完整的图表(我可以从任何一点到另一个点)。鉴于我的输入是N = 10 ^ 5,我的算法将有一个巨大的运行时间,这太慢了......

关于如何计划近似解决方案的任何想法?非常感谢你..

algorithm minimum-spanning-tree np-complete polynomial-approximations
2个回答
1
投票

我假设您实际上想要一个EMST,正如您的标题所暗示的那样,而TSP只是达到目的的一种手段,而不是实际目标本身。两者具有非常不同的限制(TSP限制性更强),因此最佳解决方案非常不同。

概观

我们的想法是,我们想运行一个改进的kruskal算法,该算法将利用k-d树来查找最接近的对,而无需评估每个潜在的边缘。我们可以找到连接组件中每个顶点的最短边,整体最短,并通过该边连接我们连接的组件。正如您将看到的,每次迭代都会连接至少一半的连接组件,因此最多需要完成logn迭代。

最近邻搜索

为了构建EMST,您将需要使用数据结构来查询4D空间中的最近邻居。你可以扩展八叉树以在更高的维度上工作,但我个人会使用k-d树。您可以使用中位数算法在O(nlogn)时间构建k-d树,以找到每个级别的中位数,并且可以在O(logn)时间插入/移除平衡的k-d树。

一旦你构建了一个k-d树,你就会想要查询每个点的最近邻居。然后我们将构造这两个顶点之间的边。这些边缘中的许多将被复制,对于一些顶点ABA的最近邻居可能是B,而B的最近邻居可能是A。我们将通过存储每个顶点所属的连接组件来处理这个问题,并且在通过边连接两个顶点之后,复制边将清楚地连接相同连接组件的两个顶点,因此我们将丢弃它。为了实现这一点,我们将使用不相交集(就像在kruskal算法的许多实现中一样)来为每个顶点分配连接的组件。这也将阻止我们在图中创建循环,这会在MST中引入不必要的边。

合并

但是,当我们构造每个边时,我们需要在检查要保留哪些边以及哪些边连接已连接的顶点之前将其插入到最小堆优先级队列中。这不会影响第一次迭代的结果,但稍后我们需要通过增加距离来处理边缘。然后将所有边缘出列,通过不相交集检查不必要/冗余边缘,将有效边插入MST,并合并各自的不相交集。所有这一切当然都引入了nlogn因子来构造和从最小堆中出列元素(如果我们愿意的话,我们也可以在一个普通的数组中对它们进行排序)。

在第一次添加边缘迭代之后,我们将连接至少一半的MST,可能更多。这是因为对于每个顶点我们添加了一条边,并且每条边最多可以有一个重复,因此我们添加了一些作为vertices / 2边,但与vertices - 1一样多。现在我们已经建成了至少1/2的MST。我们将继续按照以下段落中描述的过程,直到我们总共添加了vertices - 1边缘。

推广NN-Search

为了继续,我们将要构建每个连接组件中的顶点列表,以便我们可以按组迭代它们。这可以在接近线性的时间内完成,因为搜索(也合并)不相交集需要O(α(n))时间(α是逆ackermann函数)并且我们重复n次。一旦我们得到每个连接组件的顶点列表,其余的就相当简单了。我们将采用现有的k-d树,并删除当前连接组件中的所有顶点。然后,我们将查询每个顶点与我们连接组件中每个顶点的最近邻居,并将这些边添加到我们的最小堆中。然后我们将这些顶点添加回k-d树中,并在下一个连接的组件上重复。由于我们插入/删除了总共n元素,这相当于O(nlogn)时间复杂度的平均情况。

现在我们有一个连接我们连接组件的最短潜在边缘的队列,我们​​将按顺序将它们出列,就像插入有效边并合并不相交集一样。出于与以前相同的原因,保证连接至少一半的组件,甚至可能连接所有组件。我们将重复此过程,直到我们将所有顶点连接到一个连接的组件,这将是我们的MST。请注意,因为每次迭代我们将断开连接的组件的数量减半,所以最多需要O(logn)迭代来连接我们MST中的每个顶点(很可能远远少于此)。

备注

总的来说,这需要O(nlog^2(n))时间。然而,可能会远远少于log(n)迭代,所以期望在实践中加速。还要注意R树可能是k-d树的一个很好的替代品 - 但我不知道它们在实践中如何比较。


2
投票

我知道这是二次时间,但我认为你应该考虑使用隐式图形Prim。算法的结构是

for each vertex v
    mindist[v] := infinity
    visited[v] := false
choose a root vertex r
mindist[r] := 0
repeat |V| times
    let w be the minimizer of d[w] such that not visited[w]
    visited[w] := true
    for each vertex v
        if not visited[v] and distance(w, v) < mindist[v]:
            mindist[v] := distance(w, v)
            parent[v] := w

由于使用的存储是线性的,它可能会驻留在缓存中,并且没有奇特的数据结构,因此该算法应该运行得非常快。

© www.soinside.com 2019 - 2024. All rights reserved.