在机器学习和深度学习中经常需要求解一个最优值,但是很多函数得到的仅仅是局部最优解,在求解全局最优解时,则需要用到一些算法,这里我用模拟退火算法来进行最优解的求解。这个原理网上教程应该很多,我就不赘述了,具体影响到算法解的结果有很多因素,比如初始温度值的设定,退火的速率以及随机数的随机性,都会影响到解的结果,这里我用C++实现了一遍,希望对大家有所帮助:
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<vector>
#include<ctime>
#define T_MAX 1e+6
#define T_MIN 50
#define ITER_TIMES 50
#define ALPHA 0.98
using namespace std;
vector<int>Value_Input;
vector<int>Value_Output;
void Input_Data()
{
int x[29] = { 11501760,6301660,402090,7501100,7502030,10302070,1650650,
14901630,7902260,7101310,8400550,11702300,9701340,5100700,
7500900,12801200,2300590,4600860,10400950,5901390,8301770,4900500,
18401240,12601500,12800790,4902130,14601420,12601910,3601980}; //对坐标进行编码,利于计算。
for (unsigned int i = 0; i < 29; i++)
{
Value_Input.push_back(x[i]);
}
}
static float Distance()
{
int x, y; //解码后的x,y
vector<int>x_state; //将解码后的x储存起来
vector<int>y_state; //将解码后的y储存起来
int Distance_Of_All = 0; //总距离
int square_x;
int square_y;
float Distance_Each;
/*将输入解码后得到输出x,y*/
for (size_t i = 0; i < Value_Input.size(); i++)
{
x = Value_Input[i] / 10000;
y = Value_Input[i] - 10000 * x;
x_state.push_back(x);
y_state.push_back(y);
}
/*分别获取x,y方向的总距离*/
for (size_t i = 0; i < x_state.size() - 1; i++)
{
square_x = pow((x_state[i] - x_state[i + 1]), 2);
square_y = pow((x_state[i] - x_state[i + 1]), 2);
Distance_Each = powf((square_x + square_y), 0.5);
Distance_Of_All += Distance_Each;
}
/*计算终点与起点的距离*/
square_x += pow((x_state[0] - x_state[x_state.size() - 1]), 2);
square_y += pow((y_state[0] - y_state[y_state.size() - 1]), 2);
Distance_Each = powf((square_x + square_y), 0.5);
Distance_Of_All += Distance_Each;
return Distance_Of_All;
}
void Simulated_Annealing()
{
int a, b; //生成两个随机数
float p; //随机概率(与Metropolis准则得到的概率比较)
float Metropolis;
float temputure = T_MAX; //当前温度
float E_FIRST, E_SECOND; //E_FIRST为原来的总能量,E_SECOND为变化后的能量
srand(time(NULL));
while (temputure >= T_MIN)
{
for (int i = 0; i < ITER_TIMES; i++)
{
a = rand() % 29; //生成第一个随机数
b = rand() % 29; //生成第二个随机数
while (a!=b)
{
E_FIRST = Distance(); //求解初始距离
swap(Value_Input[a], Value_Input[b]); //交换两个数
E_SECOND = Distance(); //求解第二个距离
/*如果得到更优的解则替换,不是的话进行下一步*/
if (E_FIRST >= E_SECOND)
{
break;
}
/*生成随机概率p与Metropolis值进行比较*/
p = rand() % 2;
Metropolis = exp2f((E_FIRST - E_SECOND) / temputure);
if (p >= Metropolis)
{
Value_Output.push_back(E_SECOND);
break;
}
/*如果不同意的话取消交换*/
swap(Value_Input[a], Value_Input[b]);
}
}
temputure *= ALPHA; //温度迭代(退火)
}
}
void outputVector()
{
int x, y;
float distance;
for (size_t i = 0; i < Value_Input.size(); i++)
{
x = Value_Input[i] / 10000; //解码得到x
y = Value_Input[i] - 10000 * x; //解码得到y
cout << "x = " << x << "y =" << y << endl;
}
distance = Distance(); //输出距离
cout << "距离为" << distance << endl;
}
int main()
{
Input_Data(); //这里可以自己输入TSP坐标
Simulated_Annealing(); //模拟退火算法
outputVector(); //输出最终结果
return 0;
}
结果输出为:
x = 1150,y =1760
x = 360,y =1980
x = 490,y =500
x = 750,y =2030
x = 1280,y =790
x = 1260,y =1500
x = 750,y =1100
x = 630,y =1660
x = 710,y =1310
x = 1040,y =950
x = 970,y =1340
x = 40,y =2090
x = 510,y =700
x = 830,y =1770
x = 840,y =550
x = 1490,y =1630
x = 165,y =650
x = 750,y =900
x = 1030,y =2070
x = 1460,y =1420
x = 790,y =2260
x = 460,y =860
x = 1260,y =1910
x = 590,y =1390
x = 1840,y =1240
x = 1280,y =1200
x = 490,y =2130
x = 230,y =590
x = 1170,y =2300
距离为21374
但是最终输出并不稳定,需要与迭代过程中的值进行比较,得到最终的最优规划。因此加入修正项后的值已经接近最优。最终的值为13212.
sort(Value_Output.begin(), Value_Output.end());
distance = Distance(); //迭代完成后的距离
if (Value_Output[0]<=distance)
{
cout << "距离为" << Value_Output[0] << endl;
}
else
{
cout << "距离为" << distance << endl;
}
这里是对结果的修正。
接下来是输出:
x = 510,y =700
x = 1260,y =1500
x = 1490,y =1630
x = 1280,y =1200
x = 750,y =1100
x = 710,y =1310
x = 1460,y =1420
x = 830,y =1770
x = 230,y =590
x = 1030,y =2070
x = 40,y =2090
x = 750,y =900
x = 460,y =860
x = 1150,y =1760
x = 630,y =1660
x = 1280,y =790
x = 750,y =2030
x = 1040,y =950
x = 840,y =550
x = 790,y =2260
x = 490,y =2130
x = 590,y =1390
x = 1170,y =2300
x = 490,y =500
x = 970,y =1340
x = 165,y =650
x = 1260,y =1910
x = 1840,y =1240
x = 360,y =1980
距离为13212
这就是模拟退火算法的大概流程了,想继续研究精度的可以继续研究。如果有错的地方也请大家批评指正