算法原理

      旅行商问题是一个经典的NP问题,假设有N个城市,需要确定一个访问顺序,使得每个城市都访问一面,最后回到起点城市,且保证行走的总距离最短。
       假设随机生成10个城市坐标,城市之间的距离用欧式距离表示。

       在以往例子中,粒子xi的值是具有意义的值,而这里需要优化序列的顺序,因此粒子xi如何表示序列以及序列的改进是粒子群算法的一个关键所在。同时也应该认识到,虽然智能算法的原理比较容易理解,但在不同应用里,巧妙的构造解xi的表示形式,以及解xi的进化方式,才是关键。

       在求解TSP问题时,可以用一个序列s表示城市的访问顺序,因此xi可以表示成如下形式:
                            xi=(1,2,3,4,5)
       式中表示有5个城市的TSP问题,其访问顺序为1-3-2-5-4-1.因此全部城市的所有可能序列构成了问题的搜索空间,例子位置的更新意味着粒子xi从所有城市的一种序列si变成另一种序列sj。
       那么序列是如何变化得,这里引入交换子 和交换序列的概念。
      交换子定义为s=Swap x(i,j)表示交换子s作用在序列x上使位置i的元素和位置j的元素相互交换,如x=(1,3,2,5,4),对其添加一个交换操作s=Swap x(1,3),其新的序列为x=(2,3,1,5,4),在粒子群算法中,交换子可以看作是粒子的速度,能改变x的位置。
      交换序列 定义为一组有前后顺序的交换子的集合,即ss=[Swap1,Swap2…],表示对序列x进行多次交换子交换操作,如x=(1,3,2,5,4),交换序列ss=[(3,2),(1,5)],当序列进行交换序列后,新的序列为x=(4,2,3,5,1)
      解释了交换子和交换序列后,重新定义粒子的速度和更新公式:

      式中Pbesti-xi表示有一个交换序列ss,它使xi经过ss变换得到Pbest,同理Gbesti-xi也是一个交换序列ss,符号⨁表示两个交换序列合并一个交换序列,如[(3,2),(1,5)]⨁[(4,2)]=[(3,2),(1,5),(4,2)];符号+表示对一个序列按照交换序列ss执行交换操作。因此位置更新公式表示一个粒子经过交换序列Vi(t+1) 作用得到新的序列。r1,r2表示0-1之间的随机数,表示一定概率进行交换子操作。

粒子群算法原理路见往期:粒子群算法求解无约束优化问题 源码实现

算例

假设随机生成10个城市坐标

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns

sns.set_style("whitegrid")
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

# 固定随机数种子
np.random.seed(1234)

# 一些参数
city_num = 10  # 城市的数量
iter_num = 1000  # 算法最大迭代次数

# 随机生成city_num个城市的坐标,注意是不放回抽样
X = np.random.choice(list(range(1, 100)), size=city_num, replace=False)
Y = np.random.choice(list(range(1, 100)), size=city_num, replace=False)

plt.scatter(X, Y, color='r')
plt.title('城市坐标图')
plt.show()

步骤1:初始化参数

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import seaborn as sns

sns.set_style("whitegrid")
mpl.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

# 固定随机数种子
np.random.seed(1234)

# 一些参数
city_num = 10  # 城市的数量
size = 50  # 种群大小,即粒子的个数
r1 = 0.7  # pbest-xi 的保留概率
r2 = 0.8  # gbest-xi 的保留概率
iter_num = 1000  # 算法最大迭代次数
fitneess_value_list = []  # 每一步迭代的最优解

# 随机生成city_num个城市的坐标,注意是不放回抽样
X = np.random.choice(list(range(1, 100)), size=city_num, replace=False)
Y = np.random.choice(list(range(1, 100)), size=city_num, replace=False)

步骤2:距离函数 和适应度函数


# 计算城市之间的距离
def calculate_distance(X, Y):
    """
    计算城市两辆之间的欧式距离,结果用numpy矩阵存储
    :param X: 城市的X坐标,np.array数组
    :param Y: 城市的Y坐标,np.array数组
    """
    distance_matrix = np.zeros((city_num, city_num))
    for i in range(city_num):
        for j in range(city_num):
            if i == j:
                continue
            dis = np.sqrt((X[i] - X[j]) ** 2 + (Y[i] - Y[j]) ** 2)  # 欧式距离计算
            distance_matrix[i][j] = dis
    return distance_matrix

def fitness_func(distance_matrix, xi):
    """
    适应度函数,计算目标函数值.
    :param distance: 城市的距离矩阵
    :param xi: PSO的一个解
    :return: 目标函数值,即总距离
    """
    total_distance = 0
    for i in range(1, city_num):
        start = xi[i - 1]
        end = xi[i]
        total_distance += distance_matrix[start][end]
    total_distance += distance_matrix[end][xi[0]]  # 从最后一个城市回到出发城市
    return total_distance

步骤3:绘制最优解的图像函数

