一、问题描述
15数码问题同八数码问题,是人工智能中一个很典型的智力问题。15数码问题是在4×4方格盘上,放有15个数码,剩下一个位置为空(方便起见,用0表示空),每一空格其上下左右的数码可移至空格。本问题给定初始位置和目标位置,要求通过一系列的数码移动,将初始状态转化为目标状态。
状态转换的规则:空格四周的数移向空格,我们可以看作是空格移动,它最多可以有4个方向的移动,即上、下、左、右。问题的求解方法,就是从给定的初始状态出发,不断地将空格上下左右的数码移至空格,将一个状态转化成其它状态,直到产生目标状态。
本报告利用A*算法,给出了15数码问题的C++算法实现。
二、A*算法
2.1算法简介
A*(A-Star)算法是求解最短路最有效的直接搜索方法,也是许多其他问题的常用启发式算法。
其基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的结点来扩展。评价函数形式为:f(n)=g(n)+h(n)。其中f(n)是从初始状态经由状态n到目标状态的代价估计,g(n) 是在状态空间中从初始状态到状态n的实际代价,h(n) 是从状态n到目标状态的最佳路径的估计代价。
A*算法要求启发函数中的h(n)是处在h*(n)的下届范围,即满足h(n)<=h*(n).
2.2评价函数设置
本报告取f(n)=g(n)+h(n)=d(n)+P(n)。其中,d(n)为当前结点的深度,P(n)为当前状态下各将牌到目标位置的距离之和。距离的定义为:“某将牌行下标与目标位置行下标之差的绝对值 + 列下标与目标位置列下标之差的绝对值”。距离越小,该节点的效果越好。某个状态所有将牌到目标位置的距离之和用“h值”表示。
三、算法设计
本课程使用产生式系统的结构。
3.1综合数据库
主要包括:目标状态、初始状态、当前状态、状态所属表、当前状态的评价函数值、当前状态的移动方向。
3.2规则库
主要包括:向左移动、向上移动、向右移动、向下移动。
3.3控制系统
算法的功能:产生15数码问题的解(由初始状态到达目标状态的过程)
输入:初始状态,目标状态
输出:从初始状态到目标状态的一系列过程
算法描述:
Begin:
读入初始状态和目标状态,并计算初始状态评价函数值f;
初始化两个open表和closed表,将初始状态放入open表中
If(open表为空)
查找失败;
End if
else
① 在open表中找到评价值最小的节点,作为当前结点,并放入closed表中;
② 判断当前结点状态和目标状态是否一致,若一致,跳出循环;否则跳转到③;
③ 对当前结点,分别按照上、下、左、右方向移动空格位置来扩展新的状态结点,并计算新扩展结点的评价值f并记录其父节点;
④ 对于新扩展的状态结点,进行如下操作:
A.新节点既不在open表中,也不在closed表中,则ADD (mj, OPEN);
B.新节点在open表中,则IF f(n-mk) < f(mk)
THEN f(mk):=f(n-mk),
C.新节点在closed表中,则IF f(n-ml) < f(ml)
THEN f(ml):=f(n-ml),
⑤ 把当前结点从open表中移除;
End if
End
四、实现
4.1 S类(表示状态)
#include "stdafx.h"
#include "S.h"
#include <math.h>
S::S(void)
{
int G0[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};
memcpy(G,G0,sizeof(int)*4*4);
state=0;
}
void S::setS0(int d0,int f0){
d=d0;
f=f0;
}
void S::setS(int s0[4][4]){
memcpy(s,s0,sizeof(int)*4*4);
// setH();
}
void S::setState(int state0){
state=state0;
}
void S::setH(){
h=0;
int p=0;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
for(int m=0;m<4;m++){
for(int k=0;k<4;k++){
if(s[i][j]==G[m][k]&&s[i][j]!=0){
p=abs(i-m)+abs(j-k);
}
}
}
h=h+p;
p=0;
}
}
setD();
}
int S::getH(){
return h;
}
void S::setD(){
d=d+1;
setF();
}
int S::getD(){
return d;
}
void S::setF(){
f=getD()+getH();
}
int S::getF(){
return f;
}
bool S::compare(int temps[4][4]){
int temp[4][4];
memcpy(temp,temps,sizeof(int)*4*4);
int sum=0;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(s[i][j]==temps[i][j]){
sum=sum+1;
}
}
}
if(sum==16){
return 1;//1是目标
}
else
return 0;
}
void S::show(){
// int h=getH();
// int result=compare(G);
// cout<<h<<endl<<result<<endl;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(j!=3){
if(s[i][j]<10){
cout<<s[i][j]<<" ";
}
else
cout<<s[i][j]<<" ";
}
else
cout<<s[i][j]<<endl;
}
}
cout<<"D值为:"<<d<<" ";
cout<<"H值为:"<<h<<" ";
cout<<"F值为:"<<f<<endl;
cout<<endl;
}
void S::error(){
cout<<endl<<"查找失败!"<<endl;
}
void S::success(int i){
cout<<endl<<"使用"<<i<<"步,找到目标!"<<endl<<endl;
}
bool S::Up(){
int i=0,j=0;
int temp;
if(Direct!=1){//上一步不是向下移
Direct=0;//这步是向上移
for(i=0;i<4;i++){
for(j=0;j<4;j++){
if(s[i][j]==0&&i-1>=0){
temp=s[i][j];
s[i][j]=s[i-1][j];
s[i-1][j]=temp;
setH();
return 1;
break;
}
}
}
}
setH();
return 0;
}
bool S::Down(){
int i=0,j=0;
int temp;
if(Direct!=0){//上一步不是向上移
Direct=1;//这步是向下移
for(i=0;i<4;i++){
for(j=0;j<4;j++){
if(s[i][j]==0&&i+1<4){
temp=s[i][j];
s[i][j]=s[i+1][j];
s[i+1][j]=temp;
setH();
return 1;
break;
}
}
}
}
setH();
return 0;
}
bool S::Left(){
int i=0,j=0;
int temp;
if(Direct!=3){//上一步不是向右移
Direct=2;//这步是向左移
for(i=0;i<4;i++){
for(j=0;j<4;j++){
if(s[i][j]==0&&j-1>=0){
temp=s[i][j];
s[i][j]=s[i][j-1];
s[i][j-1]=temp;
setH();
return 1;
break;
}
}
}
}
setH();
return 0;
}
bool S::Right(){
int i=0,j=0;
int temp;
if(Direct!=2){//上一步不是向左移
Direct=3;//这步是向右移
for(i=0;i<4;i++){
for(j=0;j<4;j++){
if(s[i][j]==0&&j+1>0){
temp=s[i][j];
s[i][j]=s[i][j+1];
s[i][j+1]=temp;
setH();
return 1;
break;
}
}
}
}
setH();
return 0;
}
4.2 AStar类(算法描述)
#include "stdafx.h"
#include "AStar.h"
#include <algorithm> // std::sort
using namespace std;
AStar::AStar(void)
{
int G0[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,0}};
memcpy(G,G0,sizeof(int)*4*4);
int ss0[4][4]={{5,1,2,4},{9,6,3,8},{13,15,10,11},{14,0,7,12}};
//int ss0[4][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,0,14,15}};
s0.setS0(-1,0);
s0.setS(ss0);
s0.setH();
open.push_back(s0);//①初始状态放入OPEN表中
s0.setState(1);//②改变s0的状态值,1为在OPEN表中
step=-1;
}
/*
void AStar::show(){
int h=open[0].getH();
int result=open[0].compare(G);
cout<<h<<endl<<result<<endl;
switch (index0)
{
case 0:{
cout<<"查找失败"<<endl;
}
case 1:
cout<<"OPEN表不为空"<<endl;
default:
break;
}
}*/
void AStar::AA(){
step++;
if(open.size()!=0){
BestS=open[0];
BestS.show();
closed.push_back(open[0]);//OPEN表第一个状态放入CLOSED表中
open.erase(open.begin());//将OPEN表第一个状态删除
if(BestS.compare(G)==1){
BestS.success(step);
}
else{
genChild();
AA();
}
}
else
BestS.error();
}
void AStar::genChild(){
U=BestS;
D=BestS;
L=BestS;
R=BestS;
if(L.Left()==1){
L.setState(1);
if(ifInOpen(L)!=1&&ifInClosed(L)!=1){
open.push_back(L);
}
// L.show();
}
if(U.Up()==1){
U.setState(1);
if(ifInOpen(U)!=1&&ifInClosed(U)!=1){
open.push_back(U);
}
// U.show();
}
if(R.Right()==1){
R.setState(1);
if(ifInOpen(R)!=1&&ifInClosed(R)!=1){
open.push_back(R);
}
// R.show();
}
if(D.Down()==1){
D.setState(1);
if(ifInOpen(D)!=1&&ifInClosed(D)!=1){
open.push_back(D);
}
// D.show();
}
reverse(open.begin(), open.end());
sortOpen();
}
void AStar::sortOpen(){
S min;
vector<S> newOpen;
sort(open.begin(),open.end(),compare);//降序排列
/*
vector<S>::iterator it;
while(open.size()!=0){
min=open[0];
for(it=open.begin();it!=open.end();it++){
if(it[0].getF()<min.getF()){
min=it[0];
}
}
}*/
}
bool AStar::compare(S pfirst,S psecond) //如果该vector存入的是对象的话该函数参数须是
{ // 对象的引用,而不该是指针
return pfirst.getF() < psecond.getF();
}
bool AStar::ifInOpen(S temp){
int i=0;
vector<S>::iterator it;
for(it=open.begin();it!=open.end();it++){
if(temp.compare(it[0].s)&&temp.getF()<it[0].getF()){
open.erase(open.begin()+i);
open.push_back(temp);
return 1;
}
i++;
}
return 0;
}
bool AStar::ifInClosed(S temp){
int i=0;
vector<S>::iterator it;
for(it=closed.begin();it!=closed.end();it++){
if(temp.compare(it[0].s)&&temp.getF()<it[0].getF()){
closed.erase(closed.begin()+i);
open.push_back(temp);
return 1;
}
i++;
}
return 0;
}
4.3主函数
// AI.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <vector>
#include "AStar.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
//int s0[4][4]={{5,1,2,4},{9,6,3,8},{13,15,10,11},{14,0,7,12}};
/*int s0[]={5,1,2,4,9,6,3,8,13,15,10,11,14,0,7,12};
int g[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
vector<int> s(s0,s0+16);
vector<int> G(g,g+16);*/
AStar test;
test.AA();
system("pause");
return 0;
}
五、运行结果
初始矩阵:
5 | 1 | 2 | 4 |
9 | 6 | 3 | 8 |
13 | 15 | 10 | 11 |
14 | 0 | 7 | 12 |
目标矩阵:
1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 0 |