AI中的幾種搜索算法---Tabu搜索算法
引言
Tabu相對於啓發式算法家族中其他成員,要簡單易懂的多。關於啓發式算法的基本概念可以參見筆者的《AI中的幾種搜索算法---A*搜索算法》。這裏就不多介紹了。
一、Tabu搜索算法的基本介紹
1.局部最優和全局最優
局部最優(Local Optimums)和全局最優(Global Optimums)這對概念在搜索中,經常被提到。所以在這裏,先對其做一個簡單介紹。
我們假設一個搜索算法,將訪問的結點(cur_node)與當前最優結點(best_node)進行比較,如果cur_node優於best_node,則進行替換;否則繼續訪問下一個結點。
如果用上述的算法進行搜索,我們得到的結果便是局部最優。
在一個搜索領域中,有一個結點A,是這個領域中的最優結點,我們訪問過A之後,因爲在A的鄰接結點中找不到更好的結點,所以我們的搜索會在A的周圍不停地“徘徊”,無法去探索新的搜索領域。這樣我們就陷入了局部最優的泥沼。
這裏Tabu搜索算法,降低對鄰接結點的要求,使其逃離了局部最優。
2.Tabu List
Tabu List是一個“先進先出”(First In ,First Out)的隊列,保存了已經被“估值”的結點,這樣就會避免重複估值,並且防止了因爲挑選了同一個最優鄰接結點而使算法陷入局部的死循環。
我們可以爲Tabu List設定一個容量限制,如果當Tabu List滿了之後,將會把最舊的結點給移除。
3.Tabu算法的流程
大致描述一下這個流程:
1. 首先便是初始化工作,創建tabu list,然後獲得初始結點(隨機獲得)。
2. 對初始結點進行估值,得到它的“消耗”,設置爲當前最小消耗best_cost,並將初始結點設置爲當前最優結點best_node,然後將初始結點放入tabu list中。
3. 遍歷當前最優結點的鄰接結點,並挑選不在tabu list中的最優鄰接結點(best_neighbor)。
4. 如果tabu list容量已滿,則移除其中最舊的結點。
5. 將best_neighbor放入tabu list中。
6. 將best_neighbor與best_node進行比較。如果best_neighbor優於best_node,則進行替換。
7. 判斷best_node是否是目標結點,如果是,結束算法;否則繼續步驟3。
下面便是一個僞代碼的流程:
Tabu_list = CreateTabuList();
CurNode = RandNde();
Cacaulate(CurNode);
BestNode = CurNode;
Tabu_List.push(Best_Node);
While(true)
{
CurNode= best_neighbor_not_ontabulist(CurNode);
If(Tabu_List.IsFull())Tabu_List.deQueue();
Cacaulate(CurNode);
If(CurNode.cost< BestNode.cost)
BestNode = CurNode;
If(BestNode.cost== 0)
Break;
}
二、N皇后問題
NQueen問題一直是算法界的一個經典案例。
1.N皇后問題簡介
N皇后問題,是在一個N*N的方格正方形中,要放入N個棋子,並且保證每一個棋子的水平方向、豎直方向和斜方向上都沒有第二個棋子。
解決N皇后問題的算法有很多,這裏我們就用剛介紹的tabu算法進行解決。之後我會專門寫一篇N皇后問題,用不同算法進行求解,並比較不同算法在這個問題上的性能。
2.代碼
這裏附上Tabu解決NQueen問題的核心算法代碼。
其中NQBoard是一個自定義的結構體,其中包含一個數組,表示棋盤。
randBoard這個函數可以隨機獲得一個已經放入了N個皇后的初始棋。calucateConflict這個函數可以計算棋盤中皇后的衝突量,就是算法中的cost。getBestNeighbot能獲得當前棋盤不在tabulist中的最後鄰接結點。
有興趣的讀者可以去Tabu搜索算法解決N皇后問題
int tabu_nqueens(NQBoard & solution,int nQueens)
{
NQBoard neighbor;
neighbor.board = newint[nQueens];
memset(neighbor.board,0,sizeof(int)*nQueens);
randBoard(solution,nQueens);
memcpy(neighbor.board,solution.board,sizeof(int)*nQueens);
int bestCost= calucateConflict(solution,nQueens);
int depth =0;
tabulist.enQueue(solution);
//
while(true)
{
intcurCost = getBestNeighbor(neighbor,nQueens);
if(!tabulist.isOnList(neighbor))
{
//////////////////////////////////////////////////////////////////////////
//在nQueens超過十的時候,可以考慮去掉以下代碼
/*if (tabulist.isFull())
{
tabulist.deQueue();
}*/
//////////////////////////////////////////////////////////////////////////
tabulist.enQueue(neighbor);
tabulist.enQueue(neighbor);
}
if(curCost<= bestCost)
{
bestCost = curCost;
memcpy(solution.board,neighbor.board,sizeof(int)*nQueens);
}
if(bestCost== 0)
break;
++depth;
}
delete[]neighbor.board;
return depth;
}
3.程序運行截圖
三、總結
當nQueen變得超過14的時候,程序找出結果變很慢,隨着nQueen增長,速度越發慢。
這個算法的程序我寫的還不夠完善,如果好心的讀者找到程序中不合理的地方,可以來這裏留言,隨時歡迎。
如果有興趣的可以留言,一起交流一下算法學習的心得。
接下來我會發表介紹幾種搜索算法。
聲明:本文章是筆者整理資料所得原創文章,如轉載需註明出處,謝謝。