1. 程式人生 > >MATLAB—A*解決八數碼問題

MATLAB—A*解決八數碼問題

一、實驗目的

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

四、執行結果


五、實驗分析

首先從上向下擴充套件節點

結果從下向上輸出,依次查詢其父節點。