模拟退火算法
算法思路
以找出函数 y = sinx + cosx 的最小值为例,函数图像如图(最小值为-1.414左右)
算法开始时,先定义我们的目标函数
def aim_functhion():
return np.sin(x) + np.cos(x)
1.先随机地找一个x,并带入到目标函数,确定其y值(能量)
current_solution = random.uniform(-10,10) #随机给定(-10,10)范围的一个浮点数
current_energy = aim_function(current_solution)# 带入函数求值
2.令最优解等于当前初始化的解,即:
best_solution = current_solution
best_energy = current_energy
3.进入循环
while temperature > min_temp: #这里是要让温度迭代到最低温度的
在循环内
1.生成新的解,代码的意思就是当前的解加上一个(-1,1)内随机给出的值(当然这个-1~1的范围不是固定的),这样就相当于让当前的x左移或者右移了一下,这样就相当于是探索了函数上另一点的函数值。
new_solution = current_solution + random.uniform(-1,1)
new_energy = aim_function(new_solution)
2.设置接受概率
这行代码就是,让这两个点对应的函数差值的绝对值取负号(得到的是一个负数),再除以当前的温度,这个整体的结果再作为e^x的指数。因为为负数,所以其取值就被限制在(0-1)内了,就可以视作是对应的概率值
accept_probability = math.exp(-math.fabs((new_energy - current_energy))/temperature)
这里就是终点了。因为我们要找的是函数的最小值,所以 if 的判断条件逻辑应为新的点对应的函数值要小于当前点对应的函数值,如果新的点的函数值大于了当前点的函数值,那么按理来说我们是不能接受的,但是为了避免陷入局部最优解的情况,我们要以一定的概率去接受这个比当前值还要大的情况,所以,or 后面紧接了 random.random() < accept_probability (random.random()返回的是0~1之间的一个值,即,视为概率) 这样我们就可以以一定的概率去接受这个值了。
下图展示的是局部最优解的一种情况,看图就更为直观了
if new_energy < current_energy or random.random() < accept_probability:
current_solution = new_solution
current_energy = new_energy
if new_energy < best_energy:
best_solution = new_solution
best_energy = new_energy
temperature *=cooling_rate
如果说满足第一层 if 条件,那么就将新的解赋值给当前解
第二层 if 则是对最优的解进行更替
最后,温度按照冷却速率进行更新。
总结
总体思路就是,一开始随机找一个点(记为x),并计算对应函数值。然后这个点左移或者右移一下进行更新(记为x’)。 用y(x) 和 y(x’)计算接受概率。
在算法的整体过程中,要注意的就是更新当前解x的逻辑,y(x’) 比 y(x) 小的,则一定会更新为当前解 (current_solution) 。如果y(x’) 比 y(x) 大的,那么会以一定的概率,被更新到当前解中。
完整代码
import math
import random
import matplotlib.pyplot as plt
import numpy as np
def aim_function(x):
return np.sin(x)+np.cos(x)
def simulated_annealing(aim_function, initial_temp, min_temp, cooling_rate):
#随机给定初始值
current_solution = random.uniform(-10,10)
current_energy = aim_function(current_solution)
#初始温度
temperature = initial_temp
#最优解
best_solution = current_solution
best_energy = current_energy
while temperature > min_temp:
#生成新解
new_solution = current_solution + random.uniform(-1,1)
new_energy = aim_function(new_solution)
#接受概率
accept_probability = math.exp(-math.fabs((new_energy - current_energy))/temperature)
if new_energy < current_energy or random.random() < accept_probability:
current_solution = new_solution
current_energy = new_energy
if new_energy < best_energy:
best_solution = new_solution
best_energy = new_energy
temperature *=cooling_rate
return best_solution, best_energy
if __name__ =="__main__":
initial_temp = 1000
min_temp = 1e-14
cooling_rate = 0.9
best_sol, best_energy = simulated_annealing(aim_function, initial_temp, min_temp, cooling_rate)
print(f"最优解:{best_sol},最优值:{best_energy}")```
# 写在最后
该算法中 还有很多值得讨论的点,这里我只是简单的介绍了一下思路和流程,如果有什么问题的话希望大家可以一起讨论,这样对算法理解才会更深刻。