目錄
-
前沿
模擬退火法(simulated annealing algorithm,SAA)的基本思想源於物理中固體物質的退火過程與一般的組合優化問題之間相似,它把優化問題的可行解視爲材料的各種狀態,優化目標視爲材料的能量或者熵,在優化過程中不斷的“產生新解——判斷——接受或捨棄”進行迭代,最終實現問題的最優化。
組合優化問題 |
金屬物體 |
解 |
粒子狀態 |
最優解 |
能量最低的狀態 |
設計初溫 |
溶解過程 |
Metropolis抽樣過程 |
等溫過程 |
控制參數的下降 |
冷卻 |
目標函數 |
能量 |
-
算法目的
- 解決多項式複雜程度的非確定性(NP)複雜性問題;
- 克服優化過程陷入局部極小;
- 克服初值依賴性。
在對固體物質進行模擬退火處理時,通常先將它加溫熔化,使其中的粒子可自由運動,然後隨着溫度的逐漸下降,粒子也逐漸形成了低能態的晶格。若在凝結點附近的溫度下降速率足夠慢,則固體物質一定會形成最低能態的基態。
-
算法基礎理論
固體退火過程
退火是指將固體加熱到足夠高的溫度(熔化),使分子呈隨機排列狀態(熵增大、能量增大),然後逐步降溫使之冷卻(冷卻),最後分子以低能狀態排列(熵減小、能量減小),固體達到某種穩定狀態。
- 加溫過程——增強粒子的熱運動,消除系統原先可能存在的非均勻態;
- 等溫過程——對於與環境換熱而溫度不變的封閉系統,系統狀態的自發變化總是朝自由能減少的方向進行,當自由能達到最小時,系統達到平衡態;
- 冷卻過程——使粒子熱運動減弱並漸趨有序,系統能量逐漸下降,從而得到低能的晶體結構。
退火過程中的統計力學
此部分主要描述了退火過程中所有狀態在高溫下具有相同概率以及溫度降至很低時,材料傾向於進入具有最小能量的狀態。
Metropolis準則——以一定概率接受新狀態
(1)若在溫度T,當前狀態i → 新狀態j
(2)若 ,則接受 j 爲當前狀態;
(3)否則,若概率 p大於[0,1)區間的隨機數,則仍接受狀態 j 爲當前狀態;若不成立,則保留狀態 i 爲當前狀態。
在高溫下,可接受與當前狀態能量差較大的新狀態;在低溫下,只接受與當前狀態能量差較小的新狀態。
-
算法模型
模擬退火法流程圖
-
參數分析
初始溫度
初溫越大,獲得高質量解的機率越大,但花費較多的計算時間。
方法:
1)均勻抽樣一組狀態,以各狀態目標值的方差爲初溫;
2)隨機產生一組狀態,確定兩兩狀態間的最大目標值差,根據差值,利用一定的函數確定初溫,譬如 ,其中 爲初始接受概率;
溫度更新函數
溫度更新函數,即溫度的下降方式,用於在外循環中修改溫度值。
常用的算法溫度下降函數:
1) :α越接近1溫度下降越慢,且其大小可以不斷變化;
2):其中爲起始溫度,K爲算法溫度下降的總次數。
終止準則
外循環終止準則
- 設置終止溫度的閾值;
- 設置外循環迭代次數;
- 算法搜索到的最優值連續若干步保持不變;
- 概率分析方法。
-
C++程序測試Sphere函數
通過程序計算,用origin整理如下:
-
總結
SA算法的優點:
- 質量高;
- 初值魯棒性強;
- 簡單、通用、易實現。
SA算法的缺點:
由於要求較高的初始溫度、較慢的降溫速率、較低的終止溫度,以及各溫度下足夠多次的抽樣,因此優化過程較長。
-
visual studio2017c++源代碼
pch.h頭文件:
// Simulated annealing.cpp : 模擬退火程序編寫
//開發人員:陳帥 日期:2019.8.3-9.4 郵箱:[email protected]
#ifndef PCH_H
#define PCH_H
#include <iostream>
# include <fstream>
#include <iomanip>
#include <math.h>
#include <vector>
#include<random>
#include<ctime>
using namespace std;
//產生隨機小數或整數
class RandomNumber {
public:
RandomNumber() {
srand(time(0)); //析構函數,在對象創建時數據成員執行初始化操作
}
int integer(int begin, int end)
{
return rand() % (end - begin + 1) + begin;
}
double decimal(double a, double b)
{
return double(rand() % 10000) / 10000 * (b - a) + a;
}
};
//定義推過過程中材料的各種固有屬性
class sa
{
private:
//=====================模擬退火算法參數=====================
int T_0 ; //設置初始溫度 跟跳出目前優化狀態相關
int L_0 ; //設置循環次數
int T_s ; //停止準則溫度爲0
int K ; //停止準則2
int n_variable ; //變量數量
double alpha ; //降溫係數
public:
vector<double>x_low = { -100 }; //儲存變量上限值
vector<double>x_high = { 100}; //儲存變量下限值
vector<double>x_i; //初始優化變量賦值
vector<double>x_new; //新狀態優化變量賦值
vector<double>x_best; //最優變量
double Ene_value; //能量值
double Ene_value_new; //新狀態能量值
double Ene_delta; //能量差值
double Ene_best; //最優能量值
double P_T; //P_T以一定的概率接受不好的狀態
double T_k; //更新溫度
void SetParameters(); //設置算法參數
void intialize(); //初始化
void metropolis(); //metropolis準則
void update_var_Ene(); //能量更新
void Optimization_iteration(); //尋優迭代
};
double function(vector<double> x); //目標函數
#endif //PCH_H
Simulated annealing.cpp主函數文件:
#include "pch.h"
#include <iostream>
RandomNumber r; //隨機數
int main()
{
sa SA; //定義物體,包括材料的各種固有屬性
SA.SetParameters();
SA.intialize(); //初始化
SA.Ene_value = function(SA.x_i); //計算目標測試函數
SA.Ene_best = SA.Ene_value;
//進入主要循環,按照公式依次迭代,直到滿足精度要求
SA.Optimization_iteration();
}
sa_function.cpp函數文件:
#include "pch.h"
double function(vector<double> x)
{
double fx = 0;
int n = size(x);
//============================測試函數=============================
//1.Sphere函數 變量[-100,100]
for (int i = 0; i < n; i++)
{
fx = fx + pow(x[i], 2);
}
return fx;
}
void sa::SetParameters()
{
T_0 = 1500; //設置初始溫度 跟跳出目前優化狀態相關
L_0 = 200; //設置循環次數
T_s = 1; //停止準則溫度爲0
K = 400; //停止準則2
n_variable = 2; //變量個數
alpha = 0.99; //降溫係數
}
//*********************************
//初始化
//*******************************
void sa::intialize()
{
extern RandomNumber r; //隨機數
x_i.resize(n_variable); //定義初始優化變量的值
x_new.resize(n_variable); //定義優化變量更新值
x_best.resize(n_variable); //最優變量初始化
T_k = T_0;
for (int i = 0; i < n_variable; i++)
{
x_i[i] = r.decimal(x_low[0], x_high[0]); //給變量賦值新狀態/新的可行解
}
}
//***************************
//更新新狀態下的變量和最優能量值
//******************************
void sa::update_var_Ene()
{
extern RandomNumber r; //隨機數
//舊狀態下的函數值
for (int i = 0; i < n_variable; i++)
{
x_new[i] = r.decimal(x_low[0], x_high[0]); //給變量賦值新狀態/新的可行解
}
Ene_value_new = function(x_new); //新狀態下的函數值
//選優
if (Ene_value_new < Ene_best)
{
Ene_best = Ene_value_new;
x_best = x_new; //更新最優變量
x_i = x_new; //更新變量
}
}
//***************************
//metropolis準則
//******************************
void sa::metropolis( )
{
extern RandomNumber r; //隨機數
Ene_delta = Ene_value_new - Ene_value; //能量(函數)差值
if (Ene_delta > 0)
{
P_T = exp(-Ene_delta / (T_0));
if (P_T > r.decimal(0, 1))
{
x_i = x_new; //更新變量
// << exp(-Ene_delta / (T_0)) << endl;
}
}
Ene_value = function(x_i);
}
void sa::Optimization_iteration()
{
clock_t startTime, endTime; //定義運行時間和結束時間
startTime = clock(); //計時開始
ofstream out("模擬退火優化結果.dat");
for (int k = 0; T_k > T_s; k++) //停止準則
{
for (int l =0; l < L_0; l++) //迭代條件
{
update_var_Ene(); //更新新狀態下的變量和最優能量值
metropolis(); //metropolis準則
}//某一溫度下的計算結束
T_k= pow(alpha,k) * T_0; //更新溫度
//T_k = double( T_0)/k;
//T_k = double( T_0)/log(k+2);
out << fixed << setw(3) << setprecision(5) << k << fixed << setw(12) << setprecision(5) << Ene_best << endl;
}
out << "最優變量:" << endl;
for (int i = 0; i < n_variable; i++)
{
out << "x" << i << "=" << fixed << setw(12) << setprecision(5) << x_best[i] << endl;//輸出最優變量
}
out << "最優值=" << fixed << setw(12) << setprecision(5) << Ene_best << endl;
endTime = clock();//計時結束
out << "run time:" << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
out.close();
}
-
源文件下載地址
各位閱讀文章的朋友覺得文章可以給個好評或者點贊,大家覺得有問題可以在評論區指出來或者發郵箱[email protected]聯繫我!如需要轉載請附上鍊接,謝謝各位朋友!
(更新於2020.05.03)