模拟退火算法求解tsp

data文件夹
st70.tsp
NAME: st70
TYPE: TSP
COMMENT: 70-city problem (Smith/Thompson)
DIMENSION: 70
EDGE_WEIGHT_TYPE : EUC_2D
NODE_COORD_SECTION
1 64 96
2 80 39
3 69 23
4 72 42
5 48 67
6 58 43
7 81 34
8 79 17
9 30 23
10 42 67
11 7 76
12 29 51
13 78 92
14 64 8
15 95 57
16 57 91
17 40 35
18 68 40
19 92 34
20 62 1
21 28 43
22 76 73
23 67 88
24 93 54
25 6 8
26 87 18
27 30 9
28 77 13
29 78 94
30 55 3
31 82 88
32 73 28
33 20 55
34 27 43
35 95 86
36 67 99
37 48 83
38 75 81
39 8 19
40 20 18
41 54 38
42 63 36
43 44 33
44 52 18
45 12 13
46 25 5
47 58 85
48 5 67
49 90 9
50 41 76
51 25 76
52 37 64
53 56 63
54 10 55
55 98 7
56 16 74
57 89 60
58 48 82
59 81 76
60 29 60
61 17 22
62 5 45
63 79 70
64 9 100
65 17 82
66 74 67
67 10 68
68 48 19
69 83 86
70 84 94
EOF
import random
import math
import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl  # 在绘图中显示中文

