启发式算法解决八数码(九宫)问题

人工智能课程实验


  背景知识:


估价函数

在对问题的状态空间进行搜索时,为提高搜索效率需要和被解问题的解有关的大量控制性知识作为搜索的辅助性策略。这些控制信息反映在估价函数中。

估价函数的任务就是估计待搜索节点的重要程度,给这些节点排定次序。估价函数可以是任意一种函数,如有的定义它是节点x处于最佳路径的概率上,或是x节点和目标节点之间的距离等等。在此,我们把估价函数f(n)定义为从初始节点经过n节点到达目标节点的最小代价路径的代价估计值,它的一般形式是:

      f(n) = g(n) + h(n)

其中g(n)是从初始节点到节点n的实际代价,g(n)可以根据生成的搜索树实际计算出来;h(n)是从n到目标节点的最佳路径的代价估计,h(n)主要体现了搜索的启发信息。

1)  状态表示的数据结构

struct  Node

{

    stringfather;

    stringid;

    intf,g=0,h;

    intvalue[3][3];

};

value为3X3的九宫格,表示为当前状态,f,g,h分别对应估价函数中的f(n)= g(n) + h(n),id为当前状态的唯一表示,即九宫格数字的顺序排序,空格用‘0’来表示,则实例中的第一个状态的id即为‘283164705’,father为父状态的id。

2)  状态扩展规则的表示

从某一状态出发,寻找空格的位置,探寻空格的上下左右四个方向可移动性,并避免走回头路,计算下一节点的估价函数值。

int calcuH(int *value)

{

    int h=0;

    for(int i= 0; i < 9; i++)

    {

        intnum=value[i]-1;

       if(num==-1)continue;

        intnowx=i%3,nowy=i/3;

        intgoalx=num%3,goaly=num/3;

       h+=abs(nowx-goalx)+abs(nowy-goaly);   

    }

    returnh;}

估价函数为所有数字当前的位置和目标位置的水平距离和垂直距离的绝对值的和。

3)  搜索产生的状态空间图


4)  OPEN表和CLOSE表变化过程

从open表取出最后一个状态,进行状态搜索,将新搜索到的点加入open表,后将该状态加入close表,最后重新排序open表。

#include "iostream"
#include <vector>
#include <algorithm>
#include <stack>
#include <sstream>
#include <stdlib.h>

using namespace std;

struct  Node
{
    string father;
    string id;
    int f,g=0,h;
    int value[3][3];
};
string getID(int *value)
{
    stringstream ss;
    for(int i = 0; i < 9; i++)
    {
        ss<<value[i];
    }
    return ss.str();
}

int calcuH(int *value)
{
    int h=0;
    for(int i = 0; i < 9; i++)
    {
        int num=value[i]-1;
        if(num==-1)continue;
        int nowx=i%3,nowy=i/3;
        int goalx=num%3,goaly=num/3;
        h+=abs(nowx-goalx)+abs(nowy-goaly);    
    }
    return h;
}

bool operator == (const Node & obj1,const Node & obj2)
{
    return obj1.id== obj2.id;
}
bool cmpH (Node i,Node j) { return (i.h>j.h); }  
vector<Node> close;
vector<Node> open;
static int oldH=999999;
Node bestResult;
int moveMat[9][4]={
    {0,1,1,0},
    {0,1,1,1},
    {0,0,1,1},
    {1,1,1,0},
    {1,1,1,1},
    {1,0,1,1},
    {1,1,0,0},
    {1,1,0,1},
    {1,0,0,1}
};//move mat ,list as up right down left

