N-puzzle-Problem
?
N-Puzzle Problem
文章目錄
- N-Puzzle Problem
- Preview
- N-Puzzle Problem:
- N-Puzzle Problem 的可解性判斷
- Algorithms
- Three Stages and Related Algorithms
- First phase:
- 所需解決樣例以及最多時(shí)間:
- Algorithm:A*
- Code:
- Result:
- Second phase:
- 所需解決樣例以及最多時(shí)間:
- Algorithm:IDA*
- Code:
- Result:
- Third phase:
- 所需解決樣例以及最多時(shí)間:
- Algorithm:IDA*+Disjoint Pattern Database Heuristics
- 不斷優(yōu)化的啟發(fā)式
- 1、曼哈頓距離
- 2、Manhattan Distance+Linear Conflict
- 3、非可加性的模式數(shù)據(jù)庫(kù)
- 4、不相交的模式數(shù)據(jù)庫(kù)(可加性的)
- 5、裁剪
- 6、動(dòng)態(tài)分區(qū)模式數(shù)據(jù)庫(kù)啟發(fā)式 Dynamically-Partitioned Database Heuristics
- Code:不相交模式數(shù)據(jù)庫(kù)靜態(tài)6-6-3分區(qū)
- Result:
- Reference resources
Preview
N-Puzzle Problem:
在人工智能中,常常以N-Puzzle 問題的求解為例子來說明各種搜索策略。
N=K*K-1;
當(dāng)N=8時(shí),即為重排九宮格問題;當(dāng)N=15時(shí),即為十五迷問題;
一個(gè)狀態(tài):N個(gè)數(shù)碼及一個(gè)空格的每一組置放形式,就形成了該問題的一個(gè)狀態(tài)。
N-Puzzle問題空間:所有(N+1)!個(gè)互不相同的狀態(tài)構(gòu)成了一個(gè)N-Puzzle問題空間。
128M 1e7
操作:平移操作——把與空格相鄰 的某一數(shù)碼移入空格,稱這種操作為平移操作。
Problem:對(duì)于給定的任一初始狀態(tài),使用平移操作,求解到目標(biāo)狀態(tài)的最優(yōu)步數(shù) (最少步驟的移法) 以及移動(dòng)的每一步狀態(tài)。
Goal position:
N-Puzzle Problem 的可解性判斷
分奇偶性然后根據(jù)逆序數(shù)判斷。
https://blog.csdn.net/Wood_Du/article/details/88730885
Algorithms
? N-Puzzle問題之所以能用來為例說明各種搜索策略,在于對(duì)于N-Puzzle問題,根據(jù)所需求解的初始狀態(tài)的復(fù)雜性,我們可以從爆搜開始,一步一步優(yōu)化,當(dāng)某種算法不足于求解時(shí),可從優(yōu)化數(shù)據(jù)結(jié)構(gòu),更換搜索算法上升一個(gè)境界,求得更加復(fù)雜的初始狀態(tài)的解。
:你會(huì)搜索嗎?
:我會(huì)爆搜 dfs+STL庫(kù),但是DFS不能找到最優(yōu)解;找最優(yōu)解我會(huì)bfs
:那么存狀態(tài)呢?
:STL庫(kù)會(huì)很廢效率,我可以用hash判重;正好學(xué)了Zobrist Hash
:你還有什么可以進(jìn)一步優(yōu)化嗎?
:為了減少狀態(tài)的膨脹,我可以使用雙向廣搜,從初始狀態(tài)和目標(biāo)狀態(tài)同時(shí)開始搜索,當(dāng)某方向遇到另一個(gè)方向搜索過的狀態(tài)的時(shí)候,則搜索成功。兩個(gè)方向?qū)拥玫阶詈蠼Y(jié)果。
:這些都是無信息搜索算法,你可以使用有信息搜索嗎?
:啟發(fā)式搜索,那就想辦法減少搜索范圍,降低問題復(fù)雜度。這個(gè)時(shí)候我們就可以上A*+Hash了。啟發(fā)函數(shù)可以用錯(cuò)位數(shù)或者曼哈頓距離。
:對(duì)于A*,需要每次找到Open表中f(f(n) = g(n)+h(n)) 最小的的元素。如果排序那么工作量會(huì)很大應(yīng)該怎么辦呢?
:上優(yōu)先隊(duì)列
:但是對(duì)于15碼問題,內(nèi)存不能存下狀態(tài)了
:IDA*,解決內(nèi)存問題。
是基于迭代加深的A*算法。并且不需要判重,不需要估價(jià)排序,用不到哈希表,空間需求量變得超級(jí)少。啟發(fā)函數(shù)用曼哈頓距離。在找最優(yōu)解的時(shí)候,對(duì)超過目前最優(yōu)解的地方進(jìn)行剪枝,可導(dǎo)致搜索深度急劇減少。再check一下不要回到上個(gè)狀態(tài)。
:一般的15碼樣例都能解決,但是對(duì)于15碼最復(fù)雜的一些樣例,仍然需要三四十分鐘才能解出答案。還有什么可以優(yōu)化嗎?
:那就只能去啃英文論文了。A.Felner,R.Korf的Disjoint Pattern Database Heuristics, 不相交模式數(shù)據(jù)庫(kù)啟發(fā)式算法。Additive Pattern Database Heuristics ——E.Korf,A.Felner,S.Hanan。這是一中新的設(shè)計(jì)更精確的可容許啟發(fā)式評(píng)價(jià)函數(shù)的方法,該方法基于模式數(shù)據(jù)庫(kù)。
本文我們將略去無信息搜索算法,求解最小步數(shù),詳解算法為:
從A* 開始,優(yōu)化數(shù)據(jù)結(jié)構(gòu);
再到IDA*;
最后用Pattern Database Heuristics解決15碼最復(fù)雜的樣例。
針對(duì)不同復(fù)雜度的樣例,本文后面將分為三個(gè)階段。
每個(gè)階段將說明解決的樣例,求解的要求時(shí)間,算法以及啟發(fā)函數(shù),代碼實(shí)現(xiàn)以及每個(gè)樣例的解決時(shí)間。
對(duì)于展示代碼因?yàn)榇a量很大,并且是一個(gè)框架,所以只能展示核心代碼,若需要查看整個(gè)項(xiàng)目代碼,我會(huì)push到github上。
Three Stages and Related Algorithms
First phase:
所需解決樣例以及最多時(shí)間:
? 對(duì)于以下兩個(gè)實(shí)例,均能夠在1秒之類解出
Algorithm:A*
? A*算法是一種靜態(tài)網(wǎng)中求解最短路徑最有效的直接搜索方法也是也是一種啟發(fā)式算法。在搜索過程中使用啟發(fā)函數(shù)。估價(jià)函數(shù)與實(shí)際值越接近,最終搜索到目標(biāo)格局的時(shí)間越快。
估價(jià)函數(shù)表示:
f(n) = g(n) + h(n) //f(n) 表示從初始狀態(tài)到目標(biāo)狀態(tài)的估測(cè)代價(jià) //g(n) 表示從初始狀態(tài)到當(dāng)前狀態(tài)(節(jié)點(diǎn)n)的代價(jià)(已經(jīng)確定) //h(n) 表示從當(dāng)前狀態(tài)(節(jié)點(diǎn)n)到目標(biāo)狀態(tài)的估測(cè)代價(jià)(預(yù)測(cè))h(n) < h'(n) //h'(n)為當(dāng)前狀態(tài)到目標(biāo)狀態(tài)的實(shí)際步數(shù)h(n) 的好壞直接影響評(píng)估函數(shù)的好壞。
流程圖:
? 從初始狀態(tài)出發(fā) =>經(jīng)過一系列中間狀態(tài) =>最終到達(dá)目標(biāo)狀態(tài)(或者無法到達(dá))。
? 該算法用于經(jīng)過中間狀態(tài)時(shí)候的行進(jìn)策略(其中的中間狀態(tài)或者由題目給出,或者在前邊已經(jīng)推導(dǎo)得出)。
優(yōu)點(diǎn):與廣度優(yōu)先搜索策略和深度優(yōu)先搜索策略相比,A*算法不是盲目搜索,而是有提示的搜索
缺點(diǎn):該算法一般要使用大量的空間用于存儲(chǔ)已搜索過的中間狀態(tài),防止重復(fù)搜索。隨著樣例變復(fù)雜將會(huì)爆內(nèi)存。
Code:
完整代碼傳送門:???
Search為A*算法,此代碼由于調(diào)用了框架中其他類的代碼所以比較抽象,我是邊百度java邊寫這個(gè)項(xiàng)目,沒學(xué)過java的娃娃面向百度編程。所以如果你會(huì)java,相信你噸噸噸就看明白我代碼了。
package core.astar;import core.problem.Action; import core.problem.Problem; //操作 import core.problem.State;import java.util.ArrayList;public class AStar {public AStar(Problem problem) {super();this.problem = problem;}public Node childNode(Node parent, Action action) {State state = problem.result(parent.getState(), action);//getPathCost 初始到當(dāng)前的實(shí)際代價(jià) stepCost 從父級(jí)到后續(xù)任務(wù)的路徑成本int pathCost = parent.getPathCost() + problem.stepCost(parent.getState(), action);int heuristic = problem.heuristic(state); //估計(jì)從狀態(tài)到目標(biāo)狀態(tài)最便宜路徑的成本return new Node(state, parent, action, pathCost, heuristic);}public Problem getProblem() {return problem;}public void setProblem(Problem problem) {this.problem = problem;}public Node Search(){//起始節(jié)點(diǎn)State initState = problem.getInitialState();Node node = new Node(initState, null, null, 0, problem.heuristic(initState));Fringe fringe = new Fringe();fringe.insert(node);Explored explored = new Explored();while (true) {if (fringe.isEmpty()) return null; //失敗node = fringe.pop(); //choose the lowest-cost node in frontierif (problem.goalTest(node.getState())) return node;explored.insert(node.getState());for (Action action : problem.Actions(node.getState())) {Node child = childNode(node, action);//child.getState()不再在擴(kuò)展節(jié)點(diǎn)的集合(Close表中)且fringe(Open表)中不存在狀態(tài)為state的節(jié)點(diǎn) 則將節(jié)點(diǎn)插入fringe中if (!explored.contains(child.getState()) && !fringe.contains(child.getState())) {fringe.insert(child);}else {/**如果鄰接結(jié)點(diǎn)N’在CLOSED表中,比較CLOSED表中的g(N')值和當(dāng)前的g(N')值,如果當(dāng)前的g(N')更小,那么刪除CLOSED表中的N',把N設(shè)成N'的父節(jié)點(diǎn),重新將N'插入OPEN表。如果鄰接結(jié)點(diǎn)N’在OPEN表中,比較OPEN表中的g(N')值和當(dāng)前的g(N')值,如果當(dāng)前的g(N')更小,那么刪除OPEN表中的N',把N設(shè)成N'的父節(jié)點(diǎn),重新將N'插入OPEN表。***/Node revisited = fringe.revisited(child.getState());if (revisited != null && revisited.evaluation() > child.evaluation()) {fringe.replace(revisited, child);//用child節(jié)點(diǎn)代替Fringe中的 revisited節(jié)點(diǎn)}}}} }//用動(dòng)畫展示問題的解路徑public void solution(Node node){// Fix me// 調(diào)用Problem的drawWorld方法,和simulateResult方法problem.drawWorld();}private Problem problem; }Result:
項(xiàng)目目錄:Searching_Student: E:\University\AI\Searching_student
| 1 | 8, 6, 7, 2, 5, 4, 3, 0, 1 | 31 | 0.132s | 1s |
| 2 | 6, 4, 7, 8, 5, 0, 3, 2, 1 | 31 | 0.133s | 1s |
| 3 | 8, 13, 0, 6, 1, 15, 9, 14, 3, 4, 5, 11, 7, 2, 10, 12 | 52 | 1.929s | |
| 4 | 2,9,5,11, 8,3,4,14, 7,10,1,12, 0,15,6,13 | GC | ||
| 5 | 4,7,0,9,12,10,11,8,14,6,15,1,2,5,3,13 | GC |
Second phase:
所需解決樣例以及最多時(shí)間:
4階的,能夠在與老師程序同級(jí)別的時(shí)間內(nèi),解決相同 的問題實(shí)例。
對(duì)于以上后面幾個(gè)初始狀態(tài),A*就已經(jīng)無法解決。因?yàn)闀?huì)生成太多新狀態(tài)并消耗大量?jī)?nèi)存維護(hù)這些列表。直接爆內(nèi)存。
Algorithm:IDA*
IDA*算法, ID(Iterative Deepening)指的是迭代加深.
迭代加深搜索算法,在搜索過程中采用估值函數(shù),以減少不必要的搜索。
IDA*算法核心:
? 設(shè)置每次可達(dá)的最大深度depth,若沒有到達(dá)目標(biāo)狀態(tài)則加深最大深度。
? 采用估值函數(shù),剪掉f(n)大于depth的路徑
IDA*算法的步驟
a、首先對(duì)初始狀態(tài)進(jìn)行評(píng)估, 評(píng)估值作為最小限度, 而最大限度為自己的設(shè)置.這個(gè)評(píng)估值在這個(gè)問題中可以用此狀態(tài)到正確狀態(tài)的每個(gè)位置的曼哈頓距離來表示.
b、從最小限度到最大限度進(jìn)行遍歷, 此值作為當(dāng)前dfs的限度值, 這個(gè)限度不斷在有效范圍內(nèi)遞增的過程就稱作迭代加深
c、進(jìn)行dfs, 調(diào)整狀態(tài), 將新狀態(tài)加入到新的dfs中, 直到找到了一個(gè)解(由于迭代加深, 此解為最優(yōu)解). 進(jìn)行回溯, 加入路徑, 算法結(jié)束.
IDA* is described as follows:
-
Set threshold equal to the heuristic evaluation of the initial state.
-
Conduct a depth-first search, pruning a branch when the cost of the latest node exceeds threshold. If a solution is found during the search, return it.
-
If no solution is found during the current iteration, increment threshold by the minimum amount it was exceeded, and go back to the previous step.
-
設(shè)置閾值等于初始狀態(tài)的啟發(fā)式評(píng)估。
-
進(jìn)行深度優(yōu)先搜索,在最新節(jié)點(diǎn)的成本超過閾值時(shí)修剪分支。如果在搜索過程中找到解決方案,請(qǐng)將其返回。
-
如果在當(dāng)前迭代期間未找到解決方案,則將閾值增加超過最小值,然后返回上一步驟。
Code:
完整代碼傳送門:???
IDA*代碼除了讀取數(shù)據(jù)時(shí)調(diào)用了框架中的類,其他在一個(gè)類完成。
當(dāng)時(shí)已經(jīng)對(duì)框架有點(diǎn)絕望。由于我使用反向搜索使用框架會(huì)改很多類。加上不能與滑塊公用一個(gè)算法,所以寫在一個(gè)類里面。
第二階段我采用IDA*反向搜索,從目標(biāo)狀態(tài)到初始狀態(tài)搜索,所以看代碼要注意。速度會(huì)快很多,對(duì)于給定樣例有的比老師快幾十倍,分析過原因。不過后面理論又被自己否定,應(yīng)該是由于樣例的特殊性所以加快了速度。
main函數(shù)中調(diào)用:
ArrayList<Problem> problems = ProblemFactory.getProblems(1);test2(problems);test2():
public static void test2(ArrayList<Problem> problems) {for (Problem problem : problems) {Rstep=0;flg=0;result = new int[200][20];PuzzleState state = (PuzzleState) problem.getInitialState();side = state.getSide();tmp = state.getStatus();System.out.println();xx = new int[20];yy = new int[20];for (int i = 0; i < side * side; i++) {if (tmp[i] == 0) pos = i;else {xx[tmp[i]] = (i / side);yy[tmp[i]] = (i % side);}}if (!check(tmp)) System.out.println("No");mat = new int[20];for (int i = 0; i < side * side; i++) mat[i] = i + 1;mat[side * side - 1] = 0;pos = side * side - 1;double time1 = 0;Stopwatch timer1 = new Stopwatch(); // for(int i=0;i<100;i++){ // layer[i]=-1; // }//System.out.println(H(mat));for (bound = H(mat); bound <= 100; bound = dfs(0, H(mat), 4))if (flg == 1) break;time1 = timer1.elapsedTime();Ans_show();System.out.printf("行走了 %s 步,執(zhí)行了 %.3f 秒\n", Rstep, time1);}}check()方法:檢查是否可行解
public static boolean check(int[] a) {int tot=0;int flag=0;for(int i=0;i<side*side;i++){if(a[i]==0) {flag = i/side;continue;}for(int j=0;j<i;j++){if(a[j]>a[i]){tot++;}}}if(side%2==1) return tot%2==0;else{tot+= abs(flag-(side-1));if(tot%2==0) return true;else return false;}}H()方法:算曼哈頓距離
public static int H(int[] mat) {int h = 0;for (int i = 0; i < side*side-1; i++) {h += abs(xx[mat[i]] - (i / side)) + abs(yy[mat[i]] - (i % side));}return h;}ok()方法:判斷是否回到上一個(gè)狀態(tài)
public static boolean ok(int x,int y){if(x>y){int ff=x;x=y;y=ff;}if(x==0&y==1) return false; //與操作 1 1 為1if(x==2&&y==3) return false;return true;}IDA*:
public static int dfs(int step, int h, int las) {//g(n)+h(n)=f(n) 更新f(n) bound 最大限度 采用估值函數(shù),剪掉f(n)大于depth的路徑if (step + h > bound) return step + h; // // if(layer[h]==-1)layer[h]=step; // if(step>layer[h]+15){ // System.out.printf("enter\n"); // return step+h; // }if (h == 0) { // 到達(dá)最終狀態(tài) 輸出g(n)即可// System.out.println(step);flg = 1;Rstep = step;return step;}int pos1 = pos;int ret = 127, x = pos1 / side, y = pos1 % side;int dx, dy, tar, ht, temp, i;for (i = 0; i < 4; i++) { //四個(gè)方向擴(kuò)展dx = x + u[i];dy = y + p[i];if (dx < 0 || dy < 0 || dx > side - 1 || dy > side - 1 || !ok(i, las)) continue;tar = (dx * side) + dy; //計(jì)算出擴(kuò)展出的新節(jié)點(diǎn)的一維坐標(biāo) 2,2 2*4+2= 10mat[pos1] = mat[tar]; // 0的坐標(biāo)等于擴(kuò)展出來的點(diǎn)的坐標(biāo) a.mat[10]=11;mat[tar] = 0;//相當(dāng)于swap()pos = tar;//計(jì)算新的h值//階段三主要在這修改 用不相交模式數(shù)據(jù)庫(kù) h(n) 為當(dāng)前節(jié)點(diǎn)的各點(diǎn)的曼哈頓距離和。ht = h - (Math.abs(xx[mat[pos1]] - dx) + Math.abs(yy[mat[pos1]] - dy)) + Math.abs(xx[mat[pos1]] - x) + Math.abs(yy[mat[pos1]] - y);if (step + ht <= bound)for (int k = 0; k < side*side; k++)result[step][k] = mat[k];temp = dfs(step + 1, ht, i);if (flg == 1) return temp;if (ret > temp) ret = temp;mat[tar] = mat[pos1];mat[pos1] = 0;pos = pos1;}return ret;}Result:
項(xiàng)目目錄:SearchingAstar: E:\University\AI\SearchingAstar
Limited Time為老師IDA跑的時(shí)間。從結(jié)果可以看出,反向IDA速度會(huì)更快。
| 1 | 8, 6, 7, 2, 5, 4, 3, 0, 1 | 31 | 0.000s | 0.047s |
| 2 | 6, 4, 7, 8, 5, 0, 3, 2, 1 | 31 | 0.003s | 0.047s |
| 3 | 8, 13, 0, 6, 1, 15, 9, 14, 3, 4, 5, 11, 7, 2, 10, 12 | 52 | 0.147s | 0.304s |
| 4 | 2,9,5,11, 8,3,4,14, 7,10,1,12, 0,15,6,13 | 51 | 2.423s | 3.652s |
| 5 | 4,7,0,9,12,10,11,8,14,6,15,1,2,5,3,13 | 56 | 0.554s | 12.367s |
| 6 | 12, 10, 3, 2, 0, 7, 14, 9, 1, 15, 5, 6, 8, 4, 13, 11 | 57 | 4.341s | 75.458s |
| 7 | 12, 1, 5, 6, 2, 11, 7, 9, 14, 10, 0, 4, 15, 3, 13, 8 | 50 | 0.058s | 3.671s |
| 8 | 4, 6, 15, 13, 12, 9, 10, 2, 8, 0, 7, 3, 14, 5, 1, 11 | 61 | 10.918s | 299.286s |
| 9 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 1, 2, 0 | 70 | 1761.019s |
Third phase:
所需解決樣例以及最多時(shí)間:
? 4階的,能夠在1分鐘之內(nèi),解出以下實(shí)例
說明:對(duì)于這個(gè)樣例,曾聽說過老師用IDA*大概跑了四十多分鐘得出結(jié)果,不過由于樣例的特殊性,我們可以使用反向搜索,這樣會(huì)增快速度,我跑的時(shí)間不到30分鐘,不過這個(gè)時(shí)間肯定也不是我們所能接受的,我們需要的是在一分鐘內(nèi)
、
對(duì)于15碼最復(fù)雜的狀態(tài),A*肯定不能解,會(huì)爆內(nèi)存。
IDA*雖然在空間上消耗少,但是由于初始狀態(tài)逆序數(shù)大,要搜很久,即大概三四十分鐘才能解決。
解決辦法:
a、曼哈頓距離加線性沖突——優(yōu)化不了多少
b、添加不相交模式數(shù)據(jù)庫(kù)(Disjoint Pattern Database Heuristics)與靜態(tài)6-6-3分區(qū)
Algorithm:IDA*+Disjoint Pattern Database Heuristics
這個(gè)算法就只能硬著頭皮去讀E.Korf和A.Felner的論文Disjoint Pattern Database Heuristics。
就剛開始看了Tsan-sheng Hsu的一個(gè)應(yīng)該是用來講課的pdf,看完之后???開始思考三連:我是,我去,我思???大概花了一下午的時(shí)間看完那篇pdf,當(dāng)時(shí)是很崩潰的,伴隨著這門課的無理取鬧與老師的嗯。。。就已經(jīng)絕望了,此時(shí),滑塊那邊也沒有進(jìn)展,老師張口閉口嚴(yán)格證明就很無語老師你是真的沒搞懂滑塊吧,真準(zhǔn)備棄了。當(dāng)天晚上又因?yàn)橐粋€(gè)評(píng)估函數(shù)的優(yōu)劣性被同學(xué)懟了,心情很糟糕。
第二天醒來已經(jīng)中午,開始接手了N-Puzzle和滑塊兩個(gè)問題,開始了找牛逼網(wǎng)友過程。也找到了那篇論文開始啃,(英語真的毀我一生)。
參考論文:
Additive Pattern Database Heuristics ——E.Korf,A.Felner,S.Hanan
Disjoint Pattern Database Heuristics——E.Korf,A.Felner
以下內(nèi)容來自論文(有些地方翻譯會(huì)有問題)
不斷優(yōu)化的啟發(fā)式
1、曼哈頓距離
? 例如,在滑動(dòng)拼圖拼圖中,要將拼貼從位置x移動(dòng)到位置y,x和y必須相鄰,并且位置y必須為空。如果我們忽略空約束,我們得到一個(gè)簡(jiǎn)化的問題,任何瓦片都可以移動(dòng)到任何相鄰位置,并且多個(gè)瓦片可以占據(jù)相同的位置。在這個(gè)新問題中,瓦片彼此獨(dú)立,我們可以通過沿著最短路徑將每個(gè)瓦片移動(dòng)到其目標(biāo)位置來最佳地解決任何實(shí)例,計(jì)算所做的移動(dòng)的數(shù)量。
? 解決這個(gè)簡(jiǎn)化問題的最佳解決方案的成本恰好是曼哈頓從初始狀態(tài)到目標(biāo)狀態(tài)的距離。由于我們刪除了對(duì)移動(dòng)的約束,原始問題的任何解決方案也是簡(jiǎn)化問題的解決方案,并且針對(duì)簡(jiǎn)化問題的最優(yōu)解決方案的成本是對(duì)原始問題的最優(yōu)解決方案的成本的下限。
不足:曼哈頓距離之所以只是實(shí)際解決方案成本的下限,就是沒有考慮到滑塊的相互作用。
可改進(jìn):
通過考慮其中一些相互作用,我們可以計(jì)算出更準(zhǔn)確的可接受的啟發(fā)函數(shù)。
2、Manhattan Distance+Linear Conflict
Historically, the linear-con?ict heuristic was the ?rst signi?cant improvementover Manhattan distance [5]. It applies to tiles in their goal row or column, but reversed relative to each other. For example, assume the top row of a given state contains the tiles (2 1) in that order, but in the goal state they appear in the order (1 2). To reverse them, one of the tiles must move out of the top row, to allow the other tile to pass by, and then move back into the top row. Since these two moves are not counted in the Manhattan distance of either tile, two moves can be added to the sum of the Manhattan distances of these two tiles without violating admissibility.
(從歷史上看,線性沖突啟發(fā)式是曼哈頓距離的第一個(gè)顯著改進(jìn)[5]。它適用于目標(biāo)行或列中的切片,但相對(duì)于彼此反轉(zhuǎn)。例如,假設(shè)給定狀態(tài)的頂行按順序包含tile(2 1),但在目標(biāo)狀態(tài)下它們按順序出現(xiàn)(1 2)。要反轉(zhuǎn)它們,其中一個(gè)切片必須移出頂行,以允許另一個(gè)切片通過,然后移回頂行。由于這兩個(gè)移動(dòng)不計(jì)入任一區(qū)塊的曼哈頓距離,因此可以將兩個(gè)移動(dòng)添加到這兩個(gè)區(qū)塊的曼哈頓距離的總和而不違反可接受性。)
同樣的想法也可以應(yīng)用于其目標(biāo)列中的切片。實(shí)際上,目標(biāo)位置的圖塊可以同時(shí)參與行和列的沖突。由于解決行沖突所需的額外移動(dòng)是垂直移動(dòng),而解決列沖突所需的額外移動(dòng)是水平移動(dòng),因此兩組移動(dòng)都可以添加到曼哈頓距離,而不會(huì)違反允許性。與曼哈頓距離相比,線性沖突啟發(fā)式減少了一個(gè)數(shù)量級(jí)的節(jié)點(diǎn)數(shù)量,成本幾乎是速度的兩倍,總體加速比超過五倍。接下來的兩行用于不相交的模式數(shù)據(jù)庫(kù)啟發(fā)式。
3、非可加性的模式數(shù)據(jù)庫(kù)
圖顯示了十五個(gè)拼圖拼貼的子集,稱為邊緣拼貼。對(duì)于給定狀態(tài),使邊緣拼貼到其目標(biāo)位置所需的最小移動(dòng)次數(shù)(包括其他拼塊的所需移動(dòng))是解決整個(gè)拼圖所需的移動(dòng)次數(shù)的下限。
與其他瓦片的位置無關(guān)。
因此,我們可以預(yù)先計(jì)算所有這些值,將它們存儲(chǔ)在內(nèi)存中,并在搜索過程中查找它們。由于有七個(gè)邊緣拼貼和一個(gè)空白,以及十六個(gè)不同的位置,邊緣拼貼和空白的可能排列總數(shù)為16!/(16-8)!= 518,918,400。
對(duì)于每個(gè)排列,我們存儲(chǔ)將數(shù)字圖塊和空白移動(dòng)到其目標(biāo)位置所需的移動(dòng)次數(shù),這需要不到一個(gè)字節(jié)。因此,我們可以將整個(gè)模式數(shù)據(jù)庫(kù)表存儲(chǔ)在少于495兆字節(jié)的內(nèi)存中。
存儲(chǔ)表后,我們使用IDA *搜索特定問題實(shí)例的最佳解決方案。在生成每個(gè)狀態(tài)時(shí),使用數(shù)字圖塊和空白的位置來計(jì)算圖案數(shù)據(jù)庫(kù)中的索引,并且使用相應(yīng)的條目,即解決數(shù)字圖塊和空白所需的移動(dòng)的數(shù)量,作為該狀態(tài)的啟發(fā)式值。
使用這種模式數(shù)據(jù)庫(kù),Culberson和Schaeffer將生成的節(jié)點(diǎn)數(shù)量減少了346倍,并將運(yùn)行時(shí)間縮短了6倍,與曼哈頓距離相比[1]。將此與另一個(gè)模式數(shù)據(jù)庫(kù)相結(jié)合,并將兩個(gè)數(shù)據(jù)庫(kù)值的最大值作為整體啟發(fā)式值,將生成的節(jié)點(diǎn)減少大約一千,將運(yùn)行時(shí)間減少十二。
局限:
非加性模式數(shù)據(jù)庫(kù)的主要限制是它們無法解決更大的問題。例如,由于二十四個(gè)拼圖包含25個(gè)不同的位置,一個(gè)覆蓋n個(gè)拼貼和空白的模式數(shù)據(jù)庫(kù)需要25!/(25 - n - 1)!條目。僅有六個(gè)圖塊和空白的數(shù)據(jù)庫(kù)將需要超過24億個(gè)條目。此外,來自僅六個(gè)瓦片的數(shù)據(jù)庫(kù)的值將小于所有瓦片的曼哈頓距離。對(duì)于多個(gè)數(shù)據(jù)庫(kù),允許組合它們的最佳方法是獲取其值的最大值,即使這些圖塊組是不相交的。原因是非加性模式數(shù)據(jù)庫(kù)值包括解決模式切片所需的所有移動(dòng),包括其他切片的移動(dòng)
可改進(jìn):
我們希望能夠在不違反可接受性的情況下對(duì)其值進(jìn)行求和,以獲得更準(zhǔn)確的啟發(fā)式,而不是采用最大的不同模式數(shù)據(jù)庫(kù)值。這是不相交模式數(shù)據(jù)庫(kù)的主要思想。
4、不相交的模式數(shù)據(jù)庫(kù)(可加性的)
為了構(gòu)建滑動(dòng)拼圖的不相交模式數(shù)據(jù)庫(kù),我們將拼貼劃分為不相交的組,這樣任何拼貼都不屬于多個(gè)組。然后,我們預(yù)先計(jì)算每組中瓦片最小移動(dòng)次數(shù)的表格,這些移動(dòng)是將這些瓦片移動(dòng)到其目標(biāo)位置所需的。我們稱這些表為一組,每組一個(gè)瓦片,一個(gè)不相交的模式數(shù)據(jù)庫(kù),或簡(jiǎn)稱為adisjoint數(shù)據(jù)庫(kù)。然后,給定搜索中的特定狀態(tài),對(duì)于每組圖塊,我們使用這些圖塊的位置來計(jì)算相應(yīng)表格中的索引,檢索解決該組中圖塊所需的移動(dòng)次數(shù),然后將每個(gè)組的值相加,以計(jì)算給定狀態(tài)的整體啟發(fā)式。該值至少與曼哈頓距離一樣大,并且通常更大,因?yàn)樗紤]了同一組中的切片之間的交互。
上述不相交數(shù)據(jù)庫(kù)和非附加數(shù)據(jù)庫(kù)之間的關(guān)鍵區(qū)別在于,非加法數(shù)據(jù)庫(kù)包括解決圖案圖塊所需的所有移動(dòng),包括不在圖案集中的圖塊移動(dòng)。結(jié)果,給定兩個(gè)這樣的數(shù)據(jù)庫(kù),即使它們的區(qū)塊之間沒有重疊,我們也只能將這兩個(gè)值中的最大值作為可接受的啟發(fā)式,因?yàn)樵谝粋€(gè)數(shù)據(jù)庫(kù)中計(jì)數(shù)的移動(dòng)可能會(huì)移動(dòng)另一個(gè)數(shù)據(jù)庫(kù)中的區(qū)塊,因此這些舉動(dòng)將被計(jì)算兩次。在不相交的數(shù)據(jù)庫(kù)中,我們只計(jì)算組中切片的移動(dòng)。
這兩種類型的數(shù)據(jù)庫(kù)之間的第二個(gè)區(qū)別是我們的不相交數(shù)據(jù)庫(kù)不考慮空白位置,減小它們的大小。一個(gè)不相交的數(shù)據(jù)庫(kù)包含了解決一組圖塊所需的最小移動(dòng)量,對(duì)于所有可能的空白位置。
該搜索的狀態(tài)由所討論的圖塊的位置和空白的位置唯一地確定,并且僅計(jì)算感興趣的圖塊的移動(dòng)。由于滑動(dòng)拼圖拼圖的操作員是可逆的,我們可以從其目標(biāo)位置開始對(duì)每個(gè)拼貼執(zhí)行單個(gè)搜索,并記錄將拼塊移動(dòng)到每個(gè)其他位置需要多少移動(dòng)。對(duì)所有圖塊執(zhí)行此操作會(huì)生成一組表格,這些表格為每個(gè)圖塊的每個(gè)可能位置提供距其目標(biāo)位置的曼哈頓距離。
對(duì)于每個(gè)圖塊,我們可以使用上述廣度搜索來自動(dòng)計(jì)算將圖塊從任何位置移動(dòng)到其目標(biāo)位置所需的最小移動(dòng)次數(shù)的表格。這樣的一組表格將包含從每個(gè)位置到其目標(biāo)位置的每個(gè)瓦片的曼哈頓距離。然后,給定一個(gè)特定的狀態(tài),我們只需在相應(yīng)的表中查找每個(gè)圖塊的位置并對(duì)結(jié)果值求和,從而計(jì)算曼哈頓距離的總和。
因此我們可以將曼哈頓距離相加以獲得可接受的啟發(fā)式
作為一般規(guī)則,在對(duì)圖塊進(jìn)行分區(qū)時(shí),我們希望將目標(biāo)狀態(tài)中彼此靠近的圖塊組合在一起,因?yàn)檫@些圖塊將彼此交互最多。
使用[12]中開發(fā)的分析結(jié)果,我們可以預(yù)測(cè),單獨(dú)使用曼哈頓距離解決二十四難題每個(gè)問題平均需要大約5萬年!對(duì)于10,000個(gè)初始狀態(tài)的隨機(jī)樣本,曼哈頓平均距離是76.078個(gè)移動(dòng),而對(duì)于我們的不相交數(shù)據(jù)庫(kù)啟發(fā)式,它是81.607個(gè)移動(dòng)。
第一行給出了曼哈頓距離啟發(fā)式的結(jié)果。
第二行是通過線性沖突增強(qiáng)的曼哈頓距離。
第三行表示啟發(fā)式,它是圖4左側(cè)所示的七和八數(shù)據(jù)庫(kù)值的總和
第四行表示通過從第三行的啟發(fā)式開始計(jì)算的啟發(fā)式。然后,我們計(jì)算圖4右側(cè)所示的七和八數(shù)據(jù)庫(kù)值的總和。最后,整體啟發(fā)式是這兩個(gè)總和中的最大值。
第一個(gè)數(shù)據(jù)列顯示了啟發(fā)函數(shù)在1000個(gè)初始狀態(tài)下的平均值。第二列給出了每個(gè)問題實(shí)例生成的平均節(jié)點(diǎn)數(shù),以找到第一個(gè)最優(yōu)解。第三列顯示算法的平均速度,以每秒節(jié)點(diǎn)數(shù)為單位,, on a 440 MegaHertz Sun Ultra10 workstation. 。第四列表示找到第一個(gè)最佳解決方案的平均運(yùn)行時(shí)間(以秒為單位)。最后一列給出了生成的平均節(jié)點(diǎn)數(shù),以找到問題實(shí)例的所有最優(yōu)解。
采用其最大值來組合來自不同模式數(shù)據(jù)庫(kù)的啟發(fā)式算法。這是最通用的方法,因?yàn)槿魏蝺蓚€(gè)可接受的啟發(fā)式方法的最大值始終是另一個(gè)可接受的啟發(fā)式方法。我們引入了不相交模式數(shù)據(jù)庫(kù),以允許將來自不同數(shù)據(jù)庫(kù)的值相加,從而產(chǎn)生更準(zhǔn)確的啟發(fā)式值。不相交的模式數(shù)據(jù)庫(kù)將子目標(biāo)集劃分為不相交的組,然后將解決每個(gè)組中所有子目標(biāo)的成本加在一起。這要求組不相交,并且單個(gè)運(yùn)算符僅影響單個(gè)組中的子目標(biāo)。例如,在滑動(dòng)拼圖游戲中,每個(gè)操作員僅移動(dòng)一個(gè)拼貼。這與獲取不同值的最大值一樣有效,但更準(zhǔn)確,并且仍然可以接受。
5、裁剪
used a technique, based on ?nite-state machines (FSMs), to prune duplicate nodes representing the same state arrived at via different paths in the graph
*使用了一種基于有限狀態(tài)機(jī)(FSM)的技術(shù)來修剪表示通過圖中不同路徑到達(dá)的相同狀態(tài)的重復(fù)節(jié)點(diǎn)[16]。 FSM修剪減少了IDA 在五個(gè)問題上產(chǎn)生的節(jié)點(diǎn)數(shù)量,范圍從2.4到3.6。對(duì)于這項(xiàng)工作,我們沒有使用FSM修剪,因?yàn)樵摷夹g(shù)很復(fù)雜,結(jié)果取決于所使用的特定FSM,使得其他研究人員難以重現(xiàn)相同的結(jié)果。
6、動(dòng)態(tài)分區(qū)模式數(shù)據(jù)庫(kù)啟發(fā)式 Dynamically-Partitioned Database Heuristics
? 之前(Korf&Felner,2002)我們展示了如何將滑動(dòng)拼圖塊靜態(tài)劃分為不相交的拼貼組以計(jì)算可接受的啟發(fā)式,為每個(gè)狀態(tài)和問題實(shí)例使用相同的分區(qū)。在這里,我們擴(kuò)展該方法并顯示它也適用于其他域。我們還提出了另一種加法啟發(fā)式方法,我們將其稱為動(dòng)態(tài)分區(qū)模式數(shù)據(jù)庫(kù)。在這里,我們動(dòng)態(tài)地將問題劃分為每個(gè)搜索狀態(tài)的不相交的子問題。
論文:Additive Pattern Database Heuristics
15-puzzle ;24-puzzle 動(dòng)態(tài)模式數(shù)據(jù)庫(kù)會(huì)比靜態(tài)模式數(shù)據(jù)庫(kù)慢。
35-puzzle 動(dòng)態(tài)模式數(shù)據(jù)庫(kù)會(huì)比靜態(tài)模式數(shù)據(jù)庫(kù)快。
Code:不相交模式數(shù)據(jù)庫(kù)靜態(tài)6-6-3分區(qū)
完整代碼傳送門:???
模式數(shù)據(jù)庫(kù)生成器:
/*** File: PatternDatabaseGenerator.java* Author: Brian Borowski* Date created: June 10, 2010* Date last modified: May 13, 2011*/ import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.StringTokenizer;/*** Examples:* When generating a complete pattern database (i.e. no dummy tiles):* java PatternDatabaseGenerator 8 1,2,3,4,5,6,7,8,0 8-puzzle.db** When generating a disjoint additive pattern database (i.e. dummy tiles 'x'):* java PatternDatabaseGenerator 15 0,2,3,4,x,x,x,x,x,x,x,x,x,x,x,0 15-puzzle-663-0.db* java PatternDatabaseGenerator 15 1,x,x,x,5,6,x,x,9,10,x,x,13,x,x,0 15-puzzle-663-1.db* java PatternDatabaseGenerator 15 x,x,x,x,x,x,7,8,x,x,11,12,x,14,15,0 15-puzzle-663-2.db*/ public final class PatternDatabaseGenerator {public static final byte KEY_NOT_FOUND = -1;final int numOfTiles, numOfTilesMinusOne, dimension;PrimitiveHashMap tempMap;List<Entry> stateToCostEntries_8_puzzle;byte[] costTable_15_puzzle;long[] configTable_15_puzzle;public PatternDatabaseGenerator(final int numOfTiles, final long boardConfig,final byte dummyTile, final String filename) {this.numOfTiles = Node.numOfTiles = numOfTiles;this.numOfTilesMinusOne = numOfTiles - 1;this.dimension = Node.dimension = (int)Math.sqrt(numOfTiles);DataOutputStream dos = null;if (filename != null) {try {dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(filename)));} catch (final FileNotFoundException fnfe) {System.err.println("Error: Cannot open file '" + filename + "' for output.");System.exit(1);}}if (numOfTilesMinusOne == 15) {generateFifteenPuzzleDB(dummyTile, boardConfig);outputFifteenPuzzleData(filename, dos);} else {generateEightPuzzleDB(boardConfig);outputEightPuzzleData(filename, dos);}}private void generateFifteenPuzzleDB(final byte dummyTile,final long boardConfig) {final boolean[] tilesInSubset = computeSubset(dummyTile, boardConfig);final int[] tilePositions = new int[tilesInSubset.length];int numTilesInSubset = 0;for (int i = 0; i < tilePositions.length; ++i) {if (tilesInSubset[i]) {tilePositions[i] = numTilesInSubset++;}}breadthFirstSearch(boardConfig, tilesInSubset);final int tableLength = (int)Math.pow(2, numTilesInSubset * 4);costTable_15_puzzle = new byte[tableLength];configTable_15_puzzle = new long[tableLength];for (int i = tableLength - 1; i >= 0; --i) {costTable_15_puzzle[i] = KEY_NOT_FOUND;}System.err.println("Total states visited: " + tempMap.size());System.err.print("Removing duplicates...");final Set<Entry> entries = tempMap.entrySet();final Iterator<Entry> it = entries.iterator();while (it.hasNext()) {final Entry entry = it.next();final long config = entry.getKey();final byte movesRequired = entry.getValue();final int index = indexFor(config, true, tilesInSubset, tilePositions);final byte moves = costTable_15_puzzle[index];if (moves == KEY_NOT_FOUND || movesRequired < moves) {configTable_15_puzzle[index] = config;costTable_15_puzzle[index] = movesRequired;}}int numElements = 0;for (int i = tableLength - 1; i >= 0; --i) {if (costTable_15_puzzle[i] != KEY_NOT_FOUND) {++numElements;}}System.err.println("done");System.err.println("States in subset: " + numElements);}private void generateEightPuzzleDB(final long boardConfig) {breadthFirstSearch(boardConfig, null);final PrimitiveHashMap costMap_8_puzzle = new PrimitiveHashMap();System.err.println("Total states visited: " + tempMap.size());System.err.print("Removing duplicates...");final Set<Entry> entries = tempMap.entrySet();final Iterator<Entry> it = entries.iterator();while (it.hasNext()) {final Entry entry = it.next();final long config = entry.getKey();final byte movesRequired = entry.getValue();final byte moves = costMap_8_puzzle.get(config);if (moves == PrimitiveHashMap.KEY_NOT_FOUND || movesRequired < moves) {costMap_8_puzzle.put(config, movesRequired);}it.remove();}System.err.println("done");final int numElements = costMap_8_puzzle.size();stateToCostEntries_8_puzzle = new LinkedList<Entry>(costMap_8_puzzle.entrySet());System.err.print("Sorting entries...");Collections.sort(stateToCostEntries_8_puzzle, new Comparator<Entry>() {public int compare(final Entry e1, final Entry e2) {return e1.getValue() - e2.getValue();}});System.err.println("done");System.err.println("States in subset: " + numElements);}private void outputFifteenPuzzleData(final String filename,final DataOutputStream dos) {System.err.print("Writing file...");if (filename == null) {// Write values to stdout. User can redirect stdout to a file, if desired.for (int i = 0; i < configTable_15_puzzle.length; ++i) {final long config = configTable_15_puzzle[i];System.out.println((i + 1) + "," + config + "," +costTable_15_puzzle[i] + "," +Utility.longToString(config, numOfTiles));}} else {int i = 0;try {for ( ; i < costTable_15_puzzle.length; ++i) {dos.writeByte(costTable_15_puzzle[i]);}} catch (final IOException ioe) {System.err.println("Error: Cannot write entry " + i + " to file.");System.exit(1);} finally {try {if (dos != null) {dos.close();}} catch (final IOException ioe) { }}}System.err.println("done");}private void outputEightPuzzleData(final String filename,final DataOutputStream dos) {System.err.print("Writing file...");final Iterator<Entry> listIter = stateToCostEntries_8_puzzle.iterator();if (filename == null) {// Write values to stdout. User can redirect stdout to a file, if desired.int i = 1;while (listIter.hasNext()) {final Entry entry = listIter.next();final long config = entry.getKey();System.out.println(i + "," + config + "," + entry.getValue() +"," + Utility.longToString(config, numOfTiles));++i;}} else {int i = 0;try {while (listIter.hasNext()) {final Entry entry = listIter.next();dos.writeLong(((Long)entry.getKey()).longValue());dos.writeByte(((Byte)entry.getValue()).byteValue());++i;}} catch (final IOException ioe) {System.err.println("Error: Cannot write entry " + i + " to file.");System.exit(1);} finally {try {if (dos != null) {dos.close();}} catch (final IOException ioe) { }}}System.err.println("done");}private void breadthFirstSearch(final long boardConfig,final boolean[] tilesInSubset) {BFSNode currentState = new BFSNode(boardConfig);currentState.cost = 0;tempMap = new PrimitiveHashMap();tempMap.put(boardConfig, (byte)0);final Queue<BFSNode> queue = new LinkedList<BFSNode>();queue.add(currentState);byte previous = 1;while (true) {final char fromDirection = currentState.direction;if (fromDirection != 'R') {final BFSNode left = currentState.moveLeftNode(tilesInSubset);if (left != null) {final byte moves = tempMap.get(left.boardConfig);if (moves == PrimitiveHashMap.KEY_NOT_FOUND || left.cost < moves) {tempMap.put(left.boardConfig, left.cost);queue.add(left);}}}if (fromDirection != 'L') {final BFSNode right = currentState.moveRightNode(tilesInSubset);if (right != null) {final byte moves = tempMap.get(right.boardConfig);if (moves == PrimitiveHashMap.KEY_NOT_FOUND || right.cost < moves) {tempMap.put(right.boardConfig, right.cost);queue.add(right);}}}if (fromDirection != 'D') {final BFSNode up = currentState.moveUpNode(tilesInSubset);if (up != null) {final byte moves = tempMap.get(up.boardConfig);if (moves == PrimitiveHashMap.KEY_NOT_FOUND || up.cost < moves) {tempMap.put(up.boardConfig, up.cost);queue.add(up);}}}if (fromDirection != 'U') {final BFSNode down = currentState.moveDownNode(tilesInSubset);if (down != null) {final byte moves = tempMap.get(down.boardConfig);if (moves == PrimitiveHashMap.KEY_NOT_FOUND || down.cost < moves) {tempMap.put(down.boardConfig, down.cost);queue.add(down);}}}if (!queue.isEmpty()) {currentState = queue.remove();final byte movesRequired = currentState.cost;if (movesRequired > previous) {System.err.println("Generating boards with up to " + previous +" moves; map size: " + tempMap.size());previous = movesRequired;}} else {break;}}}private int indexFor(final long boardConfig, final boolean isFifteenPuzzle,final boolean[] tilesInSubset, final int[] tilePositions) {if (isFifteenPuzzle) {int hashCode = 0;for (int i = numOfTilesMinusOne; i >= 0; --i) {final int tile = (int)((boardConfig >> (i << 2)) & 0xF);if (tilesInSubset[tile]) {hashCode |= i << (tilePositions[tile] << 2);}}return hashCode;}return (int)boardConfig;}private boolean[] computeSubset(final byte dummyValue, final long boardConfig) {final boolean[] tilesInSubset = new boolean[numOfTiles];for (int pos = numOfTiles - 1; pos >= 0; --pos) {final byte tile = (byte)((boardConfig >> (pos << 2)) & 0xF);if (tile != dummyValue && tile != 0) {tilesInSubset[tile] = true;}}return tilesInSubset;}private static byte getArray(final String arrayString, final byte[] tiles,final int numOfTiles) {final StringTokenizer st = new StringTokenizer(arrayString, ",");final int numOfTokens = st.countTokens();// Validate the number of tiles entered.if (numOfTokens < numOfTiles) {System.out.println("Error: Input contains only " + numOfTokens +" of the " + numOfTiles + " tiles.");System.exit(1);} else if (numOfTokens > numOfTiles) {System.out.println("Error: Input exceeds required " +numOfTiles + " tiles.");System.exit(1);}// Create an array of String representations of the tile numbers.final String[] numStrings = new String[numOfTokens];int i = 0;while (st.hasMoreTokens()) {numStrings[i++] = st.nextToken();}// Make sure each string is a number.final int[] tilePositions = new int[numOfTiles];for (i = 0; i < tiles.length; ++i) {tiles[i] = -1;tilePositions[i] = -1;}for (i = 0; i < numStrings.length; ++i) {final String s = numStrings[i];try {final byte tile = Byte.parseByte(s);tiles[i] = tile;tilePositions[tile] = i;} catch (final NumberFormatException nfe) {if (!s.trim().toLowerCase().equals("x")) {System.err.println("Error: Expected integer or 'X' at index " + (i + 1) +", received '" + numStrings[i] + "'.");System.exit(1);}}}byte dummyTile = -1;// Make sure no tile number is missing from the input.for (i = 0; i < numOfTiles; ++i) {if (tilePositions[i] == -1) {if (i == 0) {System.err.println("Error: Tile 0 is missing for input.");System.exit(1);}dummyTile = (byte)i;break;}}// Replace 'X' (-1) tiles with the dummy value.for (i = 0; i < tiles.length; ++i) {if (tiles[i] == -1) {tiles[i] = dummyTile;}}return dummyTile;}public static void main(final String[] args) {if (args.length < 2 || args.length > 3) {System.err.println("Usage: java PatternDatabaseGenerator <num of tiles> <tile order> [filename]");System.exit(1);}int puzzleSize = 0;try {puzzleSize = Integer.parseInt(args[0].trim());if ((puzzleSize != 8) && (puzzleSize != 15)) {System.err.println("Error: Puzzle size must be either 8 or 15.");System.exit(1);}++puzzleSize;} catch (final NumberFormatException nfe) {System.err.println("Error: Puzzle size must be either 8 or 15.");System.exit(1);}final String tileOrder = args[1].trim();final byte[] tiles = new byte[puzzleSize];final byte dummyTile = getArray(tileOrder, tiles, puzzleSize);System.err.println("Using dummy tile: " + dummyTile);String filename = null;if (args.length == 3) {filename = args[2];}new PatternDatabaseGenerator(puzzleSize, Utility.byteArrayToLong(tiles), dummyTile, filename);} }IDA*:第三階段由于使用不相交可加性的靜態(tài)模式數(shù)據(jù)庫(kù),所以不能像第二階段從目標(biāo)狀態(tài)找到初始狀態(tài)。
public static int dfs(int step, int h, int las) {if (step + h > bound) return step + h; // 從目標(biāo)開始找 f(n)剛開始最小 如果有更大的則更新 f(n) 反方向找 // if(layer[h]==-1)layer[h]=step; // if(step>layer[h]+15){ // System.out.printf("enter\n"); // return step+h; // }if (h == 0) { // 到達(dá)最終狀態(tài) 輸出g(n)即可// System.out.println(step);flg = 1;Rstep = step;return step;}int pos1 = pos;int ret = 127, x = pos1 / side, y = pos1 % side;int dx, dy, tar, ht, temp, i;for (i = 0; i < 4; i++) { //四個(gè)方向擴(kuò)展dx = x + u[i];dy = y + p[i];if (dx < 0 || dy < 0 || dx > side - 1 || dy > side - 1 || !ok(i, las)) continue;tar = (dx * side) + dy; //計(jì)算出擴(kuò)展出的新節(jié)點(diǎn)的一維坐標(biāo) 2,2 2*4+2= 10tmp[pos1] = tmp[tar]; // 0的坐標(biāo)等于擴(kuò)展出來的點(diǎn)的坐標(biāo) a.mat[10]=11;tmp[tar] = 0;//相當(dāng)于swap()pos = tar;//如果換一個(gè)評(píng)估函數(shù)呢//階段三 使用不相交模式數(shù)據(jù)庫(kù)得到評(píng)估函數(shù) ht值為分為的塊的h的和加起來//ht = getH(tmp);//ht = h - (Math.abs(xx[mat[pos1]] - dx) + Math.abs(yy[mat[pos1]] - dy)) + Math.abs(xx[mat[pos1]] - x) + Math.abs(yy[mat[pos1]] - y);if (step + ht <= bound) {for (int k = 0; k < side * side; k++) {result[step][k] = tmp[k];}//System.out.println(i);if(i==0){redir[step]='r';}else if(i==1){redir[step]='l';}else if(i==2){redir[step]='d';}else{redir[step]='u';}}temp = dfs(step + 1, ht, i);if (flg == 1) return temp;if (ret > temp) ret = temp;tmp[tar] = tmp[pos1];tmp[pos1] = 0;pos = pos1;}return ret;}獲取h值:
static final int[] tilePositions = {-1, 0, 0, 1, 2, 1, 2, 0, 1, 3, 4, 2, 3, 5, 4, 5}; static final int[] tileSubsets = {-1, 1, 0, 0, 0, 1, 1, 2, 2, 1, 1, 2, 2, 1, 2, 2}; public static int getH(int [] tmp) {int index0 = 0, index1 = 0, index2 = 0;for (int pos = 15; pos >= 0; --pos) {final int tile = tmp[pos];if (tile != 0) {final int subsetNumber = tileSubsets[tile];switch (subsetNumber) {case 2:index2 |= pos << (tilePositions[tile] << 2);break;case 1:index1 |= pos << (tilePositions[tile] << 2);break;default:index0 |= pos << (tilePositions[tile] << 2);break;}}}return PuzzleConfiguration.costTable_15_puzzle_0[index0] +PuzzleConfiguration.costTable_15_puzzle_1[index1] +PuzzleConfiguration.costTable_15_puzzle_2[index2]; }Result:
項(xiàng)目目錄: SearchingAstar: E:\University\AI\SearchingAstar3\SearchingAstar
| 3 | 8, 13, 0, 6, 1, 15, 9, 14, 3, 4, 5, 11, 7, 2, 10, 12 | 52 | 1.045s | |
| 4 | 2,9,5,11, 8,3,4,14, 7,10,1,12, 0,15,6,13 | 51 | 0.070s | |
| 5 | 4,7,0,9,12,10,11,8,14,6,15,1,2,5,3,13 | 56 | 0.028s | |
| 6 | 12, 10, 3, 2, 0, 7, 14, 9, 1, 15, 5, 6, 8, 4, 13, 11 | 57 | 0.180s | |
| 7 | 12, 1, 5, 6, 2, 11, 7, 9, 14, 10, 0, 4, 15, 3, 13, 8 | 50 | 0.009s | |
| 8 | 4, 6, 15, 13, 12, 9, 10, 2, 8, 0, 7, 3, 14, 5, 1, 11 | 61 | 0.987s | |
| 9 | 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 1, 2, 0 | 70 | 4.758s | 60s |
Reference resources
(1)Ariel Felner,Richard E. Korf ,Disjoint pattern database heuristics
(2)Ariel Felner,Richard E. Korf,Sarit Hanan,Additive Pattern Database Heuristics
https://blog.csdn.net/u013009575/article/details/17140915
http://www.brian-borowski.com/software/puzzle/
http://www.ise.bgu.ac.il/engineering/upload/4273/naij.pdf
http://www.cnblogs.com/beilin/p/5981483.html
https://www.cnblogs.com/fujudge/p/7398153.html?utm_source=itdadao&utm_medium=referral
https://blog.csdn.net/shen_gan/article/details/8146027
https://www.gamedev.net/articles/programming/artificial-intelligence/a-pathfinding-for-beginners-r2003/
總結(jié)
以上是生活随笔為你收集整理的N-puzzle-Problem的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 向UBOOT 中添加自己的板子
- 下一篇: 关于CCD摄像头