1. 程式人生 > >啟發式搜尋演算法求解八數碼問題(C)

啟發式搜尋演算法求解八數碼問題(C)


下午看一個遊戲的演算法時看了一下啟發式搜尋演算法,心血來潮跑了一遍很久很久以前寫八數碼的程式(C語言),發現各種問題,後來順著思路整理了一下,貼出來和大家分享一下,直接上程式碼:

//
//  main.c
//  yunsuan
//
//  Created by mac on 12-8-7.
//  Copyright 2012年 __MyCompanyName__. All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 3 //數碼組大小
#define Max_Step 50 //最大搜索深度
#define MAX 50

typedef struct node//八數碼結構體
{
    int form[N][N];//數碼組
    int evalue;//評估值
    int udirect;//所遮蔽方向,防止往回推到上已狀態,1上2下3左4右
    struct node *parent;//父節點
}Graph;

Graph *Qu[MAX];//佇列
Graph *St[MAX];//堆疊

/////////列印數碼組
void Print(Graph *The_graph)
{
    int i,j;
    if(The_graph==NULL)
        printf("圖為空\n");
    else
    {
        printf("---------------------\n");
        for(i=0;i<N;i++)
        {
            printf("|\t");
            for(j=0;j<N;j++)
            {
                printf("%d\t",The_graph->form[i][j]);//遍歷列印
            }
            printf("\t|\n");
        }
        printf("|\t\t\t差距:%d\t|\n",The_graph->evalue);//差距顯示
        printf("---------------------\n");
    }
}

/////////評價函式
int Evaluate(Graph *The_graph,Graph *End_graph)
{
    int valute=0;//差距數
    int i,j;
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
        {
            if(The_graph->form[i][j]!=End_graph->form[i][j])
            {
                valute++;
            }
        }
    }
    The_graph->evalue=valute;
    return valute;
}

/////////移動數碼組
Graph *Move(Graph *The_graph,int Direct,int CreatNew_graph)
{
    Graph *New_graph;//
    int HasGetBlank=0;//是否獲取空格位置
    int AbleMove=1;//是否可移動
    int i,j,t_i,t_j,x,y;
    
    for(i=0;i<N;i++)//獲取空格座標i,j
    {
        for(j=0;j<N;j++)
        {
            if(The_graph->form[i][j]==0)
            {
                HasGetBlank=1;
                break;
            }
        }
        if(HasGetBlank==1)
            break;
    }
    //printf("空格位置:%d,%d\n",i,j);
    
    t_i=i;
    t_j=j;
    
    //移動空格
    switch(Direct)
    {
        case 1://上
            t_i--;
            if(t_i<0)
                AbleMove=0;
            break;
        case 2://下
            t_i++;
            if(t_i>=N)
                AbleMove=0;
            break;
        case 3://左
            t_j--;
            if(t_j<0)
                AbleMove=0;
            break;
        case 4://右
            t_j++;
            if(t_j>=N)
                AbleMove=0;
            break;
            
    }
    
    if(AbleMove==0)//不能移動則返回原節點
    {
        return The_graph;
    }
    if(CreatNew_graph==1)
    {
        New_graph=(Graph *)malloc(sizeof(Graph));//生成節點
        for(x=0;x<N;x++)
        {
            for(y=0;y<N;y++)
            {
                New_graph->form[x][y]=The_graph->form[x][y];//複製數碼組
            }

        }
    }
    else
    {
        New_graph=The_graph;
    }
    //移動後
    New_graph->form[i][j]=New_graph->form[t_i][t_j];
    New_graph->form[t_i][t_j]=0;
    //printf("移動產生的新圖:\n");
    //Print(New_graph);
    return New_graph;
}

