常用的啓發式算法
什麼是啓發式算法
啓發式算法一般用於解決NP-hard問題,其中NP是指非確定性多項式。
你可能不懂NP-HARD問題是什麼(關於NP-hard,NPC,NP,P問題,下期再說),簡單來說,NPH問題就是用電腦直接算不出來的問題,然後你要用啓發式算法,得到很可能正確的結果,但可以省下時間、空間,電腦就能跑起來了。
例如,著名的旅行商問題(Travel Saleman Problem or TSP):假設一個推銷員需要從南京出發,經過廣州,北京,上海,…,等 n 個城市, 最後返回香港。 任意兩個城市之間都有飛機直達,但票價不等。假設公司只給報銷 C 元錢,問是否存在一個行程安排,使得他能遍歷所有城市,而且總的路費小於 C?
旅行商問題可以直接看做求最短路徑問題,當規模小的時候你可以直接通過Dijkstra或Floyd算法求,但是當規模大的時候,這些算法就解決不聊了,所以我們要尋找其他算法。
推銷員旅行問題顯然是 NP 的。因爲如果你任意給出一個行程安排,可以很容易算出旅行總開銷。但是,要想知道一條總路費小於 C 的行程是否存在,在最壞情況下,必須檢查所有可能的旅行安排。
啓發式算法是相對於最優化算法提出的,是基於直觀或者經驗構造的算法,在可接受的開銷(時間和空間)內給出待解決組合優化問題的一個可行解。
目前通用的啓發式算法
目前比較通用的啓發式算法一般有模擬退火算法(SA)、遺傳算法(GA)、蟻羣算法(ACO)、人工神經網絡(ANN)、螢火蟲算法(FA)等。
模擬退火算法(SA)
簡介
模擬退火算法(Simulated Annealing, SA)的思想借鑑於固體的退火原理,當固體的溫度很高的時候,內能比較大,固體的內部粒子處於快速無序運動,當溫度慢慢降低的過程中,固體的內能減小,粒子的慢慢趨於有序,最終,當固體處於常溫時,內能達到最小,此時,粒子最爲穩定。模擬退火算法便是基於這樣的原理設計而成。
模擬退火算法步驟
-
初始化溫度T(充分大),溫度下限Tmin(充分小),初始解X,每個T值迭代次數L
-
隨機生成臨域解x_new;
-
設f(x)函數來計算用來計算解得好壞,計算出f(x_new)-f(x);
-
如果f(x_new)-f(x)>0,說明新解比原來的解好,則無條件接受,如果f(x_new)-f(x)<0,則說明舊解比新解好,則以概率
exp((f(xnew)-f(x))/k*T)
接受x_new作爲解。 -
如果當前溫度<Tmin時,則退出循環,輸出當前結果,否則減少當前溫度,回到第2步繼續循環,常用的降溫方法爲T= a*T (0<a<1),一般a取接近1的值
代碼示例
求解函數最小值問題: 其中,0<=x<=100,給定任意y值,求x爲多少時,F(x)最小。
public class SATest {
public static final int T = 1000;// 初始化溫度
public static final double Tmin = 1;// 溫度的下界
public static final int k = 100;// 迭代的次數
public static final double delta = 0.98;// 溫度的下降率
public static double getX() {
return Math.random() * 100;
}
/**
* 評價函數的值,即對應上文中的f(x)
*
* @param x目標函數中的一個參數
* @param y目標函數中的另一個參數
* @return函數值
*/
public static double getFuncResult(double x, double y) {
double result = 6 * Math.pow(x, 7) + 8 * Math.pow(x, 6) + 7
* Math.pow(x, 3) + 5 * Math.pow(x, 2) - x * y;
return result;
}
/**
* 模擬退火算法的過程
* @param y目標函數中的指定的參數
* @return最優解
*/
public static double getSA(double y) {
double result = Double.MAX_VALUE;// 初始化最終的結果
double x[] = new double[k];
// 初始化初始解
for (int i = 0; i < k; i++) {
x[i] = getX();
}
// 迭代的過程
while (t > Tmin) {
for (int i = 0; i < k; i++) {
// 計算此時的函數結果
double funTmp = getFuncResult(x[i], y);
// 在鄰域內產生新的解
double x_new = x[i] + (Math.random() * 2 - 1);
// 判斷新的x不能超出界
if (x_new >= 0 && x_new <= 100) {
double funTmp_new = getFuncResult(x_new, y);
if (funTmp_new - funTmp < 0) {
// 替換
x[i] = x_new;
} else {
// 以概率替換
double p = 1 / (1 + Math
.exp(-(funTmp_new - funTmp) / T));
if (Math.random() < p) {
x[i] = x_new;
}
}
}
}
T = T * delta;
}
for (int i = 0; i < k; i++) {
result = Math.min(result, getFuncResult(x[i], y));
}
return result;
}
public static void main(String args[]) {
// 設置y的值
int y = 0;
System.out.println("最優解爲:" + getSA(y));
}
}
遺傳算法(GA)
簡介
遺傳算法(Genetic Algorithm, GA)起源於對生物系統所進行的計算機模擬研究。它是模仿自然界生物進化機制發展起來的隨機全局搜索和優化方法,借鑑了達爾文的進化論和孟德爾的遺傳學說。其本質是一種高效、並行、全局搜索的方法,能在搜索過程中自動獲取和積累有關搜索空間的知識,並自適應地控制搜索過程以求得最佳解。
相關術語
-
編碼(coding):將物體的表現型用編碼的方式轉爲程序可控的基因型。
-
解碼(decoding):基因型到表現型的映射。
-
基因型(genotype):參數的因子;
-
表現型(phenotype):根據不同因子最終展現的形態;
-
適應度(fitness):度量某個結果的好壞。
-
進化(evolution):不斷剔除差的結果,最終逐步留下好的結果。
-
選擇(selection):以一定的概率從種羣中選擇若干個個體留下,並繁殖。選擇過程是一種基於適應度的優勝劣汰的過程。
-
複製(reproduction):將父本、母本的基因複製,以便產生下一代。
-
交叉(crossover):兩個染色體的某一相同位置處DNA被切斷,前後兩串分別交叉組合形成兩個新的染色體。也稱基因重組或雜交;
-
變異(mutation):交叉後可能(很小的概率)對染色體進行更改,來防止算法過早收斂而陷入局部最優解中。
-
個體(individual):指染色體帶有特徵的實體;
-
種羣(population):個體的集合,該集合內個體數稱爲種羣的大小
在此,關於編碼方法,交叉方法,變異方法,選擇方法等都不做詳盡概述。
蟻羣算法(ACO)
簡介
我們嘗試復原一下螞蟻尋找食物的場景。想象有一隻螞蟻找到了食物,這時它需要將食物帶回蟻穴。對於這一隻螞蟻而言,它顯然並不知道應該怎麼走。那麼,這隻螞蟻有可能會隨機選擇一條路線。
這條路線很可能是一條遠路。但是,螞蟻一路上留下了記號,也就是信息素。如果這隻螞蟻繼續不停地搬運食物,或者有許多其他螞蟻一塊搬運的話。他們總會在運氣好的時候走到更快往返的路線上。螞蟻選擇的路越好,相同時間內往返的次數也就更多,也就在路上留下了更多的信息素。
於是,螞蟻們總會發現,有一些路徑的信息素更濃,這些路徑就是更好的路線。於是螞蟻也就更多地向信息素更濃的路徑上偏移。螞蟻們不停重複這個過程,最終總能找到一條確定的路線,而這條路線就是螞蟻們找到的最優路徑。
蟻羣算法步驟
-
初始化螞蟻數量、可行路段、每條路段距離、每條路段的初始信息素大小等信息
-
設定螞蟻的起點、終點。
-
螞蟻從起點出髮根據信息素濃度,有一定的概率性選擇路段,濃度越高,概率越大,逐步回到終點。
-
在螞蟻走過的路徑上,根據每條路段的長度按比例釋放信息素,短的路段釋放的信息素多,長的路段釋放的信息素少。
-
對所有路段的信息素進行揮發。
-
回到第二步進行循環,直到螞蟻數量迭代完。
螢火蟲算法(FA)
簡介
該算法的基本思想是利用在一定範圍內螢火蟲發光低的向發光高的螢火蟲移動,從而有效的實現尋找最優解。
由於螢火蟲算法原理相對簡單、且易於實現;而且還具有良好的全局尋優能力,能夠快速地收斂於最優解等特性,現在螢火蟲算法已經應用到工業優化、動態路徑規劃、圖像處理、經濟調度等領域。
算法流程
- 初始化問題,將個體轉化爲螢火蟲,設置亮度函數,並初始化所有參數β,γ,α
- 確定每個螢火蟲兩兩之間的距離
- 計算對周圍螢火蟲的吸引度
- 對於每個螢火蟲而言,找到吸引度最高的螢火蟲個體,並更新位置
- 使最亮的螢火蟲隨機移動位置
- 如果完成迭代或者達到精度則結束,否則轉第2步
全文比較簡潔,主要是一個簡單介紹,若要具體研究,建議朋友們查閱論文。