A*算法求解15数码问题(含详细注释解释,300行代码)

问题描述:
算法程序: 设计一个启发函数,利用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)

二、算法实现步骤(结合本题代码)

  1. 根据已知的起两个15数码表建立初始状态结点start和目标状态结点end,计算初始状态结点的评价函数值
  2. 创建open表和close表,将初始状态结点start加入open表中。
  3. 如果open表为空,则查询失败并退出程序,否则进入步骤4
  4. 在open表中找到评价函数值最小的点(取open表的首个结点),判断是否为目标结点,如果是则代表查询成功并退出循环(程序),否则将当前结点作为待拓展结点,进入步骤5
  5. 对于待拓展结点,通过上下左右四个方向移动空格位置来拓展新状态结点,同时更新各个新状态节点的评价函数估计值,并且新状态结点记录下前一个结点下标index(为了打印最终状态路径)。
  6. 对于新拓展的结点:
    ①若新拓展的结点不在open表和close表中,则加入到open表中;
    ②若新拓展的结点在open表,如果新节点的评价函数估计值小于原有结点的评价函数估计值,则用新节点替换掉open表的旧结点
    ③若新拓展的结点在close表,如果新节点的评价函数估计值小于原有结点的评价函数估计值,则用新节点替换掉close表的旧结点
  7. 将当前拓展结点从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;
}

效果显示
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_44174803/article/details/109901611