模拟退火算法求解TSP问题(python实现)

引言

前文 http://t.csdnimg.cn/Q4l9X 使用局部搜索求解了一个TSP问题,如果打印信息就会发现,尽管迭代了10000次,接受新解的次数只有1image.png
因为只有在产生更优解时才接受新解,这样就容易陷入局部最优
image.png

模拟退火概念

为了能够跳出局部最优解,模拟退火是一种比较有用的方法,简而言之就是通过一定的概率接受较差的解,这个概率通常用这个公式描述:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

编码

定义模拟退火接受准则

def acceptance_function(delta_energy, temperature):
    if delta_energy < 0:
        return True
    else:
        probability = np.exp(-delta_energy / temperature)
        return random.uniform(0, 1) < probability

模拟退火方法

def simulatedAnnealing():
    temperature = 10000
    cooling_rate = 0.995
    path0 = list(range(len(dist_mat)))
    best = calcTSP(path0)
    iterations = 100000
    for i in range(iterations):
        newpath = two_swap(path0)
        delta = calcTSP(newpath) - best
        if acceptance_function(delta, temperature):
            path0 = newpath
            best = calcTSP(newpath)
        temperature *= cooling_rate
    return best

结论

更好?

用模拟退火(SA)和邻域搜索(LS)分别迭代100000次,发现模拟退火的结果甚至更遭
SA:
image.png
LS:
image.png
原因是初始解太优秀了,现在加上一行代码,打乱初始解:

random.shuffle(path0)

再次运行
SA:
image.png
LS:
image.png
嗯?居然还是更遭,一定是启发式算法太随机了,我们运行30次取平均值
SA:image.png
LS:image.png

emmm,似乎是优化了一点

关于启发式算法

笔者至今对启发式算法仍存在很多疑惑的地方,因为过程太多随机,有点像暴力求解,有各种奇奇怪怪的启发式算法变体,也说不清楚对算法的优化到底有多少,只能通过多次实验去证明。
并且影响算法的原因有很多,算子,初始化策略,迭代次数,邻域类型…

当然这只是我个人的理解,如果有什么理解错误,欢迎大佬指正。

完整代码

以下是完整代码,有错误请指正。

import copy

import pandas as pd
import numpy as np
import math
import random

filepath = './benchmark/china34.tsp'
with open(filepath, 'r') as file:
    lines = file.readlines()

nodes = []

for i in range(len(lines)):
    if i < 6:
        continue
    info = lines[i].split()
    if info[0] == 'EOF':
        break
    coor = [float(info[1]), float(info[2])]
    nodes.append(coor)


D = np.radians(nodes)

city_cnt = len(nodes)
dist_mat = np.zeros((city_cnt, city_cnt))

for i in range(city_cnt):
    for j in range(city_cnt):
        if i == j:
            # 相同城市不允许访问
            dist_mat[i][j] = 0  # 修改为0表示同一个城市的距离为0
        else:
            # 使用 Haversine 公式计算距离
            lat1, lon1 = D[i][1], D[i][0]
            lat2, lon2 = D[j][1], D[j][0]

            # Haversine 公式
            dlat = lat2 - lat1
            dlon = lon2 - lon1
            a = math.sin(dlat / 2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2)**2
            c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
            distance = 6378.14 * c  # 6378.14 是地球的半径,单位:km

            dist_mat[i][j] = distance


def acceptance_function(delta_energy, temperature):
    if delta_energy < 0:
        return True
    else:
        probability = np.exp(-delta_energy / temperature)
        return random.uniform(0, 1) < probability


def simulatedAnnealing():
    temperature = 10000
    cooling_rate = 0.995
    path0 = list(range(len(dist_mat)))
    random.shuffle(path0)
    best = calcTSP(path0)
    cnt = 0
    iterations = 100000
    for i in range(iterations):
        newpath = two_swap(path0)
        delta = calcTSP(newpath) - best
        if acceptance_function(delta, temperature):
            cnt += 1
            path0 = newpath
            best = calcTSP(newpath)
        temperature *= cooling_rate
    print("接受新解:", cnt, "次")
    print("best value:", best)
    print("best path:", path0)
    return best


def two_swap(path):
    cp = copy.deepcopy(path)
    r = random.sample(list(range(len(cp))), 2)
    cp[r[0]], cp[r[1]] = cp[r[1]], cp[r[0]]
    return cp


def calcTSP(path):
    sum = 0
    for i in range(1, len(path)):
        sum += dist_mat[path[i]][path[i - 1]]
    sum += dist_mat[path[0]][path[len(path) - 1]]
    return sum


# simulatedAnnealing()
sum = 0
for i in range(30):
    sum += simulatedAnnealing()

print(sum / 30)

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值