【启发式算法】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
"""

你可能感兴趣的:(算法笔记,算法,python,启发式算法,tsp,np问题)