【问题标题】:Traveling salesman with random initial solution, optimization algorithm returning unexpected result具有随机初始解的旅行推销员,优化算法返回意外结果
【发布时间】:2026-01-04 02:55:01
【问题描述】:

我知道旅行推销员是众所周知的,但我需要一些帮助来了解为什么我的优化算法会返回意外结果。我通过随机选择城市创建了一个初始解决方案。我还创建了一个带有构造函数的类,该构造函数以距离矩阵和初始解作为参数。优化算法非常简单;它交换两个城市并检查路线距离是否已改善,如果已改善,则应更新最佳解决方案。这持续了 6 次迭代。问题在于,即使不满足覆盖它的条件,似乎最好的解决方案也会被更新和覆盖。我将添加一张显示测试运行结果的图像。

变量bestSolution 似乎被覆盖了,但bestDistance 没有。我必须有某种隧道视野,因为即使代码非常简单,我也无法弄清楚这一点。有人可以解释为什么bestSolution 被覆盖并返回意外结果吗?

下面的代码示例:

package RandomMethod

import GreedyHeuristic
import java.util.*

fun main(args: Array<String>) {

                                           /*A  B  C*/
    val distances = arrayOf(/*A*/ intArrayOf(0, 2, 7),
                            /*B*/ intArrayOf(2, 0, 9),
                            /*C*/ intArrayOf(7, 9, 0))

    val initalSolution = findRandomRoute(distances)

    println("Initial solution: $initalSolution")
    println("Total distance: ${findTotalDistance(distances, initalSolution)}\n")

    val optimizedSolution = GreedyHeuristic(distances, initalSolution).optimize()

    println("\nOptimized solution with Greedy Heuristic: $optimizedSolution")
    println("Total distance: ${findTotalDistance(distances, optimizedSolution)}")

}

fun areAllCitiesVisited(isCityVisited: Array<Boolean>): Boolean {

    for (visited in isCityVisited) {
        if (!visited) return false
    }
    return true
}

fun findTotalDistance(distances: Array<IntArray>, orderToBeVisited: MutableList<Int>): Int {

    var totalDistance = 0

    for (i in 0..orderToBeVisited.size - 2) {
        val fromCityIndex = orderToBeVisited.get(i)
        val toCityIndex = orderToBeVisited.get(i + 1)
        totalDistance += distances[fromCityIndex].get(toCityIndex)
    }
    return totalDistance
}

fun findRandomRoute(distances: Array<IntArray>): MutableList<Int> {
    val visitedCities: Array<Boolean> = Array(distances.size, {i -> false})

    // Find starting city index. 0 = A, 1 = B, 2 = C .... N = X
    var currentCity = Random().nextInt(distances.size)
    val orderToBeVisited: MutableList<Int> = mutableListOf(currentCity)

    visitedCities[currentCity] = true

    while (!areAllCitiesVisited(visitedCities)) {

        currentCity = Random().nextInt(distances.size)

        if (!visitedCities[currentCity]) {
            orderToBeVisited.add(currentCity)
            visitedCities[currentCity] = true
        }
    }
    return orderToBeVisited
}

以及优化类:

import java.util.*

class GreedyHeuristic(distances: Array<IntArray>, initialSoltion: MutableList<Int>) {

    val mInitialSolution: MutableList<Int> = initialSoltion
    val mDistances: Array<IntArray> = distances

    fun optimize(): MutableList<Int> {
        var bestSolution = mInitialSolution
        var newSolution = mInitialSolution
        var bestDistance = findTotalDistance(mDistances, bestSolution)
        var i = 0

        while (i <= 5) {
            println("best distance at start of loop: $bestDistance")

            var cityIndex1 = Integer.MAX_VALUE
            var cityIndex2 = Integer.MAX_VALUE

            while (cityIndex1 == cityIndex2) {
                cityIndex1 = Random().nextInt(mInitialSolution.size)
                cityIndex2 = Random().nextInt(mInitialSolution.size)
            }

            val temp = newSolution.get(cityIndex1)
            newSolution.set(cityIndex1, newSolution.get(cityIndex2))
            newSolution.set(cityIndex2, temp)

            val newDistance: Int = findTotalDistance(mDistances, newSolution)
            println("new distance: $newDistance\n")

            if (newDistance < bestDistance) {
                println("New values gived to solution and distance")
                bestSolution = newSolution
                bestDistance = newDistance
            }
            i++
        }
        println("The distance of the best solution ${findTotalDistance(mDistances, bestSolution)}")
        return bestSolution
    }

    fun findTotalDistance(distances: Array<IntArray>, orderToBeVisited: MutableList<Int>): Int {

        var totalDistance = 0

        for (i in 0..orderToBeVisited.size - 2) {
            val fromCityIndex = orderToBeVisited.get(i)
            val toCityIndex = orderToBeVisited.get(i + 1)
            totalDistance += distances[fromCityIndex].get(toCityIndex)
        }
        return totalDistance
    }

}

【问题讨论】:

  • 可能是您将 MutableList 传递给某个修改它的函数,我目前正在idea中设置项目
  • bestSolution = newSolution.toMutableList() 这似乎修复了它,这样可以确保列表以后不会被修改
  • 您的建议帮了大忙,但还是得到了一些意想不到的结果。还更改了var bestSolution = mInitialSolution.toMutableListvar newSolution = mInitialSolution.toMutableList,现在每次都运行良好。非常感谢您的帮助!

标签: kotlin traveling-salesman


【解决方案1】:

除非您特别要求,否则 Kotlin(以及一般的 JVM 语言)不会复制值。这意味着,当您这样做时:

var bestSolution = mInitialSolution
var newSolution = mInitialSolution

您没有将bestSolutionnewSolution 设置为单独的mInitialSolution 副本,而是使它们指向同一个MutableList,因此使一个变异会变异另一个。也就是说:你的问题不是bestSolution被覆盖了,而是你每次修改newSolution时不小心修改了它。

然后,您可以在 while 循环的每次迭代中重复使用 newSolution,而无需创建新列表。这导致我们做两件事:

  • 因为newSolution仍然是bestSolution的别名,所以修改前者也会修改后者。
  • bestSolution = newSolution 什么都不做。

正如评论中提到的,解决此问题的最简单方法是战略性地使用 .toMutableList(),这将强制复制列表。您可以通过在顶部进行此更改来实现此目的:

var bestSolution = mInitialSolution.toMutableList()
var newSolution = mInitialSolution.toMutableList()

然后在循环内部:

bestSolution = newSolution.toMutableList()

顺便说一句:作为一般规则,您可能应该返回并接受List 而不是MutableList,除非您特别希望它成为您将要就地改变事物的函数合同的一部分。在这种特殊情况下,它会迫使你要么做一些讨厌的事情(比如不安全地将mInitialSolution 转换为MutableList,这应该会在你的脑海中敲响各种警钟),或者复制列表(这会'已将您推向正确的答案)

【讨论】: