问题描述:
算法程序: 设计一个启发函数,利用A算法求解15数码问题。
要求: 尽可能用与A算法一致的思路实现算法, 力求简单明了地给出一个解.
一、A*算法
A算法是一种常用的启发式搜索算法。 在A算法中,一个结点位置的好坏用估价函数来对它进行评估。 A*算法的估价函数可表示为: f’(n) = g’(n) + h’(n) 这里,f’(n) 是估价函数, g’(n) 是起点到终点的最短路径值,h’(n)是n到目标的最短路经的启发值。由于这个 f’(n) 无法预先知道,所以实际上使用的是下面的估价函数: f(n) = g(n) + h(n)。其中 g(n) 是从初始结点到节点 n 的实际代价, h(n)是从结点n到目标结点的最 佳路径的估计代价。在这里主要是 h(n) 体现了搜索的启发信息,因为 g(n) 是已 知的。用 f(n) 作为 f’(n) 的近似,也就是用 g(n) 代替 g’(n) ,h(n) 代替 h’(n) 。
这样必须满足两个条件:
(1)g(n)>=g’(n)且f必须保持单调递增。
(2)h 必须小于等于实际的从当前节点到达目标节点的最小耗费 h(n)<=h’(n)
二、算法实现步骤(结合本题代码)
- 根据已知的起两个15数码表建立初始状态结点start和目标状态结点end,计算初始状态结点的评价函数值
- 创建open表和close表,将初始状态结点start加入open表中。
- 如果open表为空,则查询失败并退出程序,否则进入步骤4
- 在open表中找到评价函数值最小的点(取open表的首个结点),判断是否为目标结点,如果是则代表查询成功并退出循环(程序),否则将当前结点作为待拓展结点,进入步骤5
- 对于待拓展结点,通过上下左右四个方向移动空格位置来拓展新状态结点,同时更新各个新状态节点的评价函数估计值,并且新状态结点记录下前一个结点下标index(为了打印最终状态路径)。
- 对于新拓展的结点:
①若新拓展的结点不在open表和close表中,则加入到open表中;
②若新拓展的结点在open表,如果新节点的评价函数估计值小于原有结点的评价函数估计值,则用新节点替换掉open表的旧结点
③若新拓展的结点在close表,如果新节点的评价函数估计值小于原有结点的评价函数估计值,则用新节点替换掉close表的旧结点 - 将当前拓展结点从open表中删除,加入close表中,并且对open表依据评价函数估计值进行排序,跳转到步骤3.
实现代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>
using namespace std;
#define ROW 4
#define COL 4
#define N 0 //north
#define S 1 //south
#define W 2
#define E 3
class state{
public:
int status = 0;
int d = 0; //当前深度值
int p = 0; //与目标状态的总距离
int f = 0; //函数估算值
int direction = 0; // 记录方向
int index; //下标
int pre; //记录前一状态
int matrix[ROW][COL];
state(){}
state(int matrix[ROW][COL]){ // 构造函数初始化
memcpy(this->matrix, matrix, sizeof(int)*ROW*COL);
//pre = NULL;
}
bool compare(int t[ROW][COL]) { //判断当前矩阵状态与矩阵t是否一样
int sum = 0;
for(int i = 0; i < ROW; i++)
for (int j = 0; j < COL; j++) {
if (matrix[i][j] != t[i][j])
return false;
}
return true;
}
void setS0(int d, int f) { //设置起始状态的深度和评价函数估计值
this->d = d;
this->f = f;
}
void setD(int t[ROW][COL]) { // 计算当前状态深度
d += 1; //深度值加1
setP(t);
}
void setP(int t[ROW][COL]) { // 计算当前状态各点与目标状态t的各点的最短距离之和
int num = 0;
for(int i = 0; i < ROW; i++)
for (int j = 0; j < COL; j++) {
for(int m = 0; m < ROW; m++)
for (int k = 0; k < COL; k++) {
if (matrix[i][j] == t[m][k] && matrix[i][j] != 0) {
num += abs(i - m) + abs(j - k);
}
}
}
p = num;
setF(); //接着设计估评价函数估计值(p+d)
}
void setF() {
f = p + d;
}
bool operator<(const state &temp) const {
return f < temp.f;
}
bool up(int t[ROW][COL]) {
if (direction != S) {//上一步不是向下移 防止出现来回移动
int temp;
direction = N;//这步是向上移
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (matrix[i][j] == 0 && i - 1 >= 0) { //在i j位置是0
temp = matrix[i][j];
matrix[i][j] = matrix[i - 1][j];
matrix[i - 1][j] = temp;
setD(t); //设置当前状态d p f
return true;
break;
}
}
}
}
setD(t);
return false;
}
bool down(int t[ROW][COL]) {
if (direction != N) {//上一步不是向上移
int temp;
direction = S;//这步是向下移
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (matrix[i][j] == 0 && i + 1 < ROW) {
temp = matrix[i][j];
matrix[i][j] = matrix[i + 1][j];
matrix[i + 1][j] = temp;
setD(t);
return true;
break;
}
}
}
}
setD(t);
return false;
}
bool left(int t[ROW][COL]) {
if (direction != E) {//上一步不是向右移
int temp;
direction = W;//这步是向左移
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (matrix[i][j] == 0 && j - 1 >= 0) {
temp = matrix[i][j];
matrix[i][j] = matrix[i][j - 1];
matrix[i][j - 1] = temp;
setD(t);
return true;
break;
}
}
}
}
setD(t);
return false;
}
bool right(int t[ROW][COL]) {
if (direction != W) {//上一步不是向左移
int temp;
direction = E;//这步是向右移
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (matrix[i][j] == 0 && j + 1 < COL) {
temp = matrix[i][j];
matrix[i][j] = matrix[i][j + 1];
matrix[i][j + 1] = temp;
setD(t);
return true;
break;
}
}
}
}
setD(t);
return false;
}
void show() { //显示当前状态 并且显示对应的深度值 与目标的差距 评价函数估计值
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
cout << matrix[i][j] <<" ";
}
cout << endl;
}
cout << "d:" << d << " " << "h:" << p << " " << "f:" << f << endl;
cout << endl;
}
};
vector<state> store;
class A_star{
public:
vector<state> open;
vector<state> close;
state start;
state end;
int count = 0;
A_star() {}
A_star(state start, state end){ //构造函数
this->start = start;
this->end = end;
this->start.setS0(-1, 0);
this->start.setD(end.matrix);
open.push_back(this->start); //首状态进入open表
}
bool inOpen(state temp) { // 判断是否在open表
vector<state>::iterator it;
int count = 0;
for (it = open.begin(); it != open.end(); it++) {
if (temp.compare(it[0].matrix) && temp.f < it[0].f) { //如果新节点在open表中 用新节点替换旧节点 IF 新节点的f值小于旧节点
open.erase(open.begin() + count); //删除旧结点
open.push_back(temp); //插入新节点
return true;
}
count++;
}
return false;
}
bool inClose(state temp) { // 判断是否在close表
vector<state>::iterator it;
int count = 0;
for (it = close.begin(); it != close.end(); it++) {
if (temp.compare(it[0].matrix) && temp.f < it[0].f) {
close.erase(close.begin() + count);//删除旧结点
open.push_back(temp);//插入新节点
return true;
}
count++;
}
return false;
}
state transfer(int t[ROW][COL]) { //从起始状态开始拓展 t是最终状态的矩阵值
while (true) {
if (open.empty()){// open表为空查询失败
cout<<"find error!"<<endl;
return 0;
}
state cur = open.front();
int st; //储存p在vector中的下标
store.push_back(cur); //将所有经历的状态进行储存
for(int i=0;i<store.size();i++){
int sum=0;
for(int j=0;j<ROW;j++){
for(int k=0;k<COL;k++){
if(cur.matrix[j][k]==store[i].matrix[j][k]) sum++;
}
}
if(sum==ROW*COL){
st=i;
break; //代表找到p在store中对应的下标
}
}
cur.index=st;
if (cur.compare(t)){ // 判断是否为目标状态
return cur; //若是 则代表找到 并且返回当前state
}
//针对于open表的首元素进行状态拓展
state tempup = cur;
if (tempup.up(t)) { // 上移
tempup.pre=st; //当前tempup已经发生了变化
if (inOpen(tempup) == 0 && inClose(tempup) == 0) //若新节点在open表和close中均未出现
open.push_back(tempup); //则open表加入新节点
}
state tempdown = cur;
if (tempdown.down(t)) { // 下移
tempdown.pre=st;
if (inOpen(tempdown) == 0 && inClose(tempdown) == 0)
open.push_back(tempdown);
}
state templeft = cur;
if (templeft.left(t)) { // 左移
templeft.pre=st;
if (inOpen(templeft) == 0 && inClose(templeft) == 0)
open.push_back(templeft);
}
state tempright = cur;
if (tempright.right(t)) { // 右移
tempright.pre=st;
if (inOpen(tempright) == 0 && inClose(tempright) == 0)
open.push_back(tempright);
}
open.erase(open.begin()); // 从open表删除评价函数估计值最小的元素
close.push_back(cur); // 将评价函数估计值最小的元素加入close表
sort(open.begin(), open.end()); //根据评价函数估计值进行排序
}
}
void printStep(int index){ //路径输出
vector<state> print; //储存输出路径
print.push_back(store[index]); //将状态路径储存
index=store[index].pre;
while(index!=0){ //起始状态的下标在store中对应0 因此在print数组中没有加入起始状态
print.push_back(store[index]); //将状态路径储存
index=store[index].pre;
}
cout<<"Print steps:"<<endl;
for(int i=print.size()-1;i>=0;i--){
cout<<"step: "<<print.size()-i<<endl;
for(int j=0;j<ROW;j++){
for(int k=0;k<COL;k++){
cout<<print[i].matrix[j][k]<<" "; //打印状态对应的矩阵
}
cout<<endl;
}
cout<<"d:"<<print[i].d<<" h:"<<print[i].p<<" f:"<<print[i].f<<endl<<endl;
}
cout<<"find the target!"<<endl;
}
};
int main(){
int s[ROW][COL]={
{5, 1, 2, 4},
{9, 6, 3, 8},
{13, 15, 10, 11},
{14, 0, 7, 12}};
int e[ROW][COL]={
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 0}};
state start(s); //设置起始状态
state end(e); //设置终止状态
cout<<"start state:"<<endl;
start.show();
A_star arrive(start,end); //设置起始状态的评价函数估计值
state last=arrive.transfer(e); //利用A*算法将start状态转移到end 并且获取last最终状态
arrive.printStep(last.index); //输出路径状态
return 0;
}
效果显示