class SA(object):
    def __init__(self):
        self.T0 = 4000# 当前温度
        self.Tend = 1e-3#温度终值
        self.rate = 0.9995# 降温系数
        self.num_city=70
        self.location = self.read_tsp('data/st70.tsp')#城市坐标,class 'numpy.ndarray',二维数组
        self.dis_mat = self.compute_dis_mat()#距离矩阵
        self.path = self.greedy_init(self.dis_mat,100,self.num_city)#一维列表(使用贪婪算法获得一个较好的初始解)
        # self.path = self.random_init(self.num_city)  # 一维列表(随机初始化一个解)
        self.iter_x = [0]#外层循环代数
        self.iter_y = [self.compute_pathlen(self.path)]#每个温度下的最优路径的长度
        self.count = 0  # 外层循环次数

    def greedy_init(self, dis_mat, num_total, num_city):
        """使用贪婪算法获得一个较好的初始解 """
        start_index = 0
        result = []
        for i in range(num_total):
            rest = [x for x in range(0, num_city)]
            # 所有起始点都已经生成了
            if start_index >= num_city:
                start_index = np.random.randint(0, num_city)# todo 是不是要减去1
                result.append(result[start_index].copy())
                continue
            current = start_index
            rest.remove(current)
            # 找到一条最近邻路径
            result_one = [current]
            while len(rest) != 0:
                tmp_min = math.inf
                tmp_choose = -1
                for x in rest:
                    if dis_mat[current][x] < tmp_min:
                        tmp_min = dis_mat[current][x]
                        tmp_choose = x

                current = tmp_choose
                result_one.append(tmp_choose)
                rest.remove(tmp_choose)
            result.append(result_one)
            start_index += 1
        length=[]
        for i in result:
            length.append(self.compute_pathlen(i))
        # pathlens = self.compute_paths(result)
        sortindex = np.argsort(length)
        index = sortindex[0]
        return result[index]

    # 初始化一条随机路径
    def random_init(self, num_city):
        tmp = [x for x in range(num_city)]
        random.shuffle(tmp)
        return tmp

    # 计算不同城市之间的距离
    def compute_dis_mat(self):
        dis_mat = np.zeros((self.num_city, self.num_city))
        for i in range(self.num_city):
            for j in range(self.num_city):
                if i == j:
                    dis_mat[i][j] = np.inf
                    continue
                a = self.location[i]
                b = self.location[j]
                tmp = np.sqrt(sum([(x[0] - x[1]) ** 2 for x in zip(a, b)]))
                dis_mat[i][j] = tmp
        return dis_mat

    # 计算路径长度
    def compute_pathlen(self, path):
        length=0
        for i in range(len(path) - 1):
            a = path[i]
            b = path[i + 1]
            length += self.dis_mat[a][b]
        length+=self.dis_mat[path[0]][path[-1]]
        return length

    # 产生一个新解:随机选取一个片段,然后把片段倒序,再安插在原位置
    def get_new_path(self, path):
        path = path.copy()
        t = [x for x in range(len(path))]
        a, b = np.random.choice(t, 2)
        path[a:b] = path[a:b][::-1]
        return path

    # 退火策略,根据温度变化有一定概率接受差的解
    def eval_fire(self, best_path, new_path):
        best_length = self.compute_pathlen(best_path)
        new_length = self.compute_pathlen(new_path)
        dc = new_length - best_length
        # p = max(1e-1, np.exp(-dc / self.T0))
        p = np.exp(-dc / self.T0)
        if new_length < best_length:
            return new_path, new_length
        elif np.random.rand() <= p:
            return new_path, new_length
        else:
            return best_path, best_length
        # if new_length < best_length:
        #     return new_path, new_length
        # else:
        #     if np.random.rand() <= p:
        #         return new_path, new_length
        #     else:
        #         return best_path, best_length

    def read_tsp(self, path):# 读取数据
        lines = open(path, 'r').readlines()
        assert 'NODE_COORD_SECTION\n' in lines
        index = lines.index('NODE_COORD_SECTION\n')
        data = lines[index + 1:-1]
        tmp = []
        for line in data:
            line = line.strip().split(' ')
            if line[0] == 'EOF':
                continue
            tmpline = []
            for x in line:
                if x == '':
                    continue
                else:
                    tmpline.append(float(x))
            if tmpline == []:
                continue
            tmp.append(tmpline)
        data = tmp
        data = np.array(data)  # 将列表转换成数组
        data = data[:, 1:]
        return data

    # 模拟退火总流程
    def run(self):
        # 记录最优解
        best_path = self.path#路径
        best_length = self.compute_pathlen(best_path)#长度
        while self.T0 > self.Tend:
            self.count += 1
            # 产生在这个温度下的随机解
            new_path = self.get_new_path(self.path.copy())
            # 根据温度判断是否选择这个解
            self.path, length = self.eval_fire(best_path, new_path)
            # 更新最优解
            if length < best_length:
                best_length = length
                best_path = self.path
            # 降低温度
            self.T0 *= self.rate# 温度按照一定的比例下降(冷却)
            # 记录路径收敛曲线
            self.iter_x.append(self.count)
            self.iter_y.append(best_length)
            # print(self.count, best_length)
        print(f"遍历{self.num_city}个城市所需的最短时间为{best_length}")
        print(f"对应的具体路径为{best_path}")
        path_loc=self.location[best_path]#self.location[best_path]依然是二维数组,把self.location数组按照best_path排序

        mpl.rcParams["font.sans-serif"] = ["Arial Unicode MS"]  # 设置中文显示字体
        mpl.rcParams["axes.unicode_minus"] = False  # 设置正常显示符号,负号显示解决方案

        fig,axs = plt.subplots(2, 1, sharex=False, sharey=False)
        axs[0].scatter(path_loc[:, 0], path_loc[:, 1])#散点图
        path_loc = np.vstack([path_loc, path_loc[0]])# 加上一行因为会回到起点#71*2
        axs[0].plot(path_loc[:, 0], path_loc[:, 1])
        axs[0].set_title('规划结果')

        iterations = self.iter_x#外层循环代数,列表
        best_record = self.iter_y#每个温度下的最优路径的长度,列表
        axs[1].plot(iterations, best_record)
        axs[1].set_title('收敛曲线')
        plt.show()

instance = SA()
instance.run()

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值