void outPrint( Node result )
{
    vector<Node>::iterator iter;
    stack<Node> outStack;
    outStack.push(result);
    int time=-1;
    while(result.father!="start")
    {
        result.id=result.father;
        iter = find(close.begin(), close.end(), result);
        if(iter != close.end())
        {
            result=*iter;
            outStack.push(result);
        }
        else
        {
            cout<<"program error in outprint"<<endl;
            break;  
        }       
    }
    Node oneRow[5];
    int p=0,changeRow=5;
    while(!outStack.empty())
    {       
        oneRow[p++]=outStack.top();
        outStack.pop();
        time++;
        
        if(p==changeRow)
        {
            p=0;
            for(int row = 0; row < 3; row++)
            {
                for(int col = 0; col < changeRow; col++)
                {
                    cout<< oneRow[col].value[row][0] <<oneRow[col].value[row][1]<<oneRow[col].value[row][2];                    
                    if(row==1)
                        cout<<" -> ";
                    else
                        cout<<"    ";                    
                }
                cout<<endl;                
            }
            cout<<"\n---------"<<time<<"----------"<<endl;          
        }
    }
    for(int row = 0; row < 3; row++)
    {
        for(int col = 0; col < p; col++)
        {
            cout<< oneRow[col].value[row][0] <<oneRow[col].value[row][1]<<oneRow[col].value[row][2];                    
            if(row==1)
                cout<<" -> ";
            else
                cout<<"    ";                    
        }
        cout<<endl;                
    }
    cout<<"\n---------"<<time<<"----------"<<endl;   

    cout<<"This result sum step:   "<<time<<endl;
    oldH=999999;
    system("pause");
}
void finalOutPrint()
{
    if(bestResult.father!="NO")
    {
        cout<<"----This is Finally best result"<<endl;
        outPrint(bestResult);
        cout<<"----This is Finally best result"<<endl;
        cout<<"Search all node finish"<<endl;
    }
    else
        cout<<"No result,is error"<<endl;
}
void move(Node node,int blockSite,int direction)
{
    int moveSite,oldH=node.h,*pvalue=(int*)node.value;
    switch (direction)
    {
    case  0:
        moveSite=blockSite-3;
        break;
    case  1:
        moveSite=blockSite+1;
        break;
    case  2:
        moveSite=blockSite+3;
        break;
    case  3:
        moveSite=blockSite-1;
        break;
    default:
        cout<<"error!!"<<endl;
        exit(1);
        break;
    }

    {
        int temp;
        temp=pvalue[blockSite];
        pvalue[blockSite]=pvalue[moveSite];
        pvalue[moveSite]=temp;
    }
    node.father=node.id;   
    node.id=getID((int*)node.value);
    node.g++;
    if(node.id=="123456780")
    {
        outPrint(node);
        if(node.g<bestResult.g)
        {
            bestResult.father=node.father;
            bestResult.g=node.g;
        }
        
    }
    else 
    {
        node.h=calcuH((int*)node.value);
        node.f=node.g+node.h;

        vector<Node>::iterator iter;
        iter = find(close.begin(), close.end(), node);
        if(iter != close.end())
        {
            if(iter->g>node.g)
            {
                iter->father=node.father;
                iter->g=node.g;
                iter->f=node.f;
            }           
        }
        else 
        {
            iter = find(open.begin(), open.end(), node);
            if(iter != open.end())
            {
                if(iter->g>node.g)
                {
                    iter->father=node.father;
                    iter->g=node.g;
                    iter->f=node.f;
                }    
            }
            else                         //if not in close or open ,add new
            {
                open.push_back(node);
            }
        }
    }

}

int search()
{
    int loop=0;
    int staytime=0;
    while(!open.empty())
   {
        
        sort(open.begin(),open.end(),cmpH);

        Node nowNode=open.back();
        int *pvalue=(int*)nowNode.value;
        {
            if(nowNode.h<oldH)
            {
                staytime=0;
                oldH=nowNode.h;
            }
            else 
            {
                staytime++;
            }
            if(staytime==3000)
            {
                break;
            }            
        }

        open.pop_back();
        close.push_back(nowNode);
        if(nowNode.id=="123456780")
        {
            outPrint(nowNode);
            continue;
        }
        int blockSite;              //get empty situation
        for(int i = 0; i < 9; i++)
        {
            if(pvalue[i]==0)
            {
                blockSite=i;
                break;
            }
        }

        {                           //check direction
            if(moveMat[blockSite][0]==1)
            {
                move(nowNode,blockSite,0);
            }
            if(moveMat[blockSite][1]==1)
            {
                move(nowNode,blockSite,1);
            }
            if(moveMat[blockSite][2]==1)
            {
                move(nowNode,blockSite,2);
            }
            if(moveMat[blockSite][3]==1)
            {
                move(nowNode,blockSite,3);
            }
        }

    }
    finalOutPrint();    
    return 0;
}