/////////搜尋函式
Graph *Search(Graph *Begin,Graph *End)
{
    Graph *g1,*g2,*g;
    int Step=0;//深度
    int Direct=0;//方向
    int i;
    int front,rear;
    front=rear=-1;//佇列初始化
    g=NULL;
    rear++;//入隊
    Qu[rear]=Begin;
    while(rear!=front)//佇列不空
    {
        front++;//出隊
        g1=Qu[front];
        //printf("開始第%d個圖:\n",front);
        //Print(g1);
        for(i=1;i<=4;i++)//分別從四個方向推匯出新子節點
        {
            Direct=i;
            if(Direct==g1->udirect)//跳過遮蔽方向
                continue;
            g2=Move(g1, Direct, 1);//移動數碼組
            
            if(g2!=g1)//數碼組是否可以移動
            {
                //可以移動
                Evaluate(g2, End);//評價新的節點
                //printf("開始產生的第%d個圖:\n",i);
                //Print(g2);
                if(g2->evalue<=g1->evalue+1)
                {
                    //是優越節點
                    g2->parent=g1;
                    //移動空格
                    switch(Direct)//設定遮蔽方向,防止往回推
                    {
                        case 1://上
                            g2->udirect=2;
                            break;
                        case 2://下
                            g2->udirect=1;
                            break;
                        case 3://左
                            g2->udirect=4;
                            break;
                        case 4://右
                            g2->udirect=3;
                            break;
                            
                    }
                    rear++;
                    Qu[rear]=g2;//儲存節點到待處理佇列
                    if(g2->evalue==0)//為0則搜尋完成
                    {
                        g=g2;
                        //i=5;
                        break;
                    }
                }
                else
                {
                    free(g2);//拋棄劣質節點
                    g2=NULL;
                }
            }
            
        }
        
        if(g!=NULL)//為0則搜尋完成
        {
            if(g->evalue==0)
            {
                break;
            }
        }
        
        Step++;//統計深度
        if(Step>Max_Step)
        {
            break;
        }
    }
    return g;
}

/////////初始化一個八數碼結構體
Graph *CR_BeginGraph(Graph *The_graph)
{
    srand((unsigned)time(0));
    int M=10;//隨機移動步數
    int Direct;
    int i,x,y;
    Graph *New_graph;
    New_graph=(Graph *)malloc(sizeof(Graph));
    for(x=0;x<N;x++)
    {
        for(y=0;y<N;y++)
        {
            New_graph->form[x][y]=The_graph->form[x][y];
        }
    }
    for(i=0;i<M;i++)
    {
        Direct=rand()%4+1;//產生1-4隨機數
        //printf("Direct:%d\n",Direct);
        New_graph=Move(New_graph, Direct, 0);
        //Print(New_graph);
    }
    
    New_graph->evalue=0;
    New_graph->udirect=0;
    New_graph->parent=NULL;
    
    return New_graph;
}

int main (int argc, const char * argv[])
{
    
    // insert code here...
    /*
    Graph Begin_graph={
        {{2,8,3},{1,6,4},{0,7,5}},0,0,NULL
    };
    
    Graph Begin_graph={
        {{2,8,3},{1,0,4},{7,6,5}},0,0,NULL
    };
    
    
    Graph Begin_graph={
        {{2,0,1},{4,6,5},{3,7,8}},0,0,NULL
    };
    */
    
    //目標數碼組
    Graph End_graph={
        {{1,2,3},{8,0,4},{7,6,5}},0,0,NULL
    };
    
    //初始數碼組
    Graph *Begin_graph;
    Begin_graph=CR_BeginGraph(&End_graph);//隨機產生初始數碼組
    Evaluate(Begin_graph, &End_graph);//對初始的數碼組評價
    printf("初始數碼組:\n");
    Print(Begin_graph);
    printf("目標數碼組:\n");
    Print(&End_graph);
    
    Graph *G,*P;
    int top=-1;
    
    //圖搜尋
    G=Search(Begin_graph, &End_graph);
    //列印
    if(G)
    {
        //把路徑倒序
        P=G;
        //壓棧
        while(P!=NULL)
        {
            top++;
            St[top]=P;
            P=P->parent;
        }
        printf("<<<<<<<<<<<<<<<搜尋結果>>>>>>>>>>>>>>>>\n");
        //彈棧列印
        while(top>-1)
        {
            P=St[top];
            top--;
            Print(P);
        }
        printf("<<<<<<<<<<<<<<<<<完成>>>>>>>>>>>>>>>>>>\n");
    }
    else
    {
        printf("搜尋不到結果,深度為%d\n",Max_Step);
        //設計搜尋深度範圍主要是防止佇列記憶體越界
    }

    
    return 0;
}