Pythonデジタルおよびアナログノート-シミュレーテッドアニーリングアルゴリズム(4)巡回セールスマン問題


1.巡回セールスマン問題(TSP)

巡回セールスマン問題は、古典的な組み合わせ最適化問題であり、すべての都市を横断し、各都市を1回だけ訪問する最短の移動ルートを見つける必要があります。つまり、特定の正の重みの完全グラフに対して、合計が最小のハミルトン回路を見つける必要があります。重み:n個の都市があり、距離行列D = [dij]があります。ここで、dijは都市iから都市jまでの距離を表します(i、j = 1,2…n)。問題は、それぞれを訪問する回路を見つけることです。一度だけ都市にしてそれを作るパスの長さは最短です。巡回セールスマン問題はNP完全問題であり、大域的最適化ソリューションの計算の複雑さは、問題サイズの階乗関係とともに増加します。巡回セールスマン問題は、アルゴリズムの研究と比較の事例としてよく使用される典型的な組み合わせ最適化問題であるだけでなく、ルート計画、交通ロジスティクス、ネットワーク管理などの多くの実用的なアプリケーション問題も巡回セールスマン問題に変換できます。 。
  現在、巡回セールスマン問題の研究は、問題の特徴情報(都市の位置、距離、角度など)に基づいて構築されたさまざまなヒューリスティック検索アルゴリズムを含む、効率的な近似最適解のためのさまざまな最適化手法の探索と開発に主に焦点を当てています。シミュレートされたアニーリングアルゴリズム、遺伝的アルゴリズム、アリコロニーアルゴリズム、自然の法則をシミュレートまたは説明することによって開発されたニューラルネットワークアルゴリズム、または2つを組み合わせたハイブリッドアルゴリズムなどの最適化アルゴリズム。
  シミュレーテッドアニーリングアルゴリズムは、連続関数最適化問題を解決できるだけでなく、1983年にKIRKPATRICKを組み合わせ最適化問題の解決に成功させました。シミュレーテッドアニーリングアルゴリズムは、巡回セールスマン問題を解決するための一般的な方法になりました。通常、逆順、シフト、交換などの演算子を使用して新しいソリューションを生成します。

Youcansのオリジナルシリーズに注目し、毎週デジタルノートとアナログノートを更新することを歓迎します

Pythonデジタルおよびアナログノート-PuLPライブラリ
Pythonデジタルおよびアナログノート-StatsModels統計回帰
Pythonデジタルおよびアナログノート-SklearnPython
デジタルおよびアナログノート-NetworkXPythonデジタル
およびアナログノート-シミュレーテッドアニーリングアルゴリズム


2.巡回セールスマン問題を解決するためのシミュレーテッドアニーリングアルゴリズム

シミュレーテッドアニーリングアルゴリズムは、現在の解の近傍から新しい候補解を生成する必要があります。解の式の形式と近傍構造は、アルゴリズムが収束するために非常に重要です。組み合わせ最適化の問題は、独立変数が連続的に変化しないという点で関数の最適化とは異なり、目的関数は独立変数の値に依存するだけでなく、変数の配置順序にも関係します。極端な場合、巡回セールスマン問題のパスの長さは順列の順序にのみ依存するため、ソリューションを表すために一連の都市番号がよく使用されます。
  新しいソリューションの生成メカニズムは、現在のソリューションシーケンスに基づいて変換操作を実行し、シーケンス内のいくつかのポイントの配置順序をランダムに変更することです。一般的な基本的な変換操作には、スワップ演算子と反転演算子が含まれます。、ムーブ演算子、等 交換演算子は、現在のパスS_now内のi番目の都市Ciとj番目の都市Cjの位置を交換します。逆順演算子は2-optとも呼ばれ、現在のパスS_nowをi番目の都市Ciからj番目の都市に変換します。 Cj間の都市の順序が逆になります。シフト演算子は、現在のパスS_nowのi番目の都市Ciをj番目の都市Cjの後の位置に移動するOr-opt操作tと同等です。


3.プログラムの説明

次の段落では、巡回セールスマン問題を解決するためのシミュレーテッドアニーリングアルゴリズムのPythonプログラムを示します。プログラムでの詳細な処理の手順は次のとおりです。

  1. データの取得。読者がデータファイルをダウンロードできないようにするために、プログラムは直接割り当てを使用して旅行都市の場所の座標を読み取ります。実際、都市の場所のパラメータは通常データファイルによって与えられ、TSPLib(巡回セールスマン問題の国際標準データセット)を読み取るためのサブルーチンと呼び出しメソッドがプログラムに与えられています。
  2. 都市間の距離の計算。TSPLibの処理仕様によると、1つ目は、地球が丸いときの球形距離は言うまでもなく、ユークリッド距離に従って都市間の距離を計算することです。2つ目は、計算された都市間の距離を丸めることです(切り上げ)。都市間の距離を表すために実数を使用する代わりに、これは最適化結果にいくつかの違いをもたらす可能性があります。
  3. 新しいパスの生成。理解を容易にするために、このプログラムはスワップ演算子のサブルーチンのみを提供します。交換操作は非常に理解しやすいですが、実際には最適化の効率と効果は良くなく、逆順操作のパフォーマンスは交換オペレーターのパフォーマンスよりも大幅に優れています。

