Boltzmann机算法(模拟退火算法)(郑捷 著)

1. 问题的提出

1)TSP问题可以描述:
假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。
2)TSP的应用:
运输路线问题,装配线上的螺帽问题,在排产计划、电路板布线、基因测序和机器人控制等方面有着重要的应用。

2. 模拟退火原理

退火的物理学解释:
缓慢的降温过程释放了材料中的内能,使停留在局部最小值位置(内能)的原子离开原来位置,而随机地在材料内部移动,这就有可能找到比原先更低内能的位置。要达到这个目标,退火过程一般分为三个阶段:加温过程、等温过程、冷却过程。
从算法所需的角度而言,如果把材料看作整个数据集,降温过程看作算法的迭代过程,“释放材料内部应力”意味着激活函数求解的计算,那么找到全局最优的可能性就随着迭代次数增加(随着温度的下降)而增加,就认为寻找到数据集中所有的可能解,而其中最小(或最大)的那个解就近似地看作全局最优解,即以概率1收敛于全局最优解。

3. Boltzmann分布(玻尔兹曼分布)

在统计学和数学中,玻尔兹曼分布是系统各种可能的状态的一个概率分布、概率测度或粒子频度分布。该分布在统计力学中的表达形式为:
这里写图片描述
选择问题对于优化结果就会产生很大的影响:
1)初始温度的设置会影响算法执行的时间;初始温度过高,会导致运行时间过长,不足则容易漏掉全局最优点
2)降温速度的快慢,过快的降温往往会漏掉全局最优点,使算法收敛于某个局部最优;如果速度太慢,则会延迟得到全局最优的时间,从而影响求解问题的规模。

4. 降温策略

这里写图片描述

# -*- coding:utf-8 -*-
"""
Boltzmann机求解TSP问题
completation by20180825
"""
import numpy as np
from numpy import *
import copy
import matplotlib.pyplot as plt
import pandas as pd
import xlrd

class BoltzmannNet(object):
    def __init__(self):
        self.dataMat = [] # 外部导入的数据集
        self.MAX_ITER = 2000  # 外循环迭代次数
        self.T0 = 1000        # 最高温度:这个温度的变化范围应位于最大迭代范围之内
        self.Lambda = 0.97    # 温度下降参数
        self.iteration = 0    # 迭代最优时的迭代次数
        self.dist = []        # 存储生成的距离
        self.pathindx = []    # 存储生成的路径索引
        self.bestdist = 0     # 生成的最优路径长度
        self.bestpath = []    # 生成的最优路径

    def loadDataSet(self,filename):
        self.dataMat = []
        fr = open(filename)
        for line in fr.readlines():
            lineArr = line.strip().split()
            self.dataMat.append([float(lineArr[0]), float(lineArr[1])])
        self.dataMat = mat(self.dataMat)
        return self.dataMat


    def distEclud(self, vecA, vecB):
        # 欧式距离
        distMat = zeros((vecA.shape[0], vecA.shape[0]))
        for i in range(len(vecA)):
            for j in range(len(vecA)):
                distMat[i][j] = linalg.norm(vecA[i] - vecB[:, j])
        return distMat


    def boltzmann(self,newl,oldl,T): # 玻尔兹曼函数
        return exp(-(newl-oldl)/T)


    def pathLen(self,dist,path): # 计算路径长度
        N = len(path)
        plen = 0
        for i in arange(0,N-1): # 长度为N的向量,包含1~N的整数
            plen += dist[path[i],path[i+1]]
        plen += dist[path[0],path[N-1]]
        return plen

    def changePath(self,old_path): # 交换新旧路径
        N = len(old_path)
        if np.random.rand() < 0.25: # 产生两个位置,并交换
            chpos = floor(np.random.rand(1,2)*N).tolist()[0]
            new_path = copy.deepcopy(old_path)
            new_path[int(chpos[0])] = old_path[int(chpos[1])]
            new_path[int(chpos[1])] = old_path[int(chpos[0])]
        else:  # 产生三个位置,交换a-b和b-c
            d = ceil(np.random.rand(1,3)*N).tolist()[0]
            d.sort() # 随机路径排序
            a = int(d[0])
            b = int(d[1])
            c = int(d[2])
            if a != b and b != c:
                new_path = copy.deepcopy(old_path)
                #new_path[a:c-1] = old_path[b-1:c-1]+ old_path[a:b-1] # python2 列表的某段替换
                new_path[a:a + c - b] = copy.copy(old_path[b - 1:c - 1]) # 与上等价
                new_path[a + c - b:c - 1] = copy.copy(old_path[a:b - 1])
                #new_path=new_path.tolist() # 数组转列表
            else:
                new_path = self.changePath(old_path)
        return new_path

    def drawPath(self,plt):# 绘制路径
        px = []
        py = []
        for Seq in self.bestpath:
            px.append(self.dataMat[Seq,0])
            py.append(self.dataMat[Seq,1])
        plt.plot(px,py,'b')


    def drawScatter(self,plt): # 绘制散点图
        px = (self.dataMat[:,0]).tolist()
        py = (self.dataMat[:,1]).tolist()
        plt.scatter(px,py,c='green',marker='o',s=60)
        i=65
        for x,y in zip(px,py):
            plt.annotate(str(chr(i)),xy=(x[0]+40,y[0]),color='black')
            i += 1

    def TrendLine(self,plt,color='b'): # 绘制趋势线
        plt.plot(arange(len(self.dist)),self.dist,color)

    def initBMNet(self,m,n,distMat): # 构造一个初始可行解
        self.pathindx = arange(m).tolist()
        np.random.shuffle(self.pathindx) # 随机生成每个路径
        self.dist.append(self.pathLen(distMat,self.pathindx)) # 每个路径对应的距离
        return self.T0,self.pathindx,m

    # 最短路径的实现
    # 1.导入数据,并根据配置参数初始化网络
    def train(self): # 主函数
        m,n = shape(self.dataMat)
        distMat = self.distEclud(self.dataMat,self.dataMat.T) # 转换为邻接矩阵(距离矩阵)
        # T为当前温度,curpath为当前路径索引,MAX_M为内循环最大迭代次数
        [T,curpath,MAX_M] = self.initBMNet(m,n,distMat)
        step = 0 # 初始化外循环迭代
        while step <= self.MAX_ITER: # 外循环
            m = 0 # 内循环计数器
            while m <= MAX_M:        # 内循环
                curdist = self.pathLen(distMat,curpath) # 计算当前路径距离
                newpath = self.changePath(curpath)      # 交换产生新路径
                newdist = self.pathLen(distMat,newpath) # 计算新路径距离
                # 判断网络是否是一个局部稳态
                if (curdist > newdist):
                    curpath = newpath
                    self.pathindx.append(curpath)
                    self.dist.append(newdist)
                    self.iteration += 1   # 增加迭代次数
                else: # 如果网络处于局部稳态,则执行玻尔兹曼函数
                    if np.random.rand() < self.boltzmann(newdist,curdist,T):
                        curpath = newpath
                        self.pathindx.append(curpath)
                        self.dist.append(newdist)
                        self.iteration += 1  # 增加迭代次数
                m += 1
            step += 1
            T = T*self.Lambda # 降温,返回迭代,直至算法终止

        # 提取最优路径
        self.bestdist = min(self.dist)
        indxes = argmin(self.dist)
        self.bestpath = self.pathindx[indxes]

