目录
系列文章目录
文章目录
前言
一、模型的建立
二、算法的步骤
三、代码的实现
四、运行的结果
总结
我前面的博客用NSGA-II算法求解了多目标高次函数的帕累托前沿,本文打算用模拟退火算法求解同样的问题。
相关论文提到,模拟退火算法是一种全局优化算法,用于在大规模解空间中寻找极值。相较于其他常见的优化算法,模拟退火算法具有较好的全局发现能力,并且可以逃离局部最优解。
这是因为模拟退火算法具有一定的随机性和概率性,在搜索过程中可能会以一定概率接受劣解,从而避免陷入局部最优解。而遗传算法通常采用选择、交叉、变异等操作来进化个体,并不能保证每一次进化都是优化的,有可能会导致个体陷入局部最优解而不能再继续前进。另外,模拟退火算法可以通过不断减小温度来控制搜索的范围,并逐渐靠近全局最优解,因此也能够避免过早地收敛于局部最优解。
总之,模拟退火算法相比遗传算法更加灵活和鲁棒,具有一定的随机性和概率性,从而能够避免陷入局部最优解,取得更优的最优解。因此我们在研究求解多目标规划问题的时候,不得不了解一下模拟退火算法。
研究的模型为:min(y1=,x
[0,10]), min(y2=
,x
[0,10])。 即求解两个目标函数最小值的问题。
2.1 首先设置了一些参数,例如自变量取值范围、目标函数个数、初始温度、最小温度、降温速率、每个温度下的迭代次数等。然后定义了一个自变量的类,其中包含了计算目标函数值的方法。
2.2 定义了一个Individual类,用于表示种群中的每个个体。在这个类中,我们用self.x来表示该个体的自变量,用self.objs来表示该个体对应的目标函数值。
2.3 在接下来的simulated_annealing函数中,首先初始化种群。我们通过随机数生成初始个体,并将这些个体放入pop列表中。
2.4 在每个温度下进行迭代。每次迭代中,我们对pop列表中的每个个体进行处理,产生一个新的个体,并计算其对应的目标函数值。然后,根据一定的概率,我们要么接受新的个体,要么保留原有的个体。如果接受新的个体,则从pop列表中删除当前个体,并加入新的个体;同时,若新的个体比当前的全局最优解更好,我们还需要更新best_pop列表。相反,如果保留原有的个体,则不进行任何操作。
2.5 我们进入下一个温度。为了降低温度,我们将当前的温度乘以alpha,其中alpha为降温速率。
2.6 当温度降至最小值T_min时,模拟退火算法结束。我们输出所有的不可支配解(即帕累托前沿)到控制台,并在坐标系上以红色点的形式标出这些解。
2.7 最后,我们通过散点图将所有不可支配解在坐标系中进行可视化展示。其中灰色点表示所有的解,红色点表示帕累托前沿的解。(但是模拟退火算法求出的帕累托解往往只有几个或者一个,很难形成遗传算法那种优美的帕累托前沿曲线)。
代码如下:
import math
import random
import copy
import matplotlib.pyplot as plt
#设置参数
x_range = (-10, 10) #自变量取值范围
num_obj = 2 #目标函数个数
T_init = 50 #初始温度
T_min = 1e-6 #最小温度
alpha = 0.99 #降温速率
L = 100 #每个温度下的迭代次数
#定义自变量的类
class Individual:
def __init__(self, x):
self.x = x
self.objs = [None] * num_obj
#计算目标函数的值
def evaluate(self):
self.objs[0] = self.x * self.x
# self.objs[0] = (5 - self.x) ** 2
self.objs[1] = (2 - self.x) ** 2
#模拟退火
def simulated_annealing():
global T_init, T_min, alpha, L, x_range, num_obj
#初始化种群
init_x = random.uniform(*x_range)
pop = [Individual(init_x) for _ in range(L)]
#计算目标函数初始值
for ind in pop:
ind.evaluate()
#记录全局最优解
best_pop = copy.deepcopy(pop)
#开始模拟退火
T = T_init
while T > T_min:
for i in range(L):
#生成新解
new_x = pop[i].x + random.uniform(-1, 1) * (x_range[1] - x_range[0])
new_x = max(x_range[0], min(x_range[1], new_x))
new_ind = Individual(new_x)
new_ind.evaluate()
#接受新解
if new_ind.objs[0] < pop[i].objs[0] or (new_ind.objs[0] == pop[i].objs[0] and new_ind.objs[1] < pop[i].objs[1]):
pop[i] = copy.deepcopy(new_ind)
#更新全局最优解
if new_ind.objs[0] < best_pop[0].objs[0] or (new_ind.objs[0] == best_pop[0].objs[0] and new_ind.objs[1] < best_pop[0].objs[1]):
best_pop = [copy.deepcopy(new_ind)]
#接受差解
else:
delta_e = abs(new_ind.objs[0] - pop[i].objs[0]) + abs(new_ind.objs[1] - pop[i].objs[1])
p_accept = math.exp(-delta_e / T)
if random.random() < p_accept:
pop[i] = copy.deepcopy(new_ind)
#降温
T *= alpha
#输出最优解集合
pareto_front = set()
for ind in best_pop:
pareto_front.add(ind)
print("Pareto front:")
for ind in pareto_front:
print(f"x={ind.x:.4f}, y1={ind.objs[0]:.4f}, y2={ind.objs[1]:.4f}")
#可视化
plt.scatter([ind.objs[0] for ind in best_pop], [ind.objs[1] for ind in best_pop], c='gray', alpha=0.5)
plt.scatter([ind.objs[0] for ind in pareto_front], [ind.objs[1] for ind in pareto_front], c='r')
plt.xlabel('Objective 1')
plt.ylabel('Objective 2')
plt.show()
if __name__ == "__main__":
print("加入我的qq群,大家一起讨论管理、规划类问题的算法\n群里我会不定期上传一些我自己和其他大神的算法源码\n群号为:808756207")
#运行模拟退火算法
simulated_annealing()
结果未传。
模拟退火算法在通常情况的运行速度都比遗传算法快,在调整好初始温度、最小温度、降温速率等参数的情况下,较之遗传算法也比较容易跳出局部最优解,面向全局搜索我们想要的答案。