【问题标题】:Solving TSP using dynamic programming in Java在 Java 中使用动态编程解决 TSP
【发布时间】:2015-10-30 10:07:24
【问题描述】:

我在网上找到了很多资源,讨论这个和相关主题,但我没有找到任何真正帮助我知道从哪里开始实施这个解决方案的东西。

澄清一下,从 0 城市开始,我需要每隔一个城市访问一次,然后返回 0 城市。

我有一个这样的数组:

   0 1129 1417 1240 1951
1129    0 1100  800 2237
1417 1100    0 1890 3046
1240  800 1890    0 1558
1951 2237 3046 1558    0

除了找到最佳路线,我还需要找到沿途的最佳部分路线。例如,我将从长度为 2 的路线开始,并最终打印出如下内容:

S = {0,1}
C({0,1},1) = 1129

S = {0,2}
C({0,2},2) = 1417

S = {0,3}
C({0,3},3) = 1240

S = {0,4}
C({0,4},4) = 1951

然后我会转到长度为 3 的路线,并打印如下内容:

S = {0,1,2}
C({0,1,2},1) = 2517
C({0,1,2},2) = 2229
and so on...

为了使它成为一个动态编程解决方案,我假设我应该保存任何 n 个节点之间的最短距离,我认为最好的方法是使用 Hashmap,其中键是整数值该路径中包含的每个节点,按升序排列(从节点 0>1>3>4 或 0>1>4>3 开始的路径可以存储为 '134'),并且每个密钥将包含一个可以存储的对路径顺序为列表,总距离为整数。

此时我想我想计算距离 2 的所有路径,然后计算距离 3 的所有路径,然后取最小的几个并使用哈希图为每个路径找到最短的返回路径,然后进行比较。

这看起来可行吗?还是我完全走错了路?

【问题讨论】:

标签: java algorithm dynamic-programming traveling-salesman


【解决方案1】:

你有点走上正轨了。动态编程不是计算 TSP 的方法。你有点接近的是计算最小生成树。这是一棵树,它使用尽可能短的边总和连接所有节点。有两种经常使用的算法:Primm 和 Kruskal。它们产生类似于您的最佳部分路线列表的东西。我建议你看看 Primm 的算法:https://en.wikipedia.org/wiki/Prim%27s_algorithm

解决 TSP 的最简单方法是找到最小生成树,然后在树上进行预排序树遍历。这为您提供了一个近似的旅行推销员解决方案,称为三角不等式近似。它保证不会超过最佳 TSP 的两倍,但它的计算速度可以更快。这个网页解释得很好http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/AproxAlgor/TSP/tsp.htm

如果您想要更优化的解决方案,您需要查看 Christofide 的方法,该方法更复杂。

【讨论】:

  • 如果有近似值,真的可以称为“求解TSP”吗?您也可以使用 DP 解决 TSP(实际上仅适用于小型实例,还有更多可扩展的方法)
  • 这些方法似乎都没有使用动态编程方法,对吗?不幸的是,这是该项目的要求。这是我发现的最接近我正在尝试做的事情,但说实话,很难完全理解quora.com/…
  • 顺便说一下,运行的最大节点数是 5-8 个。
  • 是的,我不太确定您问题中的“动态编程”位有多重要。 O(n^2.2n) 绝对是一个昂贵的解决方案。
  • Christofides 的方法和最小生成树算法(或@AndyN 所说的三角不等式近似)确实都是启发式方法。从理论的角度来看,它们确实很有趣,但还有其他更好的启发式方法。
【解决方案2】:

你在正确的轨道上。

我认为您正在了解这样一个事实,即 DP 递归本身只告诉您每个 S 和 j 的最佳 成本,而不是达到该成本的 节点 .处理这个问题的经典方法是通过“回溯”:一旦你发现,例如,C({0,1,2,3,4},-) = 10000,你就会找出哪个节点达到了这个成本;假设它是 3。然后你找出哪个节点达到了C({0,1,2,3,4},3);假设它是 1。然后你找出哪个节点达到了C({0,1,2,4},1);假设它是 2,... 等等。这是经典的方式,它避免了存储大量中间数据。对于状态空间较小的 DP,只需存储所有这些优化器就更容易了。在您的情况下,您有一个指数级大的状态空间,因此存储所有这些优化器可能会很昂贵,但您已经必须存储一个同样大的数据结构 (C),因此您很可能也可以存储优化器。您存储一些最好的中间解决方案可能会起作用——然后如果回溯例程需要一个你没有存储的,你可以用经典的方式计算它——似乎是合理的,但我不确定它是否值得额外的编码与纯粹的回溯方法。

一个澄清:您实际上并不想“计算距离 2 的所有路径,然后计算距离 3 的所有路径,...”,而是您想枚举大小为 2 的所有 集合 ,然后是大小为 3 的所有 等。仍然是指数级的,但不如枚举所有路径那么糟糕。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    • 1970-01-01
    • 2012-05-08
    • 2015-06-14
    • 2015-07-18
    • 1970-01-01
    • 2021-10-16
    相关资源
    最近更新 更多