软件项目实习2-八数码(A*算法)

运行环境

c++语言,vs2019

拆开的函数

启发函数(计算消耗资源多少的函数)

F=G+H
存储走的是第几步(层数)
int G;
存储不在位的格数(作为我们的启发式函数)
int H;
存储估价函数的值
int F;

定义结构体

	struct EightNum
{
    
        //存储八数码
	int status[9];
	//存储走的是第几步(层数)
	int G;
	//存储不在位的格数(作为我们的启发式函数)
	int H;
	//存储估价函数的值
	int F;
	//存储0数码的位置
	int Zero;
	//存储操作符(1左2右3上4下)
	int step;
	//父指针
	EightNum* Parent;
};

计算不在位的字格数H

下面展示一些 内联代码片

int CountH(int* status)
{
    
    
	int H = 0;
	int i;
	for (i = 0; i <= 8; i++)
	{
    
    
		if (FinalStatus[i] != status[i])
		{
    
    
			H++;
		}
	}
	return H;
}

判断新生成的节点是否已经存在于OPEN表或CLOSE表中

下面展示一些 内联代码片

int Exist(EightNum* N)
{
    
    
	int i, j;
	//计算不在位的字格数,如果为0,则证明给函数的节点在表中已存在
	int H = 0;
	int status[9];

	Node = new EightNum;
	Node = N;

	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = Node->status[i];
	}
	//判断是否在OPEN表
	for (i = 0; i <= open - 1; i++)
	{
    
    
		for (j = 0; j <= 8; j++)
		{
    
    
			if (status[j] != OPEN[i].status[j])
			{
    
    
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)
		{
    
    
			//如果在OPEN表中,返回i(节点在OPEN的位置)+ 1(在OPEN找到该节点)
			return i + 1;
		}
		//扫描完一个节点后重置H
		H = 0;
	}
	//判断是否在CLOSE表
	for (i = 0; i <= close - 1; i++)
	{
    
    
		for (j = 0; j <= 8; j++)
		{
    
    
			if (status[j] != CLOSE[i].status[j])
			{
    
    
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)
		{
    
    
			//如果在CLOSE表中,返回-i(i为节点在CLOSE的位置)- 1(在CLOSE找到该节点)
			return (-i) - 1;
		}
		//扫描完一个节点后重置H
		H = 0;
	}

	return 0;
}

初始化节点

下面展示一些 内联代码片

EightNum* EightNumInit(int status[10], int zero, int g, EightNum* parent, int step)
{
    
    
	int i;
	Node = new EightNum;
	for (i = 0; i <= 8; i++)
	{
    
    
		Node->status[i] = status[i];
	}
	Node->Zero = zero;
	Node->G = g;
	Node->H = CountH(Node->status);
	Node->F = Node->G + Node->H;
	Node->Parent = parent;
	Node->step = step;
	return Node;
}

对数组操作

int* Left(int* s, int z)
int* Right(int* s, int z)
int* Up(int* s, int z)
int* Down(int* s, int z)

判断子节点是否在OPEN或CLOSE中,并进行对应的操作

void ExistAndOperate(EightNum* N)
{
    
    
	int i;
	//定义表示新生成节点是否在OPEN表或CLOSE表中, 值为0 均不在,值>0 只在OPEN表,值<0 只在CLOSE表
	int inList;
	Node = new EightNum;
	Node = N;
	//如果是第一层的节点,直接加入OPEN中,返回
	if (Node->G == 1)
	{
    
    
		OPEN[open] = *Node;
		open++;
		return;
	}
	//判断新节点是否在OPEN或CLOSE中
	inList = Exist(Node);
	//如果均不在两个表中,将节点加入OPEN表中
	if (inList == 0)
	{
    
    
		//将拓展出的新结点加入到OPEN表中
		OPEN[open] = *Node;
		open++;
	}
	//如果在OPEN中,说明从初始节点到该节点找到了不同路径,保留耗散值短的那条路径
	else if (inList > 0)
	{
    
    //如果表内节点F值大于新节点F值,用新节点代替表内节点
		if (OPEN[inList - 1].F > Node->F)
		{
    
    
			OPEN[inList - 1] = *Node;
		}
	}
	//如果在CLOSE中,说明初始节点到该节点有两条路径,如果新找到的路径耗散值大,什么都不做,如果较小,将其从CLOSE中取出放入OPEN中    
	else if (inList < 0)
	{
    
    
		inList = -inList;
		//如果较小
		if (CLOSE[inList - 1].F > Node->F)
		{
    
    //将其取出放入OPEN
			OPEN[open] = *Node;
			open++;
		}
		//将其在CLOSE中释放
		for (i = inList - 1; i <= close - 1; i++)
		{
    
    
			CLOSE[i] = CLOSE[i + 1];
		}
		close--;
	}
}

全部代码

下面展示一些 内联代码片

#include <iostream>
#include <time.h>
using namespace std;


/*
定义结构体
*/
struct EightNum
{
    
        //存储八数码
	int status[9];
	//存储走的是第几步(层数)
	int G;
	//存储不在位的格数(作为我们的启发式函数)
	int H;
	//存储估价函数的值
	int F;
	//存储0数码的位置
	int Zero;
	//存储操作符(1左2右3上4下)
	int step;
	//父指针
	EightNum* Parent;
};


#define MAXLISTSIZE 10000
#define MAXSTEPSIZE 100
//声明最终状态
int FinalStatus[9];
//定义OPEN表和CLOSE表,open和close是表中最后一个内容的下一位序号
EightNum OPEN[MAXLISTSIZE];
EightNum CLOSE[MAXLISTSIZE];
int open = 0;
int close = 0;

EightNum* Node;






/*
计算不在位的字格数H
返回 H
*/
int CountH(int* status)
{
    
    
	int H = 0;
	int i;
	for (i = 0; i <= 8; i++)
	{
    
    
		if (FinalStatus[i] != status[i])
		{
    
    
			H++;
		}
	}
	return H;
}

/*
判断新生成的节点是否已经存在于OPEN表或CLOSE表中
返回 表征是否存在于OPEN或CLOSE的值,值为0 均不在,值>0 只在OPEN表,值<0 只在CLOSE表,|值|-1表示所在列表中的位置
*/
int Exist(EightNum* N)
{
    
    
	int i, j;
	//计算不在位的字格数,如果为0,则证明给函数的节点在表中已存在
	int H = 0;
	int status[9];

	Node = new EightNum;
	Node = N;

	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = Node->status[i];
	}
	//判断是否在OPEN表
	for (i = 0; i <= open - 1; i++)
	{
    
    
		for (j = 0; j <= 8; j++)
		{
    
    
			if (status[j] != OPEN[i].status[j])
			{
    
    
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)
		{
    
    
			//如果在OPEN表中,返回i(节点在OPEN的位置)+ 1(在OPEN找到该节点)
			return i + 1;
		}
		//扫描完一个节点后重置H
		H = 0;
	}
	//判断是否在CLOSE表
	for (i = 0; i <= close - 1; i++)
	{
    
    
		for (j = 0; j <= 8; j++)
		{
    
    
			if (status[j] != CLOSE[i].status[j])
			{
    
    
				H++;
			}
		}
		//H=0证明在表中找到该节点
		if (H == 0)
		{
    
    
			//如果在CLOSE表中,返回-i(i为节点在CLOSE的位置)- 1(在CLOSE找到该节点)
			return (-i) - 1;
		}
		//扫描完一个节点后重置H
		H = 0;
	}

	return 0;
}

/*
初始化节点
返回 初始化后的节点Node
*/
EightNum* EightNumInit(int status[10], int zero, int g, EightNum* parent, int step)
{
    
    
	int i;
	Node = new EightNum;
	for (i = 0; i <= 8; i++)
	{
    
    
		Node->status[i] = status[i];
	}
	Node->Zero = zero;
	Node->G = g;
	Node->H = CountH(Node->status);
	Node->F = Node->G + Node->H;
	Node->Parent = parent;
	Node->step = step;
	return Node;
}

/*
左移后的变化
返回 左移后的状态
*/
int* Left(int* s, int z)
{
    
    
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = s[i];
	}
	//左移则是下标减1,需要与前一个位置进行值的交换
	temp = status[z - 1];
	status[z - 1] = 0;
	status[z] = temp;
	return status;
}

/*
右移后的变化
返回 右移后的状态
*/
int* Right(int* s, int z)
{
    
    
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = s[i];
	}
	//右移则是下表加一,需要与后一个位置的值交换
	temp = status[z + 1];
	status[z + 1] = 0;
	status[z] = temp;
	return status;
}

/*
上移后的变化
返回 上移后的状态
*/
int* Up(int* s, int z)
{
    
    
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = s[i];
	}

	//上移则是下标减三
	temp = status[z - 3];
	status[z - 3] = 0;
	status[z] = temp;
	return status;
}

/*
下移后的变化
返回 下移后的状态
*/
int* Down(int* s, int z)
{
    
    
	int temp, i;
	static int status[9];
	for (i = 0; i <= 8; i++)
	{
    
    
		status[i] = s[i];
	}
	//加三
	temp = status[z + 3];
	status[z + 3] = 0;
	status[z] = temp;
	return status;
}

/*
判断子节点是否在OPEN或CLOSE中,并进行对应的操作
返回值 NULL
*/
void ExistAndOperate(EightNum* N)
{
    
    
	int i;
	//定义表示新生成节点是否在OPEN表或CLOSE表中, 值为0 均不在,值>0 只在OPEN表,值<0 只在CLOSE表
	int inList;
	Node = new EightNum;
	Node = N;
	//如果是第一层的节点,直接加入OPEN中,返回
	if (Node->G == 1)
	{
    
    
		OPEN[open] = *Node;
		open++;
		return;
	}
	//判断新节点是否在OPEN或CLOSE中
	inList = Exist(Node);
	//如果均不在两个表中,将节点加入OPEN表中
	if (inList == 0)
	{
    
    
		//将拓展出的新结点加入到OPEN表中
		OPEN[open] = *Node;
		open++;
	}
	//如果在OPEN中,说明从初始节点到该节点找到了不同路径,保留耗散值短的那条路径
	else if (inList > 0)
	{
    
    //如果表内节点F值大于新节点F值,用新节点代替表内节点
		if (OPEN[inList - 1].F > Node->F)
		{
    
    
			OPEN[inList - 1] = *Node;
		}
	}
	//如果在CLOSE中,说明初始节点到该节点有两条路径,如果新找到的路径耗散值大,什么都不做,如果较小,将其从CLOSE中取出放入OPEN中    
	else if (inList < 0)
	{
    
    
		inList = -inList;
		//如果较小
		if (CLOSE[inList - 1].F > Node->F)
		{
    
    //将其取出放入OPEN
			OPEN[open] = *Node;
			open++;
		}
		//将其在CLOSE中释放
		for (i = inList - 1; i <= close - 1; i++)
		{
    
    
			CLOSE[i] = CLOSE[i + 1];
		}
		close--;
	}
}

