算法_针对二元最小值问题的粒子群算法_python实现

之前写过一个粒子群算法,但是经过仔细的研究 ,发现有很多可以改进的地方。首先将问题变难。
这次问题变成求解二元函数的最小值问题。

minf(x1,x2)=x14+x22+6x1+4x2 min f(x_1,x_2) = x_1^{4}+x_2^2+6*x_1+4*x_2

针对这个问题,修改后代码如下。

import numpy as np
import matplotlib.pyplot as plt
import copy


class PSO(object):
    def __init__(self, w, population_size, max_steps, x_bound, v_bound):
        self.w = w
        self.c1 = 2  # 惯性权重
        self.c2 = 2  # 惯性权重
        self.population_size = population_size                                              # 粒子群大小
        self.x_bound = x_bound                                                              # 粒子位置范围
        self.x = np.random.uniform(self.x_bound[0], self.x_bound[1], (population_size, 2))  # 初始化粒子位置
        self.v_bound = v_bound                                                              # 粒子速度范围
        self.v = np.random.uniform(self.v_bound[0], self.v_bound[1], (population_size, 2))  # 初始化粒子速度
        self.max_steps = max_steps                                                          # 最大迭代次数
        self.calculate_fitness(self.x)                                                      # 计算每个粒子适应度
        self.p = copy.deepcopy(self.x)                                                      # 每个粒子的历史最佳位置-x
        self.global_best_x = copy.deepcopy(self.x[np.argmin(fitness)])                      # 种群的粒子最佳位置-x
        self.individual_best_fitness = copy.deepcopy(fitness)                               # 个体的最优适应度-y
        self.global_best_fitness = np.min(fitness)                                          # 全局最佳适应度-y

    def calculate_fitness(self, x):
        for i in range(self.population_size):
            fitness[i] = x[i, 0] * x[i, 0] + x[i, 1] * x[i, 1] + 6 * x[i, 0] + 4 * x[i, 1]  # 100×2大小的矩阵
        return fitness

    def evolve(self):
        fig = plt.figure()
        for j in range(self.max_steps):
            r1 = np.random.rand(self.population_size, 2)
            r2 = np.random.rand(self.population_size, 2)
            self.v = self.w * self.v + self.c1 * r1 * (self.p - self.x) + self.c2 * r2 * (self.global_best_x - self.x)
            self.x = self.v + self.x
            plt.clf()  # Clear figure清除所有轴,但是窗口打开,这样它可以被重复使用
            plt.scatter(self.p[:, 0], self.p[:, 1], s=self.population_size, color='r')
            plt.xlim(self.x_bound[0], self.x_bound[1])
            plt.ylim(self.x_bound[0], self.x_bound[1])
            plt.pause(0.01)  # python每次画完图像后暂停0.01秒
            self.calculate_fitness(self.x)
            # 新一代粒子和前代粒子比较,如果新一代粒子的适应度比前代更好地,更新每个粒子的历史最佳位置,更新适应度
            for k in range(self.population_size):
                if fitness[k] < self.individual_best_fitness[k]:
                    self.individual_best_fitness[k] = fitness[k]
                    self.p[k] = self.x[k]
            # 新一代出现了更小的fitness,所以更新全局最优fitness和位置
            if np.min(fitness) < self.global_best_fitness:
                self.global_best_x = self.x[np.argmin(fitness)]
                self.global_best_fitness = np.min(fitness)
            B.append(self.global_best_fitness)
        print(self.global_best_x)
        print(self.global_best_fitness)


fitness = np.zeros((50, 1))
A = B = []
pso = PSO(0.5, 50, 51, [-10, 10], [-10, 10])
pso.evolve()
print("y = %s" % B)
x = range(100)
plt.plot(B)
B1 = B[0:100:10]
x1 = x[0:100:10]
for a, b in zip(x1, B1):
    plt.text(a, b, '%.4f' % b, ha='center', va='bottom', fontsize=9)
plt.show()

图 粒子群最佳适应度变化曲线图
算法_针对二元最小值问题的粒子群算法_python实现
这次因为有两个自变量,我还做了一个二维图像去观察粒子分布图,这里选取了几张图。
这是第一代粒子分布
图 粒子分布图
算法_针对二元最小值问题的粒子群算法_python实现
中间某代。
算法_针对二元最小值问题的粒子群算法_python实现
第100代粒子分布,其实不到100代已经是这个样子了。
算法_针对二元最小值问题的粒子群算法_python实现
从这几张图像可以表现粒子群算法的含义,每个粒子都能思考群体及个体的信息,从而每个粒子都最终跑到了能到最优适应度的位置。

现在这个算法基本上是实现了粒子群算法,当然要针对不同的问题再进行修改,但是编程思路不会变。
说几个编程时遇到的bug。
bug
最大的bug是,初始版本的算法是针对一元函数编写的,并没有画出粒子的分布,在我将问题改成二元函数求最小值后,我把粒子分布图画了出来,发现算法每次迭代后,粒子分布没有改变,但是算法还是能找到最优函数值及位置的。仔细思考后,原来算法找到最优函数值及位置并不是凭借粒子群算法的智能找到的,而是凭借类似地毯式搜索的方式找到的,举个例子就是最优解在0.5处,当你在[0,1]中按均匀分布找100个数,当然有一定概率找到这个最优值。发现这个问题后,检查了程序,整体思路和运行都是符合粒子群算法的,我就往是不是语句出了问题,我在debug模式下,发现self.x和self.p这两个array一直是一样的,原来问题出在了array的赋值上,具体请看这个博客https://www.cnblogs.com/xueli/p/4952063.html。
修改后粒子立马智能起来了,非常开心,因为这个大bug找了2-3天。

再说一下粒子群算法本身,因为算法在速度更新公式上含几个超参数,这几个超参数的设定还是有一点讲究的,所以在出现问题时我也查阅了相关论文,看别人是如何设定这几个超参数,当然如果出现算法不收敛的问题,也有可能是参数设定有问题。

后续工作就是改进粒子群算法了,虽然这个算法和最原始提出的粒子群算法已经有一点改进。我打算先看几种针对不同问题作不同改进的粒子群算法,再看看自己的问题,应该做出哪种改进。