用模拟退火求解 y = 0.1x*sin(x)的最小值问题
import numpy as np
import pandas as pd
import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
class SA(object):
def __init__(self, func):
self.max_T = 100 #起始温度
self.min_T = 0.002 #最小温度
self.search_num = 50 #每轮搜索的次数
self.t = 1 #用于计数
self.best_x = None
self.best_y = None
self.T = self.max_T
self.func = func
self.low = -20 #定义域
self.high = 20
self.best_xs = []
self.best_ys = []
def update_value(self, x, y, x_new, y_new):
"""更新x, y"""
P = np.exp(-np.abs(self.func(x_new) - self.func(x))/ self.T) #Metropolis准则
# print(P)
if (np.random.random() < P) or (y_new < y):
y = y_new
x = x_new
if y < self.best_y:
self.best_y = y
self.best_x = x
self.best_xs.append(x)
self.best_ys.append(y)
return x, y
def run(self):
"""主函数"""
x = np.random.uniform(self.low, self.high)
y = self.func(x)
self.best_x = x
self.best_y = y
x_new = x
y_new = y
while self.T >= self.min_T: # 在达到最小温度前一直循环
for n in range(self.search_num): # 在每个温度点搜寻50次
delta_x = np.random.uniform(-0.5, 0.5) #寻找x附近x_new的步长,可理解为learning_rate
x_temp = x + delta_x
if (x_temp < self.high) and (x_temp > self.low):
x_new = x_temp
y_new = self.func(x_new)
x, y =self.update_value(x, y, x_new, y_new)
self.t += 1
self.T = self.max_T/(1 + self.t)
# print(self.best_x)
# print(self.best_y)
# ax.scatter(self.best_x, self.best_y, c = 'b', cmap = 'rainbow')
def update_points(num, x , y):
"""更新数据点用于画动图"""
points.set_data(x[num], y[num])
return points,
if __name__ == '__main__':
def func(x):
y = 0.1 * x * np.sin(x)
return y
x = np.linspace(-20, 20, 1000)
y = func(x)
fig = plt.figure(figsize = (9, 6))
plt.plot(x, y, c = 'r')
ax = plt.gca()
sa = SA(func)
sa.run()
points, = ax.plot([], [], 'ro')
ani = animation.FuncAnimation(fig, update_points, range(len(sa.best_xs)), fargs=(sa.best_xs, sa.best_ys), interval= 50)
"""
fig : 当前画布
update_points : 每一帧调用的函数,第一个参数为 frame
frames : 帧 传入可迭代对象
fargs : 传入update_point的其他参数
interval : 每帧之间的时间, 理解为gif播放速度
"""
ani.save('./samples/sa1.gif')
plt.show()