/*
寻找最佳路径函数
返回 最后的节点Node
*/
EightNum* Search()
{
    
    
	int* status;
	int i, j;

	EightNum* Temp;
	//一直循环知道找到解结束


	while (1)
	{
    
    
		Temp = new EightNum;
		//用冒泡排序给OPEN表里面的f大小进行排序
		for (i = open - 1; i > 0; i--)
		{
    
    
			for (j = 0; j < i; j++)
			{
    
    
				if (OPEN[j].F > OPEN[j + 1].F)
				{
    
    
					*Temp = OPEN[j + 1];
					OPEN[j + 1] = OPEN[j];
					OPEN[j] = *Temp;
				}
			}
		}

		Node = new EightNum;
		//从OPEN表中取出第一个元素(F值最小)
		*Node = OPEN[0];
		//判断该节点是否是目标节点,若是,则不在位的格数为0,算法结束,若不是,则将该结点进行扩展
		if (!CountH(Node->status))
		{
    
    
			break;
		}

		Temp = Node;
		//将扩展过的节点放入CLOSE 
		CLOSE[close] = *Node;
		close++;
		//将扩展的节点从OPEN中释放
		for (i = 0; i <= open - 1; i++)
		{
    
    //相当于是出栈
			OPEN[i] = OPEN[i + 1];
		}
		open--;
		//如果能左移,则进行左移创造新结点,下标为0,3,6则不能进行左移
		if ((Temp->Zero) % 3 >= 1)
		{
    
    //创造新结点
			Node = new EightNum;
			//得到新的状态
			status = Left(Temp->status, Temp->Zero);
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero - 1, (Temp->G) + 1, Temp, 1);
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);
		}
		//如果能右移,则进行右移创造新结点 ,下标为2,5,8则不能
		if ((Temp->Zero) % 3 <= 1)
		{
    
      //创造新结点
			Node = new EightNum;
			//得到新的状态
			status = Right(Temp->status, Temp->Zero);
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero + 1, (Temp->G) + 1, Temp, 2);
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);
		}
		//如果能上移,则进行上移创造新结点  ,下标为0,1,2则不可以
		if (Temp->Zero >= 3)
		{
    
    
			Node = new EightNum;
			//得到新的状态
			status = Up(Temp->status, Temp->Zero);
			//初始化新结点
			Node = EightNumInit(status, Temp->Zero - 3, (Temp->G) + 1, Temp, 3);
			//判断子节点是否在OPEN或CLOSE中,并进行对应的操作
			ExistAndOperate(Node);
		}
		//如果能下移,则进行下移创造新结点 ,下标为6,7,8则不可以
		if (Temp->Zero <= 5)
		{
    
    
			Node = new EightNum;                                           //创造新结点
			status = Down(Temp->status, Temp->Zero);                   //得到新的状态
			Node = EightNumInit(status, Temp->Zero + 3, (Temp->G) + 1, Temp, 4);    //初始化新结点
			ExistAndOperate(Node);      //判断子节点是否在OPEN或CLOSE中,并进行对应的操作
		}
		//如果open=0, 证明算法失败, 没有解
		if (open == 0)
			return NULL;
	}


	return Node;
}


