TSP 算法的 BIG O 时间复杂度

Posted

技术标签:

【中文标题】TSP 算法的 BIG O 时间复杂度【英文标题】:BIG O time complexity of TSP algorithms 【发布时间】:2021-04-30 12:29:07 【问题描述】:

我在 python 中编写了 2 个最近邻算法,我必须通过 O(n) 和 Θ(n) 来分析运行时复杂度。 所以我尝试了几个样本,但我不明白为什么我的一种算法比另一种更快。

这是我的重复最近邻 (RNN) 算法的代码:

def repeated_nn_tsp(cities):
   return shortest_tour(nn_tsp(cities, start) for start in cities)

def shortest_tour(self, tours):
 return min(tours, key=self.tour_length)

nn_tsp 的运行时复杂度为 O(n^2),每个起点都会创建一个新的 NN Tour。通过所有 NN 巡回演出,我必须找到最好的巡回演出。 这就是为什么我认为 RNN 的时间复杂度必须是 T(n)=O(n^3) 和 T(n)=Θ(n^3)。

所以这是我的更改最近邻 (ANN) 算法的代码:

def alter_tour(tour):
    original_length = tour_length(tour)
    for (start, end) in all_segments(len(tour)):
        reverse_segment_if_better(tour, start, end)
    if tour_length(tour) < original_length:
        return alter_tour(tour)
    return tour

def all_segments(N):
    return [(start, start + length) for length in range(N, 2-1, -1) for start in range(N - length + 1)]

def reverse_segment_if_better(tour, i, j):
    A, B, C, D = tour[i-1], tour[i], tour[j-1], tour[j % len(tour)]
    if distance(A, B) + distance(C, D) > distance(A, C) + distance(B, D):
        tour[i:j] = reversed(tour[i:j])

all_segments 的时间复杂度应该是 T(n) = O(1/2 * n^2 - 0.5n) -> O(n^2) 并创建 n^2 个元素。 在循环内通过 all_segments(通过 n^2 个元素)我调用函数 reverse_segment_if_better。我将使用 python 的反转方法,这会导致 O(n) 的时间复杂度。 这就是为什么我认为循环的时间复杂度必须是 O(n^3)。当有更好的游览时,该函数将调用自身递归。我认为改变后的 NN 的时间复杂度为 O(n^4)。对吗?

但是我们遇到了我的问题:我的评估在 100 个城市上运行代码 100 次,结果表明 ANN 平均比 RNN 快,这与我预期的运行时复杂性相反。 (RNN 需要 4.829 秒,而 ANN 只需要 0.877 秒,1x 100 城市。)

那么我在哪里做错了?

提前致谢!

【问题讨论】:

我可以建议使用 Cprofiler 来获取函数调用的计数和时间。 【参考方案1】:

首先我必须说时间复杂性和大 O 符号并不总是正确的,一个算法可能有一个“更好”的运行时间函数,但运行时间仍然比预期慢,或者比另一个最差的函数慢运行时函数,在你的情况下,很难确定什么是最坏的情况来提供算法,我们不能保证你已经做到了!也许这些案例对ANN 算法是“令人愉快的”,而另一个案例被困在某个地方......?这就是为什么仅依靠我们计算的运行时间函数并不总是 100% 正确的原因。

我想说的是,你很可能没有故意在计算中犯错误,因为它们是难以动态分析的函数,或者What kind of input would be the worst, for example


至于“为什么?”:

    当谈到实际的个人运行时间(如您举的 0.877 秒示例)时,归结为我们自己的机器,每台计算机都有自己的幕后运行硬件,并非所有计算机生来都是一样的。

    其次,当我们谈论运行时间复杂度时,我们会像使用 all_segments 函数一样删除低项值,您可以看到您甚至删除了一个负项,这在理论上可以帮助我们减少 ' 的数量操作”。

    在很多情况下,有一些代码效率不高,以至于我们只有在满足特定条件时才费心执行,从而减少了运行时间。

    最后也是最重要的是,当我们谈论分类时 算法成集合,例如我们正在谈论的O(n)O(nlogn) 渐近函数,我们需要放眼大局 当我们向算法提供大量数据时会发生什么, 我假设您没有检查,因为正如您所写,您只运行了 100 个城市。那可能 如果我们看看数以百万计的城市,情况会有所不同。


对于您的代码,我可以注意到多个部分合理地是导致运行时间出现这种“奇怪”差异的原因。首先,在ANN 代码中,更具体地说,在reverse_segment_if_better 函数中,我们并不总是反转列表,只有当某个语句被评估为真实值时。我们无法确定你给算法提供了什么样的输入,因此我只能想象它是否符合第二种算法。

此外,可能是我遗漏了一些东西(因为函数 reverse_segment_if_better / 我们无法查看函数 tour_lengthdistance)但我不知道您是如何想到 O(n^4) 在结束了,好像在做O(n^3):

all_segments- no doubt it is O(n) - returning ~n/2 values 棘手的部分是分析reverse_segment_if_betteralter_tour - 反转只发生在i:j 因此说它有O(n) 并不完全正确 - 因为我们不反转整个之旅(至少,不是start, end 的每个值。

可以肯定地说,可能是没有渐近地检查非常大的数字,您提供了一个输入并且它对这个特定的算法很友好,或者T(n) 的最终形式不够严格。

【讨论】:

以上是关于TSP 算法的 BIG O 时间复杂度的主要内容,如果未能解决你的问题,请参考以下文章

怎么计算算法复杂度 big O

仍然不了解 Big-O 与最坏情况时间复杂度

二分查找

二分查找

Big O

NTU课程笔记 MAS714 Big-O notations