天天看點

【啟發式算法】Python實作模拟退火算法求解TSP問題模拟退火

模拟退火

算法相關參數

  • 初始溫度:T0
  • 目前溫度:t
  • 結束溫度:Tend
  • 疊代次數:Times
  • 溫度衰減系數:a
  • 目标函數內插補點(目前解和新解之間的函數內插補點):δ
  • 接受新解的機率 :p = e-(δ/t)

算法效果分析

  1. 疊代次數越多,溫度衰減系數約大(即溫度衰減得越慢),搜尋次數越多,程式運作越慢,相對來 得到的解更可信
  2. “ 渣 男 ” 套 路 : 前 期 廣 撒 網 , 後 期 精 準 打 擊 。 \color{red}{“渣男”套路:前期廣撒網,後期精準打擊。} “渣男”套路:前期廣撒網,後期精準打擊。模拟退火算法前期要增加接受新解的機率,以便于擴大我們的搜尋範圍,避免陷入局部最優解;算法後期要減少接受新解的機率,避免丢棄目前最優解
  3. 目标函數內插補點越大,接受新解的機率越大;随着疊代次數的增加,溫度t逐漸減小,接受新解的機率越小

算法步驟

  1. 資料初始化:計算距離矩陣(dist[i][j]表示i城市與j城市之間的距離);随機得到初始解(起點為0城市);編寫距離計算函數
  2. 在每個溫度下,進行Times次疊代
  3. 每次疊代時:求得新路徑并計算其路徑長度(通過二次變換法和資料切片交換來求得新解)
  4. 若得到的新路徑長度小于目前路徑長度,則更新目前路徑和目前路徑長度,若與此同時新路徑長度還優于最優路徑長度,則同時更新最優路徑及其長度
  5. 模拟退火算法的核心:若得到的新路徑長度大于等于目前路徑長度,則根據相應函數計算機率,通過随機數和此時的機率相比較,進而決定是否接收新路徑(即劣解)
  6. 直到目前溫度小于結束溫度時,算法結束
  7. 調參來優化模型
# -*- coding: utf-8 -*-
import random
import matplotlib.pyplot as plt
import numpy as np
import math
import sys
import copy
import time
#解決中文顯示問題
plt.rcParams['font.sans-serif'] = ['KaiTi'] # 指定預設字型
plt.rcParams['axes.unicode_minus'] = False # 解決儲存圖像是負号'-'顯示為方塊的問題
# 初始溫度,結束溫度
T0 = 100
Tend = 1e-20
#循環控制常數,疊代次數
Times = 50000
# 溫度衰減系數
a = 0.999

#原始資料
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]])
num = coordinates.shape[0]# 城市數目
coord_x = coordinates[:,0]# 城市橫坐标
coord_y = coordinates[:,1]# 城市縱坐标
route_origin = list(range(num))  # 初始解
sumDist_origin= 0.0  # 初始距離
dist = np.zeros((num,num))# 生成距離矩陣,記錄兩個城市之間的距離


# 初始化距離矩陣
def initDist():
    for i in range(num):
        x_i = coord_x[i]
        y_i = coord_y[i]
        for j in range(i, num):
            x_j = coord_x[j]
            y_j = coord_y[j]
            dist[i, j] = dist[j, i] = math.sqrt((x_i - x_j) ** 2 + (y_i - y_j) ** 2)# 對稱矩陣
    return dist
dist = initDist()

def shuffle(my_list):
    """
    對可行解進行排列組合,除去起點
    :param my_list: 可行解
    :return:
    """
    temp_list = my_list[1:]  # 資料切片,左閉右開,擷取除起點以外的所有元素
    np.random.shuffle(temp_list)
    shuffle_list = my_list[:1] + temp_list  # 切片的拼接
    return shuffle_list

#初始化初始解
def initRoute():
    global num, route_origin
    # route = random.sample(range(0, num), num)
    route_origin = shuffle(route_origin)
    # 使用上次程式運作得到的最優解:容易陷入局部7947.836346399286

# 計算總距離
def getSumDist(route, num, dist):
    sumDist = 0.0
    for i in range(num - 1):
        sumDist += dist[int(route[i])][int(route[i + 1])]
    sumDist += dist[int(route[num - 1])][int(route[0])]
    return sumDist


#擷取新解
def getNewRoute(route, time, num):
    route_current = copy.copy(route)
    # 偶次疊代,二變換法
    u = 0  # u,v,randint要去除起點終點
    v = 0
    if time % 2 == 0:
        while u == 0 | u == num - 1 | v == 0 | v == num - 1:
            u = random.randint(0, num - 1)
            v = random.randint(0, num - 1)
        temp = route_current[u]
        route_current[u] = route_current[v]
        route_current[v] = temp
    # 奇次疊代,多組資料交換
    else:
        c1 = random.randint(1, num - 2)
        c2 = random.randint(1, num - 2)
        c3 = random.randint(1, num - 2)
        sort_c = sorted([c1, c2, c3])
        c1 = sort_c[0]
        c2 = sort_c[1]
        c3 = sort_c[2]
        temp1 = route_current[:c1]
        temp2 = route_current[c1:c2]
        temp3 = route_current[c2:c3]
        temp4 = route_current[c3:]
        route_current = temp1 + temp3 + temp2 + temp4
    return route_current


# 模拟退火核心代碼
def SACore():
    # 新解,新距離
    print("route_origin:", route_origin)
    print("sumDist_origin:", sumDist_origin)
    route_current = route_origin
    sumDist_current = sumDist_origin
    route_new = np.zeros(num)
    sumDist_new = 0.0
    route_best = np.zeros(num)
    sumDist_best = sumDist_origin
    t = T0
    while True:
        if t < Tend:
            break
        for time in range(Times):#疊代次數
            route_new = getNewRoute(route_current, time, num)
            sumDist_new = getSumDist(route_new, num, dist)
            delt = sumDist_new - sumDist_current
            if delt <= 0:#新路線距離更短
                route_current = route_new
                sumDist_current = sumDist_new
                if sumDist_best > sumDist_new:#是最優距離
                    route_best = route_new
                    sumDist_best = sumDist_new
            else:#模拟退火算法的核心,以一定比率接收差解
                p = math.exp(-delt / t)
                randomp = random.uniform(0, 1)
                if randomp < p:
                    route_current = route_new
                    sumDist_current = sumDist_new
            t = t * a
            # print("目前溫度:", t)
            # print("**********************")

    print("最佳路徑:", route_best)
    print("最佳距離:", sumDist_best)



if __name__ == '__main__':
    start = time.time()
    initRoute()
    sumDist_origin = getSumDist(route_origin, num, dist)
    SACore()
    end = time.time()
    print('Running time: %s Seconds' % (end - start))

"""
最佳路徑: [0, 21, 49, 19, 22, 29, 6, 1, 41, 20, 30, 17, 16, 2, 44, 31, 48, 34, 38, 35, 33, 36, 39, 37, 47, 23, 5, 14, 4, 3, 24, 45, 43, 15, 28, 46, 25, 26, 12, 13, 51, 10, 11, 50, 32, 42, 9, 8, 7, 40, 18, 27, 0]
最佳距離: 9636.344979299336
最佳路徑: [0, 21, 30, 22, 49, 19, 29, 1, 6, 41, 20, 16, 2, 17, 44, 31, 48, 35, 34, 33, 38, 39, 37, 36, 47, 23, 4, 14, 5, 3, 24, 45, 43, 15, 28, 46, 25, 26, 12, 13, 51, 10, 11, 50, 32, 42, 9, 8, 7, 40, 18, 27, 0]
最佳距離: 9451.034445650703
最佳路徑: [0, 21, 30, 22, 49, 19, 29, 1, 6, 41, 20, 16, 2, 17, 31, 48, 35, 34, 33, 38, 39, 36, 37, 47, 23, 4, 14, 5, 3, 24, 45, 43, 15, 28, 46, 25, 26, 12, 13, 51, 10, 11, 50, 32, 42, 9, 8, 7, 40, 44, 18, 27, 0]
最佳距離: 9378.514369030105
最佳路徑: [0, 21, 30, 22, 19, 49, 29, 1, 6, 41, 20, 16, 2, 17, 31, 48, 35, 34, 33, 38, 39, 36, 37, 47, 23, 4, 14, 5, 3, 24, 45, 43, 15, 28, 46, 25, 26, 12, 13, 51, 10, 11, 50, 32, 42, 9, 8, 7, 40, 44, 18, 27, 0]
最佳距離: 9371.600543296801

最佳路徑: [0, 21, 35, 38, 39, 36, 37, 23, 47, 14, 4, 5, 42, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 9, 8, 40, 7, 18, 44, 31, 48, 34, 33, 45, 15, 28, 49, 19, 22, 16, 2, 17, 30, 20, 29, 41, 6, 1, 43, 0]
最佳距離: 8858.531529765904
最佳路徑: [0, 21, 35, 38, 39, 36, 37, 47, 23, 4, 14, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 42, 32, 9, 8, 7, 40, 18, 44, 31, 48, 34, 33, 45, 15, 28, 49, 19, 22, 29, 16, 2, 17, 30, 20, 41, 6, 1, 43, 0]
最佳距離: 8684.306665303991
最佳路徑: [0, 21, 35, 38, 39, 36, 37, 47, 23, 4, 14, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 42, 9, 8, 7, 40, 18, 44, 31, 48, 34, 33, 45, 15, 28, 49, 19, 22, 29, 16, 2, 17, 30, 20, 41, 6, 1, 43, 0]
最佳距離: 8461.633757845264

最佳路徑: [0, 48, 31, 21, 30, 20, 41, 6, 1, 29, 22, 19, 49, 28, 15, 45, 43, 33, 34, 35, 38, 39, 36, 37, 47, 23, 4, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 42, 8, 9, 7, 40, 18, 44, 2, 16, 17, 14, 0]
最佳距離: 8398.198925957735
最佳路徑: [0, 21, 17, 30, 2, 16, 20, 41, 6, 1, 29, 22, 19, 49, 28, 15, 45, 43, 33, 34, 38, 39, 37, 4, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 42, 8, 9, 7, 40, 18, 44, 31, 48, 35, 36, 47, 23, 14, 0]
最佳距離: 8113.096167712638
最佳路徑: [0, 21, 17, 2, 16, 30, 20, 41, 6, 1, 29, 22, 19, 49, 28, 15, 45, 43, 33, 34, 38, 39, 37, 4, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 42, 9, 8, 7, 40, 18, 44, 31, 48, 35, 36, 47, 23, 14, 0]
最佳距離: 8072.726662911702
最佳路徑: [0, 21, 30, 17, 2, 16, 20, 41, 6, 1, 29, 22, 19, 49, 28, 15, 45, 43, 33, 34, 38, 39, 37, 4, 5, 3, 24, 11, 27, 26, 25, 46, 12, 13, 51, 10, 50, 32, 42, 9, 8, 7, 40, 18, 44, 31, 48, 35, 36, 47, 23, 14, 0]
最佳距離: 7947.836346399286
          GA結果:
          [0, 21, 29, 22, 20, 30, 17, 16, 2, 18, 40, 7, 8, 44, 31, 48, 36, 37, 33, 43, 45, 15, 28, 19, 49, 34, 35, 38, 39, 4, 5, 23, 47, 14, 3, 24, 42, 32, 50, 11, 10, 27, 26, 12, 51, 13, 25, 46, 41, 1, 6, 21, 0]
          10112.612313214102
"""