4.巡回セールスマン問題Pythonプログラムを解決するためのシミュレーテッドアニーリングアルゴリズム

# 模拟退火算法求解旅行商问题 Python程序
# Program: SimulatedAnnealing_v6.py
# Purpose: Simulated annealing algorithm for traveling salesman problem
# v1.0: = 关注 Youcans,分享原创系列 https://blog.csdn.net/youcans =
#   模拟退火求解旅行商问题(TSP)基本算法
# Copyright 2021 YouCans, XUPT
# Crated:2021-05-01

#  -*- coding: utf-8 -*-
import math                         # 导入模块 math
import random                       # 导入模块 random
import pandas as pd                 # 导入模块 pandas 并简写成 pd
import numpy as np                  # 导入模块 numpy 并简写成 np YouCans
import matplotlib.pyplot as plt     # 导入模块 matplotlib.pyplot 并简写成 plt

np.set_printoptions(precision=4)
pd.set_option('display.max_rows', 20)
pd.set_option('expand_frame_repr', False)
pd.options.display.float_format = '{:,.2f}'.format

# 子程序:初始化模拟退火算法的控制参数
def initParameter():
    # custom function initParameter():
    # Initial parameter for simulated annealing algorithm
    tInitial = 100.0                # 设定初始退火温度(initial temperature)
    tFinal  = 1                     # 设定终止退火温度(stop temperature)
    nMarkov = 1000                # Markov链长度,也即内循环运行次数
    alfa    = 0.98                 # 设定降温参数,T(k)=alfa*T(k-1)
    return tInitial,tFinal,alfa,nMarkov

# 子程序:读取TSPLib数据
def read_TSPLib(fileName):
    # custom function read_TSPLib(fileName)
    # Read datafile *.dat from TSPlib
    # return coordinates of each city by YouCans, XUPT

    res = []
    with open(fileName, 'r') as fid:
        for item in fid:
            if len(item.strip())!=0:
                res.append(item.split())

    loadData = np.array(res).astype('int')      # 数据格式:i Xi Yi
    coordinates = loadData[:,1::]
    return coordinates


# 子程序:计算各城市间的距离,得到距离矩阵
def getDistMat(nCities, coordinates):
    # custom function getDistMat(nCities, coordinates):
    # computer distance between each 2 Cities
    distMat = np.zeros((nCities,nCities))       # 初始化距离矩阵
    for i in range(nCities):
        for j in range(i,nCities):
            # np.linalg.norm 求向量的范数(默认求 二范数),得到 i、j 间的距离
            distMat[i][j] = distMat[j][i] = round(np.linalg.norm(coordinates[i]-coordinates[j]))
    return distMat                              # 城市间距离取整(四舍五入)


# 子程序:计算 TSP 路径长度
def calTourMileage(tourGiven, nCities, distMat):
    # custom function caltourMileage(nCities, tour, distMat):
    # to compute mileage of the given tour
    mileageTour = distMat[tourGiven[nCities-1], tourGiven[0]]   # dist((n-1),0)
    for i in range(nCities-1):                                  # dist(0,1),...dist((n-2)(n-1))
        mileageTour += distMat[tourGiven[i], tourGiven[i+1]]
    return round(mileageTour)                     # 路径总长度取整(四舍五入)


# 子程序:绘制 TSP 路径图
def plot_tour(tour, value, coordinates):
    # custom function plot_tour(tour, nCities, coordinates)

    num = len(tour)
    x0, y0 = coordinates[tour[num - 1]]
    x1, y1 = coordinates[tour[0]]
    plt.scatter(int(x0), int(y0), s=15, c='r')      # 绘制城市坐标点 C(n-1)
    plt.plot([x1, x0], [y1, y0], c='b')             # 绘制旅行路径 C(n-1)~C(0)
    for i in range(num - 1):
        x0, y0 = coordinates[tour[i]]
        x1, y1 = coordinates[tour[i + 1]]
        plt.scatter(int(x0), int(y0), s=15, c='r')  # 绘制城市坐标点 C(i)
        plt.plot([x1, x0], [y1, y0], c='b')         # 绘制旅行路径 C(i)~C(i+1)

    plt.xlabel("Total mileage of the tour:{:.1f}".format(value))
    plt.title("Optimization tour of TSP{:d}".format(num))  # 设置图形标题
    plt.show()