if __name__ == "__main__":
    # 加载
    bmNet = BoltzmannNet()
    bmNet.loadDataSet("dataSet25.txt")
    bmNet.train()
    print("循环迭代",bmNet.iteration,"次")
    print("最优解:",bmNet.bestdist)
    print("最佳路线:",bmNet.bestpath)
    # 显示优化后的城市图,路径图
    bmNet.drawScatter(plt)
    bmNet.drawPath(plt)
    plt.show()
    # 绘制误差算法收敛曲线
    bmNet.TrendLine(plt)
    plt.show()

数据:

6734 1453
2233 10
5530 1424
401 841
3082 1644
7608 4458
7573 3716
7265 1268
6898 1885
1112 2049
5468 2606
5989 2873
4706 2674
4612 2035
6347 2683
6107 669
7611 5184
7462 3590
7732 4723
5900 3561
4483 3369
6101 1110
5199 2182
1633 2809
4307 2322
675 1006
7555 4819
7541 3981
3177 756
7352 4506
7545 2801
3245 3305
6426 3173
4608 1198
23 2216
7248 3779
7762 4595
7392 2244
3484 2829
6271 2135
4985 140
1916 1569
7280 4899
7509 3239
10 2676
6807 2993
5185 3258
3023 1942

结果:
循环迭代 5395 次
最优解: 203879.287844
最佳路线: [5, 36, 18, 16, 26, 42, 29, 27, 35, 17, 30, 37, 8, 7, 0, 39, 22, 2, 21, 15, 40, 28, 44, 1, 3, 25, 34, 9, 41, 23, 47, 4, 33, 13, 38, 31, 24, 12, 20, 46, 10, 11, 14, 19, 32, 45, 43, 6]
这里写图片描述
这里写图片描述

参考:

https://www.cnblogs.com/tuhooo/p/5440473.html
http://www.cnblogs.com/hhh5460/p/4319652.html
https://blog.csdn.net/changdejie/article/details/78102894
http://blog.sina.com.cn/s/blog_c0ea025f0102xhk0.html

猜你喜欢

转载自blog.csdn.net/jyh_AI/article/details/82049822