广度优先搜索解决八数码问题

八数码简介

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字,不同棋子上标的数字不同样。棋盘上另一个空格,与空格相邻的棋子能够移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

求解八数码问题要懂得的知识

1.康托展开,八数码在交换的过程中状态会改变,康托展开用于求出某一格局的状态数。唐托展开公式:X=a[n](n-1)!+a[n-1](n-2)!+...+a[i](i-1)!+...+a[2]1!+a[1]*0!其中a[i]为当前未出现的元素中是排在第几个(从0开始),并且0<=a[i]<i(1<=i<=n)

2.逆序数,通过求初始格局和目标格局逆序数,然后在比较两者的逆序数的奇偶性是否相同,如果奇偶性相同,则可以从初始格局变到目标格局。否则,不可达。逆序数求法:假设有n个元素的排列,a[i]为排列中的元素(0<=i<n),求在排列中比a[i]小的数的和。比如有这么一个排列:54120,5后面比它小的数有3个(0不算,后同);4后面比它小的数有2个;1后面比它小的数有0个;2后面比它的小的数有0个;所以,该排列的逆序数=3+2+0+0=5.

3.在八数码中0位置的数与它相邻的上下左右的位置的数交换不会影响这个格局的逆序数的奇偶性。比如有以下格局:

4 6 7
5 8 1
2 3 0

如果2这个数和5这个数交换就会导致格局的逆序数的奇偶性的变化,而如果0和1交换就不会导致奇偶性的变化。我们要保证移动前和移动后逆序数的奇偶性不改变,用一维数组来存储格局,要注意索引。

广度优先搜索求解八数码问题代码

#include <iostream>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
using namespace std;
#define N 9
int jc[N+1]={1,1,2,6,24,120,720,5040,40320,362880};//0-9的阶乘
typedef struct data
{
    int arr[N];//格局
    int hash;//存储某一格局的哈希
    int pos;//0当前位置
    int step;//记录步数
}Node;

int dir[4][2]={
    {0,1},
    {1,0},
    {0,-1},
    {-1,0}
};

/**
*  康托展开
*/
int cantor(int arr[N])
{
    int i,j;
    int sum=0;
    for( i=0;i<N;i++)
    {
        int nmin=0;
        for(j=i+1;j<N;j++)
        {
            if(arr[i]>arr[j])
                nmin++;
        }
        sum+=(nmin*jc[N-i-1]);

    }

    return sum;
}

/**
*数据交换
*/
void swap(int *arr,int i,int j)
{
    int t=arr[i];
    arr[i]=arr[j];
    arr[j]=t;
}

/**
* 打印数组,测试用
*/
void printArray(int * arr)
{
    int i,j;
    for (i=0;i<N;i++)
    {
        if(i%3==0)
            cout<<"\n";
        cout<<arr[i]<<" ";
    }
    cout<<endl;
}
/**
* 复制数组
*/
void copyArray(int src[N],int target[N])
{
    int i;
    for(i=0;i<N;i++)
    {
        target[i]=src[i];
    }

}

/**
* 广搜
*/
int bfs(int arr[N],int sHash,int tHash)
{
    if(sHash==tHash)
        return 0;
    int i,j;
    queue<Node> q;
    set<int> setHash;
    Node now,next;
    copyArray(arr,now.arr);
    int pos=0;
    for (i=0;i<N;i++)
    {
        if(arr[i]==0)
            break;
        pos++;
    }
    now.hash=sHash;
    now.step=0;
    next.step=0;
    now.pos=pos;
    q.push(now);
    setHash.insert(now.hash);

    while(!q.empty())
    {
        now=q.front();
        q.pop();
        for (i=0;i<4;i++)
        {
            int offsetX=0,offsetY=0;
            offsetX=(now.pos%3+dir[i][0]);
            offsetY=(now.pos/3+dir[i][1]);

            if(offsetX>=0&&offsetX<3&&offsetY<3&&offsetY>=0)
            {
                copyArray(now.arr,next.arr);//每次换方向,就复制
                next.step=now.step;

                next.step++;
                swap(next.arr,now.pos,offsetY*3+offsetX);

                next.hash=cantor(next.arr);
                next.pos=(offsetY*3+offsetX);
                int begin=setHash.size();
                setHash.insert(next.hash);
                int end=setHash.size();

                if(next.hash==tHash){

                    return next.step;
                }

                if(end>begin)
                {

                    q.push(next);
                }

            }
        }

    }
    return -1;
}

