暴力美学之广度搜索求解重排九宫格问题
前言
本文将利用C++语言实现重排九宫的搜索算法问题,代码在**第三部分**。重排九宫问题,是人工智能领域涉及搜索策略的经典问题,即在3X3的正方形方格中排列八个元素,利用空位将其一步步移动从而寻找最优解路径的过程,其中最为常见解决这个问题的算法是广度优先算法,其优点是具有完备性。而且总是能找到最优解,缺点是面对过于复杂的情况,计算量较大,受制于计算资源。广度优先搜索算法的核心思想是按照初始节点所在的层,向下逐层遍历 ,在没有搜索完本层之前不搜索下一层。写博客也主要是为了记录强化自己所学知识。一、搜索问题描述
重排九宫问题会给出两个九宫格,例如输入两个字符串,一个是123456780和另一个123456708,就是表示初始九宫格和终止九宫格。数字来表示九宫格上不同元素差别,也完全可以用字符代替。而0的含义很特殊 ,作为标识符指示空位置,这样我们显然不需要编程也可以发现其解如下:
只需数字8向右移动即可完成我们的目标,那么对于更加复杂的九宫格而言其结果可能无法一眼看出,例如238104765和123804765,亦或是可能根本无解的12346780和213456780。所以编程解决类似问题可以简化人的劳动。
二、广度优先搜索算法描述
1.广度搜索流程图
图片引用自机械工程出版社的《人工智能导论》
2.搜索过程
OPEN表中存放刚生成的节点,而CLOSE表中存放要扩展或者已经扩展的节点,如上图所示,我们通过先将初始节点放入OPEN表中,判断是否为目标节点,如果不是,则判断其可拓展性,如果也不能扩展出新的节点,或者是拓展出的所有的节点都在CLOSE表中记录过了,舍去这个节点,仅仅将其放入CLOSE表中,继续重新在OPEN表中寻找新的节点,重复这个过程。如果找到了可以拓展的节点,那么把这个节点放入CLOSE表中,并把其拓展放入OPEN表中,反复重复上述过程就可以实现对重排九宫问题的广度优先搜索。
三、C++代码实现
// 重排九宫问题.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include <iostream>
#include <string>
using namespace std;
//定义广度搜索深度为8
int deepnum = 8;
//close表中的标记
int closenumber = 0;
//定义九宫格结构体
struct Jiugong {
//搜索深度
int deepnumber;
//九宫格字符组
char jiugongge[3][3];
struct Jiugong*father;
};
struct Jiugong open[1000000];
struct Jiugong close[1000000];
//定义两个数组储存初始和终止九宫格状态
char start[3][3];
char final[3][3];
//定义九宫格初始状态函数
int opennumber;
//显示是否可以拓展
bool flag;
//检查是否到达结束状态
bool checked(char str[3][3],char str2[3][3]) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (str[i][j] != str2[i][j]) {
return false;
}
}
}
return true;
}
//判断九宫格是否已经存在
bool alreadyexit(struct Jiugong a, struct Jiugong b) {
if (checked(a.jiugongge , b.jiugongge)) {
return true;
}
else return false;
}
//寻找0所在位置
int findzero(char s[3][3]) {
for (int i =0; i <= 2; i++) {
for (int j = 0; j <= 2; j++) {
if (s[i][j] == '0')
return i*3+j;
}
}
}
//打印九宫格
void show(char square[3][3]) {
int i, j;
for (i = 0; i <= 2; i++) {
for (j = 0; j <= 2; j++) {
printf_s(" %c ", square[i][j]);
}
printf_s("\r\n");
}
}//展示需要完成移动的路径
void ShowRequire() {
printf_s("初始的位置为:\r\n");
show(start);
printf_s("终止的位置为:\r\n");
show(final);
}
//判断是否在close表中
bool inclose(char s[3][3]) {
return false;
for (int m = 0; m <= closenumber; m++) {
if (checked(s, close[m].jiugongge))
return true;
}
}
//顺序输出
void inorder(struct Jiugong*root) {
if (root == nullptr) {
return;
}
else
inorder(root->father);
show(root->jiugongge);
printf_s(" ↓ \r\n\r\n");
}
//判断是否可扩展
bool able(char o[3][3]) {
if (checked(o, final)) {
printf_s("答案已经找到%d步:\r\n\r\n", close[closenumber].deepnumber+1);
printf_s(" 初始状态:\r\n");
inorder(&close[closenumber]);
printf_s(" 终止状态:\r\n");
show(final);
exit(100);
}
//当不在close表中时,放入open表中
if (!inclose(o)) {
for (int k = 0; k <= 2; k++) {
for (int h = 0; h <= 2; h++) {
open[opennumber].jiugongge[k][h] = o[k][h];
}
}
open[opennumber].deepnumber = close[closenumber].deepnumber + 1;
open[opennumber].father = &close[closenumber];
opennumber++;
return true;
}
}
int main()
{
//定义两个字符串存储用户输入
string str1, str2;
cin >> str1 >> str2;
//初始化九宫格条件
int index = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
start[i][j] = str1[index];
final[i][j] = str2[index];
index++;
}
}
//展示问题
ShowRequire();
//广度优先搜索
//把初始节点放入open表中
open[0].deepnumber = 0;
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++)
open[0].jiugongge[i][j] = start[i][j];
}
opennumber = 1; while (true)
{
//判断open表是否为空
if (!open[0].jiugongge) {
printf_s("问题 无解在第0步停止");
exit(100);
} //不为空
else {
while ((open[0].jiugongge[1][1] + open[0].jiugongge[2][2]) >= 1)
{
//将节点n放入close表中
close[closenumber].deepnumber = open[0].deepnumber;
for (int i = 0; i <= 2; i++) {
for (int j = 0; j <= 2; j++)
close[closenumber].jiugongge[i][j] = open[0].jiugongge[i][j];
close[closenumber].father = open[0].father;
}
//删除open表中元素
for (int l = 0; l <= opennumber + 2; l++) {
open[l] = open[l + 1];
}
opennumber--;
//判断节点n是否为目标节点
if (checked(close[closenumber].jiugongge, final) == true) {
printf_s("答案已经找到0步:\r\n" );
inorder(&close[closenumber]);
printf_s(" 终止状态:\r\n");
show(final);
exit(100);
}
else {
//拓展节点n
int zero = findzero(close[closenumber].jiugongge);
char check[3][3];
//向左拓展
if ((zero) % 3 != 0) {
for (int k = 0; k <= 2; k++) {
for (int h = 0; h <= 2; h++) {
check[k][h] = close[closenumber].jiugongge[k][h];
}
}
check[(zero / 3)][(zero % 3)] = check[zero / 3][zero%3 - 1];
check[(zero / 3)][(zero % 3 - 1)] = '0';
flag=able(check);
}
//向右拓展
if ((zero) % 3 != 2) {
for (int k = 0; k <= 2; k++) {
for (int h = 0; h <= 2; h++) {
check[k][h] = close[closenumber].jiugongge[k][h];
}
}
check[(zero / 3)][(zero % 3)] = check[zero / 3][zero % 3 + 1];
check[(zero / 3)][(zero % 3 +1)] = '0';
flag=able(check);
}
//向上拓展
if ((zero) /3 != 0) { for (int k = 0; k <= 2; k++) {
for (int h = 0; h <= 2; h++) {
check[k][h] = close[closenumber].jiugongge[k][h];
}
}
check[(zero / 3)][(zero % 3)] = check[zero / 3-1][zero % 3 ];
check[(zero / 3)-1][(zero % 3 )] = '0';
flag = able(check);
}
//向下拓展
if ((zero) / 3 != 2) { for (int k = 0; k <= 2; k++) {
for (int h = 0; h <= 2; h++) {
check[k][h] = close[closenumber].jiugongge[k][h];
}
}
check[(zero / 3)][(zero % 3)] = check[zero / 3 + 1][zero % 3];
check[(zero / 3) + 1][(zero % 3)] = '0';
flag = able(check);
}
if (flag == true) {
closenumber++;
}
if (open[0].deepnumber >= deepnum) {
printf_s("\r\n很遗憾当前的搜索深度为%d,没有解!\r\n",deepnum);
exit(100);
}
}
}
}
}
}
代码实现的主要思路是按照流程图一步一步来,定义struct Jiugong结构体,里面定义了搜索深度deepnumber,九宫格字符组char jiugongge[3][3],以及 struct Jiugong*father指向其父节点的结构体指针。搜索深度在最后if (open[0].deepnumber >= deepnum)中用于判断搜索深度是否达到目标int deepnum 从而停止运行程序,九宫格字符组 用于储存九宫格状态节点,指向父节点的指针用于成功匹配九宫格目标后回溯打印九宫格路径
代码可以直接复制粘贴于c++中使用,原理方面如果有不懂的可以推荐听慕课或者教程自学,但还是可以试试我的程序感受一下广度优先搜索算法。
四、效果展示
输入283104765和813204765,经发现四步可以找到最优解
总结
:以上就是今天给大家介绍的内容,本文仅仅简单介绍了九宫格排序利用C++实现广度搜索算法。