int main(int argc, char const *argv[])
{
    Node head;
    head.father="start";
    bestResult.father="NO";
    bestResult.g=99999;
    int aim[]={1,2,3,4,5,6,7,8,0};\
    memcpy(bestResult.value,aim,9*sizeof(int));
    
    int start[]=
    {
        4,1,2,
        7,5,3,
        0,8,6  
    };   
    int inversion=0;
    for(int i = 0; i < 9; i++)
    {
        if(start[i]!=0)
        for(int j = i+1; j < 9; j++)
        {

            if(start[j]!=0&&start[i]>start[j])
            {
                inversion++;
            }   
        }       
    }
    //cout<<inversion<<endl;
    if(inversion%2!=0)
        cout<<"this series is unsolvable"<<endl;
    else 
    {
        memcpy(head.value,start,9*sizeof(int));
        head.id=getID((int*)head.value);
        head.h=calcuH((int*)head.value);
        open.push_back(head);
        search();
    }
    return 0;
}

  • 3
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
八数码问题是指在3x3的九宫格中,给定八个数字和一个空格,要求将数字移动,最终达到目标状态。其中,每个数字可以向上、下、左、右四个方向移动,但不能跨越其他数字,也不能移动到九宫格外。 A*算法是一种启发式搜索算法,它通过估计到达目标的成本来决定哪些节点应该先被扩展。在八数码问题中,我们可以用曼哈顿距离来作为启发函数,即当前状态与目标状态之间所有数字的曼哈顿距离之和。曼哈顿距离指的是两个点在网格中沿着网格线走的距离之和。 具体实现步骤如下: 1. 定义状态表示:使用一个3x3的矩阵来表示当前状态,其中0表示空格。 2. 定义状态扩展:对于一个状态,枚举空格可以移动的四个方向,将空格与相邻位置的数交换,生成新的状态。 3. 定义启发函数:计算当前状态与目标状态之间所有数字的曼哈顿距离之和。 4. 定义状态比较:比较两个状态是否相同。 5. 定义搜索过程:使用A*算法进行搜索,将状态按照启发函数的值从小到大排序,每次扩展启发函数值最小的状态,并将它的后继状态加入到搜索队列中。如果搜索到目标状态,搜索结束。 6. 输出解路径:在搜索过程中,记录每个状态的父状态,最终从目标状态开始回溯到初始状态,得到解路径。 具体实现过程可以参考以下Python代码: ```python import heapq class Puzzle: GOAL = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] def __init__(self, state): self.state = state self.f = self.g = self.h = 0 self.parent = None def __lt__(self, other): return self.f < other.f def __eq__(self, other): return self.state == other.state def __hash__(self): return hash(str(self.state)) def is_goal(self): return self.state == Puzzle.GOAL def get_neighbors(self): neighbors = [] row, col = self.find(0) for dr, dc in [(1, 0), (0, 1), (-1, 0), (0, -1)]: nr, nc = row + dr, col + dc if 0 <= nr < 3 and 0 <= nc < 3: neighbor = self.clone() neighbor.swap(row, col, nr, nc) neighbors.append(neighbor) return neighbors def find(self, value): for i in range(3): for j in range(3): if self.state[i][j] == value: return i, j return None def swap(self, r1, c1, r2, c2): self.state[r1][c1], self.state[r2][c2] = self.state[r2][c2], self.state[r1][c1] def clone(self): clone = Puzzle([row[:] for row in self.state]) clone.g = self.g + 1 clone.h = clone.manhattan_distance() clone.f = clone.g + clone.h clone.parent = self return clone def manhattan_distance(self): distance = 0 for i in range(3): for j in range(3): if self.state[i][j] != 0: r, c = self.find(self.state[i][j]) distance += abs(i - r) + abs(j - c) return distance def print_path(self): path = [] node = self while node is not None: path.append(node) node = node.parent path.reverse() for node in path: print(node) def solve(start): open_set = [start] closed_set = set() while open_set: current = heapq.heappop(open_set) if current.is_goal(): return current.print_path() closed_set.add(current) for neighbor in current.get_neighbors(): if neighbor in closed_set: continue if neighbor not in open_set: heapq.heappush(open_set, neighbor) else: existing = open_set[open_set.index(neighbor)] if neighbor.g < existing.g: existing.g = neighbor.g existing.parent = neighbor.parent existing.f = existing.g + existing.h if __name__ == '__main__': start = Puzzle([[1, 2, 0], [4, 5, 3], [7, 8, 6]]) start.h = start.manhattan_distance() start.f = start.g + start.h solve(start) ``` 在这个实现中,我们使用了一个优先队列来保存待扩展的状态,每次从中选择f值最小的状态进行扩展。对于已经搜索过的状态,我们使用一个集合来保存,避免重复搜索。在搜索结束后,我们通过回溯记录的父状态,输出解路径。 这个算法的时间复杂度为O(b^d),其中b是每个状态的平均分支因子,d是目标状态的深度。在实践中,A*算法通常能够在合理的时间内找到解,但在某些情况下可能会陷入无限循环或者搜索空间过大导致运行时间超过预期。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值