/**
*求逆序数
*/
int inversion(int arr[N])
{
    int sum=0;
    for(int i=0;i<N;++i)
    {
        for(int j=i+1;j<N;++j)
        {
            if(arr[j]<arr[i]&&arr[j]!=0)//不与0比较
            {
                sum++;
            }
        }
    }

    return sum;

}
int main(int argc, char **argv)
{
    int i,j;
    string s="123456780";
    string t="123456078";
    int is[N],it[N];//源int数组和目标int数组
    for (i=0;i<9;i++)
    {
        if (s.at(i)>='0'&&s.at(i)<='8')
        {
            is[i]=s.at(i)-'0';
        }else
        {
            is[i]=0;
        }
        if (t.at(i)>='0'&&t.at(i)<='8')
        {
            it[i]=t.at(i)-'0';
        }else
        {
            it[i]=0;
        }
    }
    int sHash,tHash;//源哈希和目标哈希

    sHash=cantor(is);
    tHash=cantor(it);
    int inver1=inversion(is);//求初始格局的逆序数
    int inver2=inversion(it);//求目标格局的逆序数
    if((inver1+inver2)%2==0)
    {
        int step=bfs(is,sHash,tHash);
        cout<<step<<endl;
    }
    else
    {
        cout<<"无法从初始状态到达目标状态"<<endl;
    }

    return 0;
}

原文地址:https://www.cnblogs.com/luoyesiqiu/p/11235238.html

时间: 2024-10-07 14:17:47

广度优先搜索解决八数码问题的相关文章

每天刷个算法题20160526:BFS解决八数码问题(九宫格问题)

版权所有.所有权利保留. 欢迎转载,转载时请注明出处: http://blog.csdn.net/xiaofei_it/article/details/51524864 为了防止思维僵化,每天刷个算法题.已经刷了几天了,现在发点代码. 我已经建了一个开源项目,每天的题目都在里面: https://github.com/Xiaofei-it/Algorithms 绝大部分算法都是我自己写的,没有参考网上通用代码.读者可能会觉得有的代码晦涩难懂,因为那是我自己的理解. 最近几天都是在写一些原来的东西

八数码问题——双向广度优先搜索解决

八数码问题:在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移.空格右移.空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态. 搜索顺序有两种: (1)两个方向交替进行扩展 (2)每次选择节点少的那个扩展 一般来说方法(2)可以克服两端生长不平衡的现象 // eight.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<vector> #include&l

有很多种方法来解决八数码

AI实验报告,改变了重定向.希望通过翼牛. 我很纳闷ida*然而,如何快速的双搜索.还找到了灵感不在位的基础上A*和Ida*来到慢.特别ida* 搜索31步骤甚至十几秒.我写的代码是有问题?忘记丹尼尔路过指点啊.!! ! 另外声明一下,有些东西也是看网上各路牛人的blog学来的,因为比較杂,再次无法一一列出.总之再次感谢把自己的思考的结果放到网上与大家分享的大牛们.谢谢! 八数码问题 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字.不同棋子上标的数字不同

A*算法解决八数码问题

以下内容仅是我个人对八数码问题和A*寻路算法的理解,因为我是菜鸟一个,所以写的比较通俗.  八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤. A*算法: A*算法是一种在静态路网中求解最短路径的有效算法,通俗地讲,它不是像深度优先搜索算法和广度优先搜索算法一样的傻瓜式的埋头

【算法】BFS+哈希解决八数码问题

15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖"X"; 拼图的目的是安排瓷砖以便它们排序为: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15× 这里唯一合法经营是交流'X'与它共享一个边缘的瓷砖之一.作为一个例子,举动下列顺序解决了一个稍微加扰难题: 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 6 7 8 5 6

人工智能作业homework2--------A*算法解决八数码

1.启发式搜索算法A 启发式搜索算法A,一般简称为A算法,是一种典型的启发式搜索算法.其基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的节点来扩展. 评价函数的形式如下: f(n)=g(n)+h(n) 其中n是被评价的节点. f(n).g(n)和h(n)各自表述什么含义呢?我们先来定义下面几个函数的含义,它们与f(n).g(n)和h(n)的差别是都带有一个"*"号. g*(n):表示从初始节点s到节点n的最短路径的耗散值: h*(n):表示从节点n到目标节点

BFS解决八数码问题和狼人过河问题

1.八数码问题 问题描述: 初态: 0    1    2 3    4    5 6    7    8 如何移动交换0的位置达到终态 1    2     3 4    5     6 7    8     0 思路如下: 先将图转换为一个整数 初态:876543210终态:087654321 构造状态的数据结构 struct node{int x;int where0;} 运动规则如下 switch where0:case0: d,rcase1: d,l,rcase2: d,lcase3:

【转】A*算法解决八数码问题

from utils import ( PriorityQueue) import copy infinity = float('inf') def best_first_graph_search(problem, f): #定义初始节点 node = Node(problem.initial) node.fvalue=f(node) #如果是最终结果,返回节点 if problem.goal_test(node): return node #frotier是一个顺序队列,从小到大排列,排列比较

八数码问题解析

八数码的问题描述为: 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用-1来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 解决八数码的方法很多,本文采用1.广度优先搜索的策略,和A星算法两种比较常用的算法思想解决此问题 广度优先搜索的策略一般可以描述为以下过程: 状态空间的一般搜索过程 OPEN表:用于存放刚生成的节点 CLOSE表:用于存放将要扩