# 子程序:交换操作算子
def mutateSwap(tourGiven, nCities):
    # custom function mutateSwap(nCities, tourNow)
    # produce a mutation tour with 2-Swap operator
    # swap the position of two Cities in the given tour

    # 在 [0,n) 产生 2个不相等的随机整数 i,j
    i = np.random.randint(nCities)          # 产生第一个 [0,n) 区间内的随机整数
    while True:
        j = np.random.randint(nCities)      # 产生一个 [0,n) 区间内的随机整数
        if i!=j: break                      # 保证 i, j 不相等

    tourSwap = tourGiven.copy()             # 将给定路径复制给新路径 tourSwap
    tourSwap[i],tourSwap[j] = tourGiven[j],tourGiven[i] # 交换 城市 i 和 j 的位置————简洁的实现方法

    return tourSwap

def main():
    # 主程序 = 关注 Youcans,分享原创系列 https://blog.csdn.net/youcans =

    # # 读取旅行城市位置的坐标
    coordinates = np.array([[565.0, 575.0], [25.0, 185.0], [345.0, 750.0], [945.0, 685.0], [845.0, 655.0],
                            [880.0, 660.0], [25.0, 230.0], [525.0, 1000.0], [580.0, 1175.0], [650.0, 1130.0],
                            [1605.0, 620.0], [1220.0, 580.0], [1465.0, 200.0], [1530.0, 5.0], [845.0, 680.0],
                            [725.0, 370.0], [145.0, 665.0], [415.0, 635.0], [510.0, 875.0], [560.0, 365.0],
                            [300.0, 465.0], [520.0, 585.0], [480.0, 415.0], [835.0, 625.0], [975.0, 580.0],
                            [1215.0, 245.0], [1320.0, 315.0], [1250.0, 400.0], [660.0, 180.0], [410.0, 250.0],
                            [420.0, 555.0], [575.0, 665.0], [1150.0, 1160.0], [700.0, 580.0], [685.0, 595.0],
                            [685.0, 610.0], [770.0, 610.0], [795.0, 645.0], [720.0, 635.0], [760.0, 650.0],
                            [475.0, 960.0], [95.0, 260.0], [875.0, 920.0], [700.0, 500.0], [555.0, 815.0],
                            [830.0, 485.0], [1170.0, 65.0], [830.0, 610.0], [605.0, 625.0], [595.0, 360.0],
                            [1340.0, 725.0], [1740.0, 245.0]])
    # fileName = "../data/eil76.dat"                      # 数据文件的地址和文件名
    # coordinates = read_TSPLib(fileName)                 # 调用子程序,读取城市坐标数据文件

    # 模拟退火算法参数设置
    tInitial,tFinal,alfa,nMarkov = initParameter()      # 调用子程序,获得设置参数

    nCities = coordinates.shape[0]              # 根据输入的城市坐标 获得城市数量 nCities
    distMat = getDistMat(nCities, coordinates)  # 调用子程序,计算城市间距离矩阵
    nMarkov = nCities                           # Markov链 的初值设置
    tNow    = tInitial                          # 初始化 当前温度(current temperature)

    # 初始化准备
    tourNow   = np.arange(nCities)   # 产生初始路径,返回一个初值为0、步长为1、长度为n 的排列
    valueNow  = calTourMileage(tourNow,nCities,distMat) # 计算当前路径的总长度 valueNow
    tourBest  = tourNow.copy()                          # 初始化最优路径,复制 tourNow
    valueBest = valueNow                                # 初始化最优路径的总长度,复制 valueNow
    recordBest = []                                     # 初始化 最优路径记录表
    recordNow  = []                                     # 初始化 最优路径记录表

    # 开始模拟退火优化过程
    iter = 0                        # 外循环迭代次数计数器
    while tNow >= tFinal:           # 外循环,直到当前温度达到终止温度时结束
        # 在当前温度下,进行充分次数(nMarkov)的状态转移以达到热平衡

        for k in range(nMarkov):    # 内循环,循环次数为Markov链长度
            # 产生新解:
            tourNew = mutateSwap(tourNow, nCities)      # 通过 交换操作 产生新路径
            valueNew = calTourMileage(tourNew,nCities,distMat) # 计算当前路径的总长度
            deltaE = valueNew - valueNow

            # 接受判别:按照 Metropolis 准则决定是否接受新解
            if deltaE < 0:                          # 更优解:如果新解的目标函数好于当前解,则接受新解
                accept = True
                if valueNew < valueBest:            # 如果新解的目标函数好于最优解,则将新解保存为最优解
                    tourBest[:] = tourNew[:]
                    valueBest = valueNew
            else:                                   # 容忍解:如果新解的目标函数比当前解差,则以一定概率接受新解
                pAccept = math.exp(-deltaE/tNow)    # 计算容忍解的状态迁移概率
                if pAccept > random.random():
                    accept = True
                else:
                    accept = False

            # 保存新解
            if accept == True:                      # 如果接受新解,则将新解保存为当前解
                tourNow[:] = tourNew[:]
                valueNow = valueNew

        # 平移当前路径,以解决变换操作避开 0,(n-1)所带来的问题,并未实质性改变当前路径。
        tourNow = np.roll(tourNow,2)                # 循环移位函数,沿指定轴滚动数组元素

        # 完成当前温度的搜索,保存数据和输出
        recordBest.append(valueBest)                # 将本次温度下的最优路径长度追加到 最优路径记录表
        recordNow.append(valueNow)                  # 将当前路径长度追加到 当前路径记录表
        print('i:{}, t(i):{:.2f}, valueNow:{:.1f}, valueBest:{:.1f}'.format(iter,tNow,valueNow,valueBest))

        # 缓慢降温至新的温度,
        iter = iter + 1
        tNow = tNow * alfa                              # 指数降温曲线:T(k)=alfa*T(k-1)

    # 结束模拟退火过程

    # 图形化显示优化结果
    figure1 = plt.figure()     # 创建图形窗口 1
    plot_tour(tourBest, valueBest, coordinates)
    figure2 = plt.figure()     # 创建图形窗口 2
    plt.title("Optimization result of TSP{:d}".format(nCities)) # 设置图形标题
    plt.plot(np.array(recordBest),'b-', label='Best')           # 绘制 recordBest曲线
    plt.plot(np.array(recordNow),'g-', label='Now')             # 绘制 recordNow曲线
    plt.xlabel("iter")                                          # 设置 x轴标注
    plt.ylabel("mileage of tour")                               # 设置 y轴标注
    plt.legend()                                                # 显示图例
    plt.show()

    print("Tour verification successful!")
    print("Best tour: \n", tourBest)
    print("Best value: {:.1f}".format(valueBest))

    exit()