void ShowStep(EightNum* Node)
{
    
    
	int STEP[MAXSTEPSIZE];
	int STATUS[MAXSTEPSIZE][9];
	int step = 0;
	int i, j;
	int totalStep = Node->G;
	while (Node)
	{
    
    
		STEP[step] = Node->step;
		for (i = 0; i <= 8; i++)
		{
    
    
			STATUS[step][i] = Node->status[i];
		}
		step++;
		Node = Node->Parent;
	}
	cout << "----------------------" << endl;
	cout << "总步数:" << totalStep << endl;
	cout << "----------------------" << endl;
	for (i = step - 1; i >= 0; i--)
	{
    
    
		if (STEP[i] == 1)
			cout << "向左走一步" << endl;
		else if (STEP[i] == 2)
			cout << "向右走一步" << endl;
		else if (STEP[i] == 3)
			cout << "向上走一步" << endl;
		else if (STEP[i] == 4)
			cout << "向下走一步" << endl;
		else if (STEP[i] == 0)
			cout << "开始:" << endl;
		for (j = 0; j <= 8; j++)
		{
    
    
			cout << STATUS[i][j] << " ";
			//换行输出
			if (j == 2 || j == 5 || j == 8)
				cout << endl;
		}
		cout << "----------------------" << endl;
	}
}
/*
主函数
*/
int main()
{
    
    
	int fstatus[9];
	int i, beginTime, endTime;
	EightNum* FNode;
	EightNum* EndNode;
	//输入初始状态
	cout << "请输入初始状态:" << endl;
	for (i = 0; i <= 8; i++)
	{
    
    
		cin >> fstatus[i];
	}
	cout << endl;
	//输入最终状态
	cout << "请输入最终状态:" << endl;
	for (i = 0; i <= 8; i++)
	{
    
    
		cin >> FinalStatus[i];
	}
	beginTime = clock();
	//判断0数码的位置
	for (i = 0; i <= 8; i++)
	{
    
    
		if (fstatus[i] == 0)
			break;
	}
	//获得初始节点
	FNode = EightNumInit(fstatus, i, 0, NULL, 0);
	//将初始节点放入OPEN中
	OPEN[open] = *FNode;
	open++;
	//寻找最佳路径
	EndNode = Search();

	if (!EndNode)
		cout << "无解" << endl;
	else
		ShowStep(EndNode);                      //展示步骤

	endTime = clock();
	cout << "Run Time:" << endTime - beginTime << "ms" << endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_49180345/article/details/115001454