def plot_tsp(gbest):
    """绘制最优解的图形"""
    plt.scatter(X, Y, color='r')
    for i in range(1, city_num):
        start_x, start_y = X[gbest[i - 1]], Y[gbest[i - 1]]
        end_x, end_y = X[gbest[i]], Y[gbest[i]]
        plt.plot([start_x, end_x], [start_y, end_y], color='b', alpha=0.8)
    start_x, start_y = X[gbest[0]], Y[gbest[0]]
    plt.plot([start_x, end_x], [start_y, end_y], color='b', alpha=0.8)

步骤4:交换序列函数 和交换操作

import numpy as np
xi=np.array([1,2,3,4,5,6,7,8,9,10])
xbest=np.array([2,1,4,3,6,5,7,8,9,10])
velocity_ss = []
for i in range(len(xi)):
      if xi[i] != xbest[i]:
      j = np.where(xi == xbest[i])[0][0]
       print(j)
      xi[i], xi[j] = xi[j], xi[i] #表示去掉重复计算,不同处只计算一处
# j=1,3,5

#交换序列函数
def get_ss(xbest, xi, r):#r=r1或r2
    """
    计算交换序列,即 x2 经过交换序列ss得到x1,对应PSO速度更新公式的:
    r1(pbest-xi) 和 r2(gbest-xi)
    :param xbest: pbest 或者 gbest
    :param xi: 例子当前解
    :return:
    xbest#最优解
    """
    velocity_ss = []
    for i in range(len(xi)):
        if xi[i] != xbest[i]:#如果当前索引粒子元素不等于最优粒子元素
            j = np.where(xi == xbest[i])[0][0]#找到当前粒子和最优粒子不同的元素所在索引
            so = (i, j, r)  # 得到交换子,表示以r的概率对i,j进行操作
            velocity_ss.append(so)
            xi[i], xi[j] = xi[j], xi[i]  # 执行交换操作
    return velocity_ss#返回交换子序列
 #交换操作,返回序列xi解
def do_ss(xi, ss):
    """
    执行交换操作
    :param xi:
    :param ss: 由交换子组成的交换序列
    :return:
    """
    for i, j, r in ss:
        rand = np.random.random()
        if rand <= r:
            xi[i], xi[j] = xi[j], xi[i]
    return xi

步骤5:PSO
      第一步:计算城市之间的距离矩阵

# 计算城市之间的距离矩阵
distance_matrix = calculate_distance(X, Y)

      第二步:初始化种群的各个粒子的位置,作为个体的历史最优解pbest。用一个 50*10 的矩阵表示种群,每行表示一个粒子, 每一行是0-9不重复随机数,表示城市访问的顺序

XX = np.zeros((size, city_num), dtype=np.int)
for i in range(size):
    XX[i] = np.random.choice(list(range(city_num)), size=city_num, replace=False)

      第三步:计算每个粒子对应适应度

pbest = XX#初始化粒子最优解
pbest_fitness = np.zeros((size, 1))
for i in range(size):
    pbest_fitness[i] = fitness_func(distance_matrix, xi=XX[i])

      第四步:计算全局适应度和对应的解gbest

gbest = XX[pbest_fitness.argmin()]
gbest_fitness = pbest_fitness.min()

      第五步:迭代寻优

# 记录算法迭代效果
fitneess_value_list.append(gbest_fitness)

# 下面开始迭代
for i in range(iter_num):#遍历迭代次数
    for j in range(size):  # 对每个粒子迭代
        pbesti = pbest[j].copy()  # 此处要用copy,不然出现浅拷贝问题
        xi = XX[j].copy()
        # 计算交换序列,即 v = r1(pbest-xi) + r2(gbest-xi)
        ss1 = get_ss(pbesti, xi, r1)

        ss2 = get_ss(gbest, xi, r2)
        ss = ss1 + ss2
        # 执行交换操作,即 x = x + v
        xi = do_ss(xi, ss)
        # 判断是否更优
        fitness_new = fitness_func(distance_matrix, xi)
        fitness_old = pbest_fitness[j]
        if fitness_new < fitness_old:
            pbest_fitness[j] = fitness_new
            pbest[j] = xi
    # 判断全局是否更优# 记录算法迭代效果
    # fitneess_value_list.append(gbest_fitness)
    gbest_fitness_new = pbest_fitness.min()
    gbest_new = pbest[pbest_fitness.argmin()]
    if gbest_fitness_new < gbest_fitness:
        gbest_fitness = gbest_fitness_new
        gbest = gbest_new
    # 加入到列表
    fitneess_value_list.append(gbest_fitness)


      第六步:绘制结果

# 打印迭代的结果
print('迭代最优结果是:', gbest_fitness)
print('迭代最优变量是:', gbest)
# 迭代最优结果是: 230.344
# 迭代最优变量是: [5 8 2 3 6 1 7 0 4 9]

# 绘制TSP路径图
plot_tsp(gbest)
plt.title('TSP路径规划结果')
plt.show()

往期TSP回顾
基于组合遗传粒子群算法的旅行商问题求解
遗传算法求最短路径(旅行商问题)python实现

在这里插入图片描述
作者:电气余登武

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