模拟退火算法学习笔记
概念:
冶炼金属的内能退化规律。
优点:避免推理中达到局部最优解,通过随机事件大概率达到全局最优。
算法原理:
随机的启发搜索算法。
包含三个部分:初始解,解空间,目标函数。意义上分别为初始状态,中途过程,优化目标。
(1)初始解:
算法迭代起点,第一个解。
(2)解空间:
可行解集合。
(3)目标函数:
对于最优解的描述。
算法流程图如下:
降温概率:
系统温度为
T
T
T时,出现能量差为
d
E
dE
dE的***降温概率***为:
p
(
d
E
)
=
e
d
E
k
T
p(dE)=e^{\frac{dE}{kT}}
p(dE)=ekTdE
求取最小值时,策略为:
Δ
f
<
0
→
B
S
→
1
Δ
f
≥
0
→
T
S
→
p
(
−
Δ
f
)
\Delta f < 0 \rightarrow BS \rightarrow 1 \\ \Delta f \geq 0 \rightarrow TS \rightarrow p(-\Delta f)
Δf<0→BS→1Δf≥0→TS→p(−Δf)
求取最大值时最大值更改正反符号即可。
C++代码
利用模拟退火解旅行商问题
#include <iostream>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <stdio.h>
#include <time.h>
#include <math.h>
#define N 30 //城市数量
#define T 3000 //初始温度
#define EPS 1e-8 //终止温度
#define DELTA 0.98 //温度衰减率
#define LIMIT 1000 //概率选择上限
#define OLOOP 20 //外循环次数
#define ILOOP 100 //内循环次数
using namespace std;
//定义路线结构体
struct Path
{
int citys[N];
double len;
};
//定义城市点坐标
struct Point
{
double x, y;
};
Path bestPath; //记录最优路径
Point p[N]; //每个城市的坐标
double w[N][N]; //两两城市之间路径长度
int nCase; //测试次数
double dist(Point A, Point B)
{
return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}
void GetDist(Point p[], int n)
{
for(int i = 0; i < n; i++)
for(int j = i + 1; j < n; j++)
w[i][j] = w[j][i] = dist(p[i], p[j]);
}
void Input(Point p[], int &n)
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
scanf("%lf %lf", &p[i].x, &p[i].y);
}
void Init(int n)
{
nCase = 0;
bestPath.len = 0;
for(int i = 0; i < n; i++)
{
bestPath.citys[i] = i;
if(i != n - 1)
{
printf("%d--->", i);
bestPath.len += w[i][i + 1];
}
else
printf("%d\n", i);
}
printf("\nInit path length is : %.3lf\n", bestPath.len);
printf("-----------------------------------\n\n");
}
void Print(Path t, int n)
{
printf("Path is : ");
for(int i = 0; i < n; i++)
{
if(i != n - 1)
printf("%d-->", t.citys[i]);
else
printf("%d\n", t.citys[i]);
}
printf("\nThe path length is : %.3lf\n", t.len);
printf("-----------------------------------\n\n");
}
Path GetNext(Path p, int n)
{
Path ans = p;
int x = (int)(n * (rand() / (RAND_MAX + 1.0)));
int y = (int)(n * (rand() / (RAND_MAX + 1.0)));
while(x == y)
{
x = (int)(n * (rand() / (RAND_MAX + 1.0)));
y = (int)(n * (rand() / (RAND_MAX + 1.0)));
}
swap(ans.citys[x], ans.citys[y]);
ans.len = 0;
for(int i = 0; i < n - 1; i++)
ans.len += w[ans.citys[i]][ans.citys[i + 1]];
cout << "nCase = " << nCase << endl;
Print(ans, n);
nCase++;
return ans;
}
void SA(int n)
{
double t = T;
srand((unsigned)(time(NULL)));
Path curPath = bestPath;
Path newPath = bestPath;
int P_L = 0;
int P_F = 0;
while(1) //外循环,主要更新参数t,模拟退火过程
{
for(int i = 0; i < ILOOP; i++) //内循环,寻找在一定温度下的最优值
{
newPath = GetNext(curPath, n);
double dE = newPath.len - curPath.len;
if(dE < 0) //如果找到更优值,直接更新
{
curPath = newPath;
P_L = 0;
P_F = 0;
}
else
{
double rd = rand() / (RAND_MAX + 1.0);
//如果找到比当前更差的解,以一定概率接受该解,并且这个概率会越来越小
if(exp(dE / t) > rd && exp(dE / t) < 1)
curPath = newPath;
P_L++;
}
if(P_L > LIMIT)
{
P_F++;
break;
}
}
if(curPath.len < bestPath.len)
bestPath = curPath;
if(P_F > OLOOP || t < EPS)
break;
t *= DELTA;
}
}
int main(int argc, const char * argv[]) {
freopen("TSP.data", "r", stdin);
int n;
Input(p, n);
GetDist(p, n);
Init(n);
SA(n);
Print(bestPath, n);
printf("Total test times is : %d\n", nCase);
return 0;
}
该代码的关键在于GetNext(Path p, int n),SA(int n)函数,GetNext(Path p, int n)用于随机选取解,SA(int n)用于选择是否接纳点,如何接纳点,与迭代次数和终止条件。
Python代码
import numpy as np
import matplotlib.pyplot as plt
import random
class SA(object):
def __init__(self, interval, tab='min', T_max=10000, T_min=1, iterMax=1000, rate=0.95):
self.interval = interval # 给定状态空间 - 即待求解空间
self.T_max = T_max # 初始退火温度 - 温度上限
self.T_min = T_min # 截止退火温度 - 温度下限
self.iterMax = iterMax # 定温内部迭代次数
self.rate = rate # 退火降温速度
#############################################################
self.x_seed = random.uniform(interval[0], interval[1]) # 解空间内的种子
self.tab = tab.strip() # 求解最大值还是最小值的标签: 'min' - 最小值;'max' - 最大值
#############################################################
self.solve() # 完成主体的求解过程
self.display() # 数据可视化展示
def solve(self):
temp = 'deal_' + self.tab # 采用反射方法提取对应的函数
if hasattr(self, temp):
deal = getattr(self, temp)
else:
exit('>>>tab标签传参有误:"min"|"max"<<<')
x1 = self.x_seed
T = self.T_max
while T >= self.T_min:
for i in range(self.iterMax):
f1 = self.func(x1)
delta_x = random.random() * 2 - 1
if x1 + delta_x >= self.interval[0] and x1 + delta_x <= self.interval[1]: # 将随机解束缚在给定状态空间内
x2 = x1 + delta_x
else:
x2 = x1 - delta_x
f2 = self.func(x2)
delta_f = f2 - f1
x1 = deal(x1, x2, delta_f, T)
T *= self.rate
self.x_solu = x1 # 提取最终退火解
def func(self, x): # 状态产生函数 - 即待求解函数
value = np.sin(x**2) * (x**2 - 5*x)
return value
def p_min(self, delta, T): # 计算最小值时,容忍解的状态迁移概率
probability = np.exp(-delta/T)
return probability
def p_max(self, delta, T):
probability = np.exp(delta/T) # 计算最大值时,容忍解的状态迁移概率
return probability
def deal_min(self, x1, x2, delta, T):
if delta < 0: # 更优解
return x2
else: # 容忍解
P = self.p_min(delta, T)
if P > random.random(): return x2
else: return x1
def deal_max(self, x1, x2, delta, T):
if delta > 0: # 更优解
return x2
else: # 容忍解
P = self.p_max(delta, T)
if P > random.random(): return x2
else: return x1
def display(self):
print('seed: {}\nsolution: {}'.format(self.x_seed, self.x_solu))
plt.figure(figsize=(6, 4))
x = np.linspace(self.interval[0], self.interval[1], 300)
y = self.func(x)
plt.plot(x, y, 'g-', label='function')
plt.plot(self.x_seed, self.func(self.x_seed), 'bo', label='seed')
plt.plot(self.x_solu, self.func(self.x_solu), 'r*', label='solution')
plt.title('solution = {}'.format(self.x_solu))
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.savefig('SA.png', dpi=500)
plt.show()
plt.close()
if __name__ == '__main__':
SA([-5, 5], 'max')
代码摘自
https://www.cnblogs.com/xxhbdk/p/9192750.html
https://www.cnblogs.com/ranjiewen/p/6084052.html

1万+

被折叠的 条评论
为什么被折叠?



