import matplotlib;
matplotlib.use('TkAgg')
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
mpl.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题
import numpy as np
from tqdm import tqdm # 进度条设置
import random
# 更改了位置更新函数,
class PBO(object):
""""
M:种群最大容量
m:种群当前规模大小
N:目标函数解的维度
T:迭代次数
V:最大视野距离
P:初始化种群数量百分比
K:动态调整规模参数
B:变量限制范围
X:解集合
"""
def __init__(self):
self.M =50
self.N= 2
self.T= 200
self.V= 0.3
self.P= 0.75
self.K= 0.25
self.x_min = -32 #暂时设置每个维度的可移动范围相同,实际需要根据N个维度进行设置
self.x_max = 32
self.m = int(np.floor(self.M*self.P)) #随后期的繁衍和死亡会变化
# 初始化种群
def init_x(self):
"""
:return:
"""
X = np.zeros((self.M, self.N))
X[:self.m] = np.random.random((self.m, self.N)) * (self.x_max - self.x_min) + self.x_min
return X
def fitness(self,X):
"""
有待更改
:param X:
:return:
"""
# y = X[0]+X[1]+X[2]+X[3]+X[4]+X[5]+X[6]+X[7]+X[8]+X[9]
y = -1*np.sum(X**2)
return y
def up_data(self,index,angle,radius,action,X):
"""
局部搜索中的位置更新公式 公式看原版文献csdn有误
:param index:每一只熊的的编号
:param angle:angles [0,2pi]
:param X[index][0]:第index只熊的0维
:param radius:
:param action:
:param X:
:return:
"""
temp = np.zeros(self.N)
result = 0
if "add".__eq__(action):
x_temp = X[index][0] + radius * np.cos(angle[0])
else:
x_temp = X[index][0] - radius * np.cos(angle[0])
if x_temp>=self.x_min and x_temp<=self.x_max:
temp[0] = x_temp
elif x_temp<self.x_min :
temp[0] = self.x_min
elif x_temp>self.x_max:
temp[0] = self.x_max
#因为从第2到n-1只熊由通用计算工时
for i in range(self.N-2):
result += np.sin(angle[i])
if "add".__eq__(action):
x_temp = X[index][i+1] + radius * (result+np.cos(angle[i+1]))
else:
x_temp = X[index][i+1] - radius * (result+np.cos(angle[i+1]))
if x_temp >= self.x_min and x_temp <= self.x_max:
temp[i + 1] = x_temp
elif x_temp < self.x_min:
temp[i + 1] = self.x_min
elif x_temp > self.x_max:
temp[i + 1] = self.x_max
if "add".__eq__(action):
x_temp = X[index][self.N-1] + radius * (result + np.sin(angle[self.N-2]))
else:
x_temp = X[index][self.N-1] - radius * (result + np.sin(angle[self.N-2]))
if x_temp >= self.x_min and x_temp <= self.x_max:
temp[self.N-1] = x_temp
elif x_temp < self.x_min:
temp[self.N-1] = self.x_min
elif x_temp > self.x_max:
temp[self.N-1] = self.x_max
return temp
def move_local(self,X):
"""
局部更新 (包含搜索半径 和局部更新)
:param X:
:return:
r=4acos(φ)sin(φ)
"""
# 对每个北极熊设计随机角度 根据公式计算出搜索半径 r=4acos(φ)sin(φ)
for i in range(self.m):
angles = np.random.random(self.N-1) * 2 * np.pi # [0,2pi]用于局部搜索中的位置更新
sta = np.random.random() * self.V # [0,0.3]
angle0 = np.random.random() * np.pi / 2 # [0,pi/2]
# 搜索半径
r = 4 * sta * np.cos(angle0) * np.sin(angle0)
# using the sign of add
tmp = self.up_data(i, angles, r, 'add', X)
if self.fitness(tmp) > self.fitness(X[i]):
X[i] = tmp
else:
tmp = self.up_data(i, angles, r, 'minus', X)
if self.fitness(tmp) > self.fitness(X[i]):
X[i] = tmp
return X
def move_globle(self,X,xbest):
"""
浮水漂移,全局搜索
X:M行N列的二维矩阵 ,每一个子矩阵代表一个多纬度的北极熊
全局位置更新
:return:
"""
for i in range(self.m):
# 每一只熊的W
w = np.sqrt(np.sum(xbest - X[i]) ** 2) #w是n-1个维度的欧氏距离
alpha = np.random.random()
gamma = np.random.random() * w # 0-w之间的随机数
x_temp = X[i] + np.sign(w) * alpha + gamma
x_temp[x_temp<self.x_min] = self.x_min
x_temp[x_temp>self.x_max] = self.x_max
if self.fitness(x_temp) > self.fitness(X[i]):
X[i] = x_temp
return X
def fit_best(self,X,f_best,x_best):
fits = np.zeros(self.m)
for i in range(self.m):
#找个体最优
fits[i] = self.fitness(X[i])
arg = np.argsort(fits[:self.m])
if fits[arg[-1]]>=f_best:
f_best = fits[arg[-1]]
x_best = X[arg[-1]]
return f_best,x_best,fits
def zhongqunfangyan(self,X,x_best,fits):
k = np.random.random()
arg = np.argsort(fits[:self.m])
# 种群繁衍
if self.m < self.M - 1 and k > 0.75:
# 从前10%中随机抽取一个 个体与最好个体交配 生成新的个体
idx = random.randint(1, np.floor(self.m * 0.1) + 1)
X[self.m] = (x_best + X[arg[self.m-1-idx]]) / 2
self.m += 1
# 最弱个体死亡
if self.m > self.M * 0.5 and k < 0.25:
# arg中排序最后的为最弱个体
# 死亡之后 后面的所有个体往前移动一个位置
X[arg[0]:self.m] = X[arg[0] + 1:self.m + 1]
self.m -= 1
return X
def main(self):
fitness_list = [] #画图使用
x_best_list=[]
X = self.init_x()
f_best = -1*np.inf #表示负无穷
x_best = 0
f_best,x_best,fits = self.fit_best(X,f_best,x_best)
fitness_list.append(f_best)
#先局部后全局搜索
for i in tqdm(range(self.T)):
X = self.move_local(X)
X = self.move_globle(X,x_best)
print(i)
#计算当前适应度
f_best, x_best,fits= self.fit_best(X, f_best, x_best)
fitness_list.append(f_best)
print(x_best)
#种群动态变化
X = self.zhongqunfangyan(X,x_best,fits)
f_best, x_best, fits = self.fit_best(X, f_best, x_best)
print("最大适应度值:", f_best, '个体为:', x_best)
plt.plot(fitness_list, "--")
plt.xlabel('迭代次数', size=15)
plt.ylabel('fitness', size=15)
plt.legend()
plt.show()
if __name__ == '__main__':
pbo = PBO()
pbo.main()