您的位置:首页 > 其它

八数码问题——A*搜索

2006-06-26 22:06 316 查看
先把问题简化一下吧:

在控制台显示这样一个矩阵

[align=center][1][2][3][/align]
[align=center][8] [4][/align]
[align=center][7][6][5][/align]
手动把它打乱,然后让程序将其复原。

和野人传教士问题类似,这也是一个对解空间进行搜索的问题,而且更为简单,这次采用可以缩减扩展节点的规模的A*启发式搜索算法。取节点深度为g(x),错位的数字数为h(x),由于问题很简单,所以与有序搜索算法类似,算法框图:



评价函数封装在类里,所以没显示在框图中。

ElemType类包括了对于状态节点所需的所有操作:显然的,用一个一维数组maze[9]保存这九个位置的数,评价函数evaluate()函数返回当前状态的评价值,对节点进行操作的运算符">>",判断是否是最终节点的isSuccess(),以及打乱初始节点的顺序的disrupt()等等。

#include <iostream>
#include <conio.h>
#include <windows.h> //显示矩阵时需要延时,这里有Sleep()函数。
using namespace std;

//接收键盘时使用
const enum Input { UP = 0x048, DOWN = 0x050, LEFT = 0x04b, RIGHT = 0x04d, ENTER = 0xd };
//扩展时判断方向使用
const int U = 0;
const int D = 1;
const int L = 2;
const int R = 3;

class ElemType {
private:
int depth; //当前节点的深度(就是距离初始节点有多远)
int numWrong(); //有多少错位的数字

public:

int maze[9]; //保存矩阵用
ElemType* flag; //指向根节点
ElemType(); //创建初始节点

ElemType(int,int,int,int,int,int,int,int,int);
bool operator ==(ElemType e) {

for(int i = 0; i < 9; i++)
if(this->maze[i]!=e.maze[i])
return false;
return true;
}
void operator =(ElemType e) {
for(int i = 0; i < 9; i++)
this->maze[i] = e.maze[i];
this->depth = e.depth;
this->flag = e.flag;
this->numWrong();
}

ElemType friend operator >>(ElemType source, int direct) {
//这个运算符的重载是对于L,R,U,D四个方向作出的移动
ElemType result = source;
int i = result.locate0();
switch(direct) {
case U: if(i < 6) {
result.maze[i] = result.maze[i+3];
result.maze[i+3] = 0;
} break;
case D: if(i > 2) {
result.maze[i] = result.maze[i-3];
result.maze[i-3] = 0;
} break;
case L: if(i%3 != 2) {
result.maze[i] = result.maze[i+1];
result.maze[i+1] = 0;
} break;
case R: if(i%3 != 0) {
result.maze[i] = result.maze[i-1];
result.maze[i-1] = 0;
}
}
result.depth++;
return result;
}
int locate0(); //确定0所在的位置,其余方法要调用

bool isSuccess(); //判定当前节点是否是最终节点
//下面是评价函数
int evaluate() { return depth + numWrong(); }
void disrupt() { //打乱初始矩阵
clrscr();
this->print();
int input;
while((input=_getch()) != ENTER) {
switch(input) {
case UP: *this = *this >> U; this->print(); break;
case DOWN: *this = *this >> D; this->print(); break;
case LEFT: *this = *this >> L; this->print(); break;
case RIGHT: *this = *this >> R; this->print();
}
}
}
void print() { //在屏幕中央显示矩阵
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
gotoxy(36+j*3, 8+i);
if(maze[i*3+j]!=0) cout <<'['<< maze[i*3+j] << ']';
else cout << " ";
} cout << endl;
}
Sleep(200);
}
};

void print(ElemType &e) { e.print(); }

ElemType null(0,0,0,0,0,0,0,0,0); //每个位置都是0,这是不存在的

#include "dso.h"
typedef ElemType Status;

接下来是搜索的操作。closed表仍然是野人传教士问题里使用过的Queuex队列;由于可能要随时删除open表里的任意一个节点,所以open表使用单链表,同时要有获得评价函数值最小的节点的位置的方法。

class LListx : public LList {
public:
ElemType getMin() {
//找出并返回评价函数值最小的节点,如果有多个相等的则返回第一个,如果有最终节点也返回。
LNode* temp = L->next;
ElemType e = temp->node;
while(temp->next) {
temp = temp->next;
if( temp->node.evaluate() < e.evaluate() )
e = temp->node;
}
temp = L->next;
while(temp->next) {
temp = temp->next;
if( temp->node.evaluate()==e.evaluate() &&
temp->node.isSuccess() )
e = temp->node;
}
return e;
}
int locateMin() {
//返回getMin()所得到的节点的位置,如果不存在返回INFEASIBLE。
int count = 0;
LNode* temp = L->next;
while( temp && !(temp->node==LListx::getMin()) &&
temp->node.evaluate()!=LListx::getMin().evaluate() ) {
count++;
temp = temp->next;
}
temp = NULL;
if(count < LList::length()) return count;
return INFEASIBLE;
}
};

Answer表的实现方法与野人传教士问题类似。

class Answer : public Queue {
public:
Answer(const ElemType original) {
LListx open;
Queuex closed;
Stack temp;
Status s1, s2;
//以下是搜索算法:
open.insert(original, open.length());
while( !open.isEmpty() ) {
s1 = open.erase(open.locateMin());
closed.enqueue(s1);
if( s1.isSuccess() ) {
while(s1.flag != NULL) { temp.push(s1); s1 = *(s1.flag); }
this->enqueue(original);
while(!s1.isSuccess()) {
s1 = temp.pop(); this->enqueue(s1);
}
return;
}
for(int i = 0; i < 4; i++) {
s2 = s1 >> i;
if( !(s1==s2))
if(closed.locate(s2) == INFEASIBLE) {
s2.flag = closed.getTailPtr();
open.insert(s2, open.length());
}
}
}
}
//...
};


点击下载源程序>>> (使用Borland编译器编译,因为VC没有gotoxy()呀。)

完成于2006年6月20日
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: