A*演算法與8數碼問題
A*與八數碼問題
最近自學A*,看來很多神犇程式,似乎都很煩,今天我要來談談A*與8數碼問題
先給個例題:
Description
八 方塊移動遊戲要求從一個含8個數字(用1-8表示)的方塊以及一個空格方塊(用0表示)的3×3矩陣的起始狀態開始,不斷移動該空格方塊以使其和相鄰的方 塊互換,直至達到所定義的目標狀態。空格方塊在中間位置時有上、下、左、右4個方向可移動,在四個角落上有2個方向可移動,在其他位置上有3個方向可移 動。例如,假設一個3×3矩陣的初始狀態為:
另外,在所有可能的從初始狀態到目標狀態的移動路徑中,步數最少的路徑被稱為最短路徑;在上面的例子中,最短路徑為5。如果不存在從初始狀態到目標狀態的任何路徑,則稱該組狀態無解。 請設計有效的演算法找到從八方塊的某初始狀態到某目標狀態的所有可能路徑中的最短路徑。
Input
程式需讀入初始狀態和目標狀態,這兩個狀態都由9個數字組成(0表示空格,1-8表示8個數字方塊)。
Output
如果輸入資料有解,輸出一個表示最短路徑的非負的整數;如果輸入資料無解,輸出-1。
Sample Input
803214765
123804765
Sample Output
5
好了,題目看起來怎麼瞎搞都行,但是效率值得考慮。
這裡我使用最佳優先搜尋策略:
它是將深度優先和廣度優先搜尋優點進行組合而成的一種簡單方法。
在最佳優先搜尋中,有一個評價函式,總是選擇目前為止產生的所有結點中具有最小代價的結點。
因此最佳優先搜尋具有全域性觀點。
以上述問題為例:
步驟一 . 使用評價函式構造一個堆,首先構造由根結點組成的一元堆。
步驟二 . 考察堆頂元素是否為目標結點。如果是,演算法停止;否則轉向步驟三。
步驟三 . 刪除堆頂元素並擴充套件堆頂,增加該元素的後代到堆中。
步驟四 . 如果堆為空,那麼失敗;否則,轉向步驟二。
好了理論先放一邊,下面貼程式碼:
#include <iostream> #include <vector> #include <bitset> #include <cmath> #include <iomanip> #include <string> #include <string.h> #include <time.h> #include <memory.h> #include <stdio.h> #include <cmath> #include <stdlib.h> #include <algorithm> #include <stack> #include <queue> #include <cstdio> #include <climits> #define intmin -2147483640 #define intmax 2147483640 using namespace std; const int Cantor_Expansion_MAXN=3628799+100; const int Move_Tag_X[7]={0,1,-1,0}; const int Move_Tag_Y[7]={1,0,0,-1}; const int Cantor_Num[15]={0,1,2,6,24,120,720,5040,40320,362880,3628800}; typedef struct Gentil_HP_Node { int num[4][4]; int Step,Bound; }node; bool operator>(const struct Gentil_HP_Node a,const struct Gentil_HP_Node b) {return a.Bound>b.Bound;} priority_queue < node,vector<node>,greater<node> > Q; node StMode,EdMode; bool Cantor[Cantor_Expansion_MAXN]; int Bound_Cantor_Expansion (const node S) //康託展開,自行百度 { return Cantor_Num[9]*S.num[1][1]+ Cantor_Num[8]*S.num[1][2]+ Cantor_Num[7]*S.num[1][3]+ Cantor_Num[6]*S.num[2][1]+ Cantor_Num[5]*S.num[2][2]+ Cantor_Num[4]*S.num[2][3]+ Cantor_Num[3]*S.num[3][1]+ Cantor_Num[2]*S.num[3][2]+ Cantor_Num[1]*S.num[3][3]; } void Put_Num_Into_Node (int x,node &S) { S.num[3][3]=x%10;x/=10; S.num[3][2]=x%10;x/=10; S.num[3][1]=x%10;x/=10; S.num[2][3]=x%10;x/=10; S.num[2][2]=x%10;x/=10; S.num[2][1]=x%10;x/=10; S.num[1][3]=x%10;x/=10; S.num[1][2]=x%10;x/=10; S.num[1][1]=x%10;x/=10; } bool Judge_In_Board (int x,int y) { if (x>=1 && y>=1 && x<4 && y<4) return 1; return 0; } void init () { int Stnum,Ednum; scanf ("%d%d",&Stnum,&Ednum); Put_Num_Into_Node (Stnum,StMode); Put_Num_Into_Node (Ednum,EdMode); StMode.Step=0; } int Bound_for_Node (node S,node T) //估價函式 { int Num=0; if (S.num[3][3]!=T.num[3][3]) Num++; if (S.num[3][2]!=T.num[3][2]) Num++; if (S.num[3][1]!=T.num[3][1]) Num++; if (S.num[2][3]!=T.num[2][3]) Num++; if (S.num[2][2]!=T.num[2][2]) Num++; if (S.num[2][1]!=T.num[2][1]) Num++; if (S.num[1][3]!=T.num[1][3]) Num++; if (S.num[1][2]!=T.num[1][2]) Num++; if (S.num[1][1]!=T.num[1][1]) Num++; return Num+S.Step; } void Out_For_Test (const node X) { cout<<X.num[1][1]<<" "<<X.num[1][2]<<" "<<X.num[1][3]<<endl; cout<<X.num[2][1]<<" "<<X.num[2][2]<<" "<<X.num[2][3]<<endl; cout<<X.num[3][1]<<" "<<X.num[3][2]<<" "<<X.num[3][3]<<endl<<endl; } void A_Star_Search () { node Now,next; StMode.Bound=(Bound_for_Node (StMode,EdMode)); Q.push (StMode); Cantor[Bound_Cantor_Expansion (StMode)]=1; int ans=intmax; int i,j,k,sx,sy,tmp; while (!Q.empty ()) { Now=Q.top ();Q.pop (); if (Now.Bound==Now.Step){ans=min (Now.Step,ans);break;} for (i=1;i<4;i++) for (j=1;j<4;j++) { if (Now.num[i][j]==0) { for (k=0;k<4;k++) { next=Now; next.Step ++; sx=i+Move_Tag_X[k],sy=j+Move_Tag_Y[k]; if (Judge_In_Board (sx,sy)) { next.num[i][j]+=next.num[sx][sy]; next.num[sx][sy]-=next.num[i][j]; tmp=Bound_Cantor_Expansion (next); if (!Cantor[tmp]) { Cantor[tmp]=1; next.Bound=Bound_for_Node (next,EdMode); Q.push (next); } } } goto Loop; } } Loop: ; } if (ans==intmax) printf ("-1"); else cout<<ans; } int main() { init (); A_Star_Search (); }
上題中用到了一個估價函式,所謂估價函式就是對最優解的估計。
我們不妨用g(n) 表示從解空間樹的根到結點n的路徑長度。
h*(n)表示從結點n到目標結點的最優路徑長度。
結點n的代價f*(n) = g(n) + h*(n)
儘管有許多方法估計h*(n),但A*演算法保證總是使h(n) ≤ h*(n)。也就是說,應該使用h*(n)的保守估計。這意味著,在A*演算法中,總是使用
f(n) = g(n) + h(n) ≤ g(n) + h*(n) = f*(n) 作為估價函式。
注意到A*演算法採用最佳優先規則,這意味著在所有將擴充套件的結點中,選擇具有最小代價的結點作為下一擴充套件結點。
A*演算法具有下列終止規則:如果被選擇的結點也是目標結點,那麼這個結點代表一個最優解並且過程可被終止。
證明直接COPY一下課件:
最後給出例題一道:
今天就說到這,希望大家點個贊。
相關推薦
A*演算法與8數碼問題
A*與八數碼問題 最近自學A*,看來很多神犇程式,似乎都很煩,今天我要來談談A*與8數碼問題 先給個例題: Description 八 方塊移動遊戲要求從一個含8個數字(用1-8表示)的方塊以及一個空格方塊(用0表示)的3×3矩陣的起始狀態開始,不斷移動該空格方塊以使其和
1326 Problem A 演算法2-8~2-11:連結串列的基本操作
問題 A: 演算法2-8~2-11:連結串列的基本操作 時間限制: 1 Sec 記憶體限制: 32 MB 提交: 136 解決: 68 [提交][狀態][討論版][命題人:外部匯入] 題目描述 連結串列是
A演算法與A*演算法區別
A演算法由 f(n)=g(n)+h(n)f(n)=g(n)+h(n) 倆個因素決定, g(n)g(n) 是這一步的代價函式, h(n)h(n) 是這一步的預估函式; 對於A*演算法來說,評判函式也是 f(n)=g∗(n)+h∗(n)f(n)=g∗(n)+
A*演算法解決15數碼問題_Python實現
1問題描述 數碼問題常被用來演示如何在狀態空間中生成動作序列。一個典型的例子是15數碼問題,它是由放在一個4×4的16宮格棋盤中的15個數碼(1-15)構成,棋盤中的一個單元是空的,它的鄰接單元中的數碼可以移到該單元中,通過這樣不斷地移動數碼來改變棋盤佈局,使棋盤從給定的初始棋局變為目標棋局(圖1)。【數字
A*演算法與matlab實現
A*演算法是一種尋路演算法,常常被用在遊戲智慧ai的自動尋路過程等等,它較之於圖論的最短路演算法而言,更加適用於節點巨大的情況下,但是該演算法是一種啟發式搜尋演算法,並不能保證總是找到最優路徑。 A*的演算法思想如下: 定義兩個函式:f和g,其中f揭示當前點到出發點的距離
A*演算法解決八數碼問題(C++版本)
八數碼問題定義: 八數碼問題也稱為九宮問題。在3×3的棋盤,擺有八個棋子,每個棋子上標有1至8的某一數字,不同棋子上標的數字不相同。棋盤上還有一個空格,與空格相鄰的棋子可以移到空格中。要求解決的問題是:給出一個初始狀態和一個目標狀態,找出一種從初始轉變成
求圖的第K短路(A*演算法與最短路的應用)
前言:最短路演算法是我們非常熟悉的了。Dijkstra,SPFA等單源最短路演算法是我們在競賽中常用的演算法。那麼,假如題目要求的不是最短的呢? Question(1):給定一個圖,求次短路。 對於次短路,很容易想到在比較的時候進行處理。 設dis(u)為原點s到u的最短路
A*演算法解決八數碼問題
1 問題描述 1.1什麼是八數碼問題 八數碼遊戲包括一個33的棋盤,棋盤上擺放著8個數字的棋子,留下一個空位。與空位相鄰的棋子可以滑動到空位中。遊戲的目的是要達到一個特定的目標狀態。標註的形式化如下: 1 2 3 4 5 6 7 8 1.2
A*演算法解決八數碼問題的C++實現
近來看了看人工智慧中的A*演算法,並將其用C++實現了一下。 其它的關於A*演算法的原理,網上有很多的,在這裡我就不提供了。 這裡的實現比遊戲中的應用略微複雜一點。 下面的程式碼雖然是解決八數碼問題的,但是其具體的實現步驟、思想可以通用於解決其它的問題。 /****
8數碼問題的A星演算法
在人工智慧課程中學習了啟發式A*演算法。該演算法都夠成功地解決8/15數碼問題。選擇的啟發式函式可以是當前狀態和目標狀態之間的城市距離,也可以是當前狀態不在目標位置的數字的和(事實證明,該啟發式函式很差)。 有興趣的同學可以從我的資源中下載原始碼(沒有文件)。
九章演算法筆記 8.雜湊表與堆 Hash & Heap
大綱 cs3k.com 資料結構概述 雜湊表 Hash: a.原理 b.應用 堆 Heap: a.原理 b.應用-優先佇列 Priority Queue c.替代品-TreeMap 資料結構的兩類問題 cs3k
python資料結構與演算法(8)
連結串列與順序表的對⽐連結串列失去了順序表隨機讀取的優點,同時連結串列由於增加了結點的指標域,空 間開銷⽐較⼤,但對儲存空間的使⽤要相對靈活。連結串列與順序表的各種操作複雜度如下所示:注意雖然表⾯看起來複雜度都是 O(n),但是連結串列和順序表在插⼊和刪除時進 ⾏的是完全不同的操作。連結串列的主要耗時操作是遍
hdu1043 A*演算法 八數碼問題
題意大致就是讓你復原一個八數碼拼圖,輸出具體路徑 思路:這裡使用的是A*演算法,A*演算法可能比較陌生,迪傑斯克拉法求最短路相比大家都比較熟悉,A*演算法就是他的進化,或者說dij演算法是A*演算法中把預估函式看為0得到的演算法, A*演算法的主體是一個路徑計算函式:f=g+h,其中g表
IDA* 迭代加深A star演算法解決15數碼問題——python實現
1 IDA* Algorithm 1.1 Description Iterative deepening A* (IDA*) was first described by Richard Korf in 1985, which is a graph traversal
N數碼問題的啟發式搜尋演算法--A*演算法python實現
一、啟發式搜尋:A演算法 1)評價函式的一般形式 : f(n) = g(n) + h(n) g(n):從S0到Sn的實際代價(搜尋的橫向因子) h(n):從N到目標節點的估計代價,稱為啟發函式(搜尋的縱向因子); 特點: 效率高, 無回溯, 搜尋演算法 OPEN表 : 存放待擴充套件的節點. CLOS
第k短路 演算法詳解(圖解)與模板(A* 演算法)
老規矩,先放模板,有時間放圖解 #include <map> #include <queue> #include <cstdlib> #include <cma
魔方陣:3階、4階、8階演算法與實現
魔方陣:一個N階魔方陣即N行N列的陣列,其每一行的和==每一列的和==對角線的和。 例如一個3階魔方陣: 8 1 6 3 5 7 4 9 2 一:3階魔方陣 這裡我們用兩種方法實現: 方法一:遍歷三階陣列的所有條件,
八數碼問題的A*演算法實現
問題描述 八數碼問題也稱為九宮問題。在3×3的棋盤,擺有八個棋子,每個棋子上標有1至8的某一數字,不同棋子上標的數字不相同。棋盤上還有一個空格,與空格相鄰的棋子可以移到空格中。要求解決的問題是:給出一個初始狀態和一個目標狀態,找出一種從初始轉變成目標狀態
hdu 1034 & poj 1077 Eight 傳說中的八數碼問題。真是一道神題,A*演算法+康託展開
Eight Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 13506 Accepted Submiss
八數碼問題的A*演算法求解
A*演算法是啟發式搜素演算法中較為出名和高效的演算法之一,其關鍵是對於啟發式函式的實際,啟發式函式h(x)需要儘可能的接近實際的h(x)∗h(x)∗。下面是人工智慧八數碼問題使用A*演算法求解的原始碼放在部落格上記錄一下。程式使用放錯位置的棋子的個數作為啟發