7.Bolzmann机解决旅行商问题

时间:2022-06-09 23:49:00
# -*- coding: utf-8 -*-
# Filename : Boltzmann.py

import operator
from numpy import *
import copy
import matplotlib.pyplot as plt


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):
        numFeat = len(open(fileName).readline().split('\t')) - 1
        dataMat = [];
        fr = open(fileName)
        for line in fr.readlines():
            lineArr = []
            curLine = line.strip().split('\t')
            lineArr.append(float(curLine[0]));
            lineArr.append(float(curLine[1]));
            dataMat.append(lineArr)
        self.dataMat = mat(dataMat)

    def distEclud(self, matA, matB):  # 计算矩阵各向量之间的距离--欧氏距离
        ma, na = shape(matA);
        mb, nb = shape(matB);
        rtnmat = zeros((ma, nb))
        for i in range(ma):
            for j in range(nb):
                rtnmat[i, j] = linalg.norm(matA[i, :] - matB[:, j].T)
        return rtnmat

    # 计算路径长度
    def pathLen(self, dist, path):
        N = len(path)
        plen = 0;
        for i in range(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 random.rand() < 0.25:  # 产生两个位置,并交换
            chpos = floor(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(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]
            else:
                new_path = self.changePath(old_path)
        return new_path

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

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

    # 训练样本
    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 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]

    # 绘制路径
    def drawPath(self, plt, color='b'):
        m, n = shape(self.dataMat)
        px = (self.dataMat[self.bestpath, 0]).tolist()
        py = (self.dataMat[self.bestpath, 1]).tolist()
        px.append(px[0]);
        py.append(py[0])
        plt.plot(px, py, color)

    # 绘制散点
    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(range(len(self.dist)), self.dist, color)

bmNet = BoltzmannNet()
bmNet.loadDataSet(r"D:\Boltzmann\dataSet25.txt")
bmNet.train()
bmNet.drawScatter(plt)
bmNet.drawPath(plt)
plt.show()

#绘制误差算法收敛曲线
plt.show()

7.Bolzmann机解决旅行商问题