MATLAB—A*解決八數碼問題
阿新 • • 發佈:2019-01-11
一、實驗目的
1、 熟悉和掌握啟發式搜尋的定義、估價函式和演算法過程。
2、 利用A*演算法求解N數碼難題,理解求解流程和搜尋順序。
二、實驗內容
以八數碼為例實現A或A*演算法 。
1、分析演算法中的OPEN表和CLOSE表的生成過程。
2、分析估價函式對搜尋演算法的影響。
3、分析啟發式搜尋演算法的特點。
三、程式實現
up8.m
function B=up8(a) %0向上移動 [x,y]=find(a==0); if x==1 %如果0在矩陣的最上一層,則B=-1 B=-1; return end B=a; %否則交換兩個位置的值 B(x,y)=a(x-1,y); B(x-1,y)=0; return
down8.m
function B=down8(a) %0向下移動
[x,y]=find(a==0);
if x==3 %如果0在矩陣的最下一層,則B=-1
B=-1;
return
end
B=a; %否則交換兩個位置的值
B(x,y)=a(x+1,y);
B(x+1,y)=0;
return
left8.m
function B=left8(a) %0向左移動 [x,y]=find(a==0); if y==1 %如果0在矩陣的最左一層,則B=-1 B=-1; return end B=a; %否則交換兩個位置的值 B(x,y)=a(x,y-1); B(x,y-1)=0; return
right8.m
function B=right8(a) %0向右移動
[x,y]=find(a==0);
if y==3 %如果0在矩陣的最右一層,則B=-1
B=-1;
return
end
B=a; %否則交換兩個位置的值
B(x,y)=a(x,y+1);
B(x,y+1)=0;
return
h1.m
function val=h1(A,T) [i,j]=find(A==0); B=A-T; index=find(B~=0); %找出A中不在位的個數,即B中非0的個數 [k,l]=find(T==0); if i==k&&j==l %除去目標矩陣中為0的位置,因為0不在位不計入 val=length(index); return; end val=length(index)-1;
chushi8.m
function OPEN8=chushi8(A,T)
OPEN8=cell(1);
OPEN8{1,1}.g=0;
OPEN8{1,1}.h=h1(A,T);
OPEN8{1,1}.f=OPEN8{1,1}.g+OPEN8{1,1}.h;
OPEN8{1,1}.S=A; %當前節點的狀態
OPEN8{1,1}.fa=[]; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態
tuozhan8.m
function [OPEN8,g,tail]=tuozhan8(g,c,tail,T,OPEN8,CLOSED8)
flag=0; %設定是否可擴充套件的標誌位
flag2=0;
if up8(OPEN8{1,1}.S)~=-1 %判斷是否可以向某個方向移動
for i=1:c-1 %判斷是否在CLOSED8表中,如果在,則不能進行擴充套件
if up8(OPEN8{1,1}.S)==CLOSED8{1,i}.S
flag=1;
break;
end
end
for j=1:tail %判斷是否在OPEN8表中,如果在,則不能進行擴充套件
if up8(OPEN8{1,1}.S)==OPEN8{1,j}.S
flag2=1;
break;
end
end
if flag~=1&&flag2~=1
tail=tail+1;
OPEN8{1,tail}.g=g;
B=up8(OPEN8{1,1}.S);
OPEN8{1,tail}.h=h1(B,T);
OPEN8{1,tail}.f=OPEN8{1,tail}.g+OPEN8{1,tail}.h;
OPEN8{1,tail}.S=B; %當前節點的狀態
OPEN8{1,tail}.fa=OPEN8{1,1}.S; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態
end
end
if down8(OPEN8{1,1}.S)~=-1
for i=1:c-1
if down8(OPEN8{1,1}.S)==CLOSED8{1,i}.S
flag=2;
break;
end
end
for j=1:tail %判斷是否在OPEN8表中,如果在,則不能進行擴充套件
if down8(OPEN8{1,1}.S)==OPEN8{1,j}.S
flag2=2;
break;
end
end
if flag~=2&&flag2~=2
tail=tail+1;
OPEN8{1,tail}.g=g;
B=down8(OPEN8{1,1}.S);
OPEN8{1,tail}.h=h1(B,T);
OPEN8{1,tail}.f=OPEN8{1,tail}.g+OPEN8{1,tail}.h;
OPEN8{1,tail}.S=B; %當前節點的狀態
OPEN8{1,tail}.fa=OPEN8{1,1}.S; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態
end
end
if left8(OPEN8{1,1}.S)~=-1
for i=1:c-1
if left8(OPEN8{1,1}.S)==CLOSED8{1,i}.S
flag=4;
break;
end
end
for j=1:tail %判斷是否在OPEN8表中,如果在,則不能進行擴充套件
if left8(OPEN8{1,1}.S)==OPEN8{1,j}.S
flag2=4;
break;
end
end
if flag~=4&&flag2~=4
tail=tail+1;
OPEN8{1,tail}.g=g;
B=left8(OPEN8{1,1}.S);
OPEN8{1,tail}.h=h1(B,T);
OPEN8{1,tail}.f=OPEN8{1,tail}.g+OPEN8{1,tail}.h;
OPEN8{1,tail}.S=B; %當前節點的狀態
OPEN8{1,tail}.fa=OPEN8{1,1}.S; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態
end
end
if right8(OPEN8{1,1}.S)~=-1
for i=1:c-1
if right8(OPEN8{1,1}.S)==CLOSED8{1,i}.S
flag=3;
break;
end
end
for j=1:tail %判斷是否在OPEN8表中,如果在,則不能進行擴充套件
if right8(OPEN8{1,1}.S)==OPEN8{1,j}.S
flag2=3;
break;
end
end
if flag~=3&&flag2~=3
tail=tail+1;
OPEN8{1,tail}.g=g;
B=right8(OPEN8{1,1}.S);
OPEN8{1,tail}.h=h1(B,T);
OPEN8{1,tail}.f=OPEN8{1,tail}.g+OPEN8{1,tail}.h;
OPEN8{1,tail}.S=B; %當前節點的狀態
OPEN8{1,tail}.fa=OPEN8{1,1}.S; %父節點的狀態,這樣的保留可以從找到的目標狀態追蹤到初始狀態
end
end
yunxing.m
%A=[2 8 3;1 6 4;7 0 5]
%T=[1 2 3;8 0 4;7 6 5]
%A=[7 5 3;1 6 4;2 8 0]
%T=[1 2 3;8 0 4;7 6 5]
%A=[1 2 3;7 8 4;0 6 5]
%T=[1 2 3;8 0 4;7 6 5]
%A=[2 3 0;1 5 6;8 4 7]
%T=[1 2 3;4 5 6;7 8 0] %執行時要修改h1函式中的if語句,因為0的位置不計入不在位個數
function [CLOSED8]= yunxing(A,T)
OPEN8=chushi8(A,T); %初始化OPEN8表,即將待擴充套件的第一個節點放入
c=1; %CLOSED8表中的指標
g=1;
tail=1;
CLOSED8=cell(1);
k=1; %設定擴充套件了多少個的標誌
while 1
if h1(OPEN8{1,1}.S,T)==0&&k<=1000 %當找到最終的目標時則輸出,並跳出while迴圈
disp(OPEN8{1,1}.S);
break;
end
if k>=1000 %如果擴充套件了的節點個數大於某個數則跳出並顯示
disp(k);
break;
end
[OPEN8,g,tail]=tuozhan8(g,c,tail,T,OPEN8,CLOSED8); %對OPEN8表中的第一個點擴充套件
CLOSED8{1,c}=OPEN8{1,1}; %將已擴充套件的節點放入CLOSED8表中
c=c+1;
b=10000;
index=1;
for i=2:tail %找出OPEN8表中f值最小的元素
if OPEN8{1,i}.f<=b
b=OPEN8{1,i}.f;
g=OPEN8{1,i}.g;
index=i;
end
end
g=g+1;
OPEN8{1,1}=OPEN8{1,index}; %將OPEN8表中的最小f的元素給OPEN8{1,1}
OPEN8{1,index}=OPEN8{1,tail}; %將OPEN表尾元素賦給最小f值的位置
tail=tail-1;
k=k+1;
end
%輸出矩陣
d=length(CLOSED8);
i=g-1; %i表示輸出的個數
t=CLOSED8{1,d}; %t為CLOSED表裡最後一個元素
while i>0
disp(t.S); %輸出此節點
t=t.fa; %令其等於其父節點
if isempty(t) %判斷其父節點是否為空,為空則結束
break;
end
for j=1:length(CLOSED8) %判斷其父節點在CLOSED表中的位置
if CLOSED8{1,j}.S==t
t=CLOSED8{1,j}; %如果有的話,將其位置賦給t
break;
end
end
i=i-1;
end
四、執行結果
五、實驗分析
首先從上向下擴充套件節點
結果從下向上輸出,依次查詢其父節點。