模拟退火解决TSP旅行商问题_Python手写实现

模拟退火算法

Metropolis准则

在温度为 T T T 时,接受能量从 E ( X o l d ) E(X o l d) E(Xold) E ( X n e w ) E(X n e w) E(Xnew) 的概率为 P P P :

p = { 1  if  E ( x new  ) < E ( x old  ) exp ⁡ ( − E ( x new  ) − E ( x old  ) T )  if  E ( x new  ) ≥ E ( x old  ) p=\left\{\begin{array}{cll} 1 & \text { if } & E\left(x_{\text {new }}\right)<E\left(x_{\text {old }}\right) \\ \exp \left(-\frac{E\left(x_{\text {new }}\right)-E\left(x_{\text {old }}\right)}{T}\right) & \text { if } & E\left(x_{\text {new }}\right) \geq E\left(x_{\text {old }}\right) \end{array}\right. p={1exp(TE(xnew )E(xold )) if  if E(xnew )<E(xold )E(xnew )E(xold )

模拟退火原理

模拟退火算法(Simulated Annealin, SA)是一种元启发式的优化算法,用于寻找函数的全局最值,其工作原理为:

  1. 从一个状态 S S S移动到一个新的状态 S + 1 S + 1 S+1
  2. f ( S ) < f ( S + 1 ) f(S) < f(S + 1) f(S)<f(S+1),则接受该状态作为新解,若 f ( S ) > f ( S + 1 ) f(S) > f(S + 1) f(S)>f(S+1),则以一定概率接受该解,且概率随时间推移而降低,这里是随温度的降低来体现的
  3. 对初始温度降温, T 0 = r T 0 T_{0}= rT_{0} T0=rT0, r ∈ ( 0 , 1 ) r \in (0, 1) r(0,1) , r r r越大,温度降低越快,与神经网络的"学习率"有异曲同工之妙
  4. 重复以上三步直到 T 0 < T m i n T_{0}< T_{min} T0<Tmin

应用

求解旅行商(TSP)问题

题目描述

一个旅行商要前往 n n n个城市推销他的商品,然后回到他所在的城市,请找出一条路线让他能经过每一个城市且总路径最短

分析

该题的本质是寻找一副完全无向有权图中边权和最小的哈密顿回路,我们设从城市 i i i到城市 j j j的路程为 d i j d_{ij} dij,那么我们的暴力的解决方法显然是枚举出所有城市的全排列,然后依次计算出每一个排列的总距离,从中找出路程最小的排列即可,显然,暴力算法的时间复杂度 O ( n ! ) O(n!) O(n!),无法在多项式时间内解决

代码实现
import itertools
import numpy as np
import pandas as pd
import graphviz as gz

# 随机生成城市的距离

class TSP:

    def __init__(self, size):
        
        self.G_SIZE = size
        self.path = None
        self.D = np.random.randint(1, 20, (self.G_SIZE, self.G_SIZE))
        self.D = self.D - np.diag(self.D.diagonal())
        self.D = np.triu(self.D) +  np.triu(self.D).T

    def calc_distance(self, arr):
        dis = 0;
        for i in range(len(arr)-1):
            j = i + 1
            dis += self.D[arr[i]][arr[j]]
        return dis

    # 暴力搜索
    def dfs(self):
        ans = None
        path = None
        arr = np.array(list(itertools.permutations(np.arange(self.G_SIZE))))
        arr = np.hstack([arr,arr[:, 0].reshape(-1, 1)])
        # 计算距离
        for i in range(len(arr)):
            res = self.calc_distance(arr[i])
            if ans is not None:
                if ans > res:
                    ans = res
                    path = arr[i]
            else:
                ans = res
                path = arr[i]
        return ans, path

    # 模拟退火
    # 传入当前温度, 降温系数,最小温度,和距离矩阵
    def simulatedAnnealing(self, T, r, T_min):
        old_dis = None
        best_path = None
        while(T >= T_min):
            # 现在考虑状态转移的方式,出于方便,我们使用numpy的随机排列函数
            arr = np.arange(0, self.G_SIZE)
            np.random.shuffle(arr)
            # 尾部插入首元素成环
            arr = np.append(arr, arr[0])
            # 计算总距离
            dis = self.calc_distance(arr)
            if old_dis is not None:
                if(old_dis >= dis):
                    old_dis = dis
                    best_path = arr
                elif(old_dis < dis):
                    if(np.exp(-(dis - old_dis) / T) > np.random.rand(1)[0]):
                        old_dis = dis
                        best_path = arr
            else:
                old_dis = dis
                best_path = arr

            T *= r
            if(T < T_min):
                self.path = best_path
                return old_dis, best_path

    # 绘制图像:

    def draw_graph(self, path=None):
        if(self.path is not None and path is None):
            path = self.path
        #生成邻接矩阵记录连接情况

        mat = np.zeros((self.G_SIZE, self.G_SIZE))
        G = gz.Graph("TSP")
        for i in range(len(path)-1):
            j = i + 1;
            G.edge(str(path[i]), str(path[j]), color = 'red', label=str(self.D[path[i]][path[j]]))
            mat[path[i]][path[j]] = 1
            mat[path[j]][path[i]] = 1
        for i in range(G_SIZE):
            G.node(str(i))
            for j in range(i + 1, self.G_SIZE):
                if mat[i][j] == 1:
                    continue
                G.edge(str(i), str(j), label=str(self.D[i][j]))
        return G

性能分析

使用%time对暴力与模拟退火分别进行测时,得到如下结果:

暴力枚举:

输入规模n时间
5384 μ \mu μs
1020s
15-(内核炸了)

模拟退火(T = 10000, T_min = 1, r = 0.96):

输入规模n时间
53ms
103.77ms
154.36ms
⋮ \vdots ⋮ \vdots
10014.6ms

可见模拟退火算法的时间复杂度随T, T_min,r的影响而变化,且随机性较强,估算为 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n)

n=5时,解的图像如下:
TSP问题的解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值