# = 关注 Youcans,分享原创系列 https://blog.csdn.net/youcans =
if __name__ == '__main__':
    main()

5.実行結果

プログラムの実行結果は参照用であり、明らかにこれは最適な結果ではありません。

Tour verification successful!
Best tour: 
 [18  7 40  2 16 17 31 38 39 36 24 27 26 11 50  3  5  4 23 47 37 14 42  9
  8 32 10 51 13 12 25 46 28 29  1  6 41 20 30 21  0 48 35 34 33 43 45 15
 49 19 22 44]
Best value: 9544.0

ここに画像の説明を挿入

= Youcansをフォローし、元のシリーズを共有するhttps://blog.csdn.net/youcans =
著作権表示:
元の作品
Copyright 2021 YouCans、XUPT Crated
:2021-05-04

Youcansをフォローして、元のシリーズを共有するhttps://blog.csdn.net/youcans

Pythonデジタルおよびアナログノート-PuLPライブラリ(1)線形プログラミングの概要
Pythonデジタルおよびアナログノート-PuLPライブラリ(2)高度な線形プログラミング
Pythonデジタルおよびアナログノート-PuLPライブラリ(3)線形プログラミングの例
Pythonデジタルおよびアナログノート-StatsModels統計的回帰(1))
Python数学ノートの概要-StatsModels統計的回帰(2)線形回帰
Python数学ノート-StatsModels統計的回帰(3)
モデルデータの準備Python数学ノート-StatsModels統計的回帰(4)ビジュアル
Python数学ノート-Sklearn (1))
Pythonデジタルおよびアナログノートの概要-Sklearn(2)クラスター分析
Pythonデジタルおよびアナログノート-Sklearn(3)主成分分析
Pythonデジタルおよびアナログノート-Sklearn(4)線形回帰
Pythonデジタルおよびアナログノート-Sklearn (5)サポートベクターマシン
Pythonデジタルおよびアナログノート-シミュレートされたアニーリングアルゴリズム(1)多変量関数の最適化
Pythonデジタルおよびアナログノート-シミュレートされたアニーリングアルゴリズム(2)制約の処理
Pythonデジタルおよびアナログノート-シミュレートされたアニーリングアルゴリズム(3)整数プログラミング問題
Pythonデジタルおよびアナログノート-シミュレートされたアニーリングアルゴリズム(4)出張セールスマンの問題

おすすめ

転載: blog.csdn.net/youcans/article/details/116396137