八数码的A*与IDA*算法-搜索进阶练习1

Posted ling_xiao007

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八数码的A*与IDA*算法-搜索进阶练习1相关的知识,希望对你有一定的参考价值。

八数码的A*与IDA*算法-搜索进阶练习1


hdu1043:http://acm.hdu.edu.cn/showproblem.php?pid=1043
poj1077:http://poj.org/problem?id=1077
题意:众所周知的八数码问题,就不再描述了。不得不说,为了练习A*以及IDA*就直接看题解了。 了解之后分析如下。
解题报告:
先谈A*。A*是一种 启发式搜索,也就是说在每次搜索前,先进行在状态空间中对每一个搜索的位置通过f(n) = g(n) + h(n)进行评估,得到最好的位置,再从这个位置,进行搜索直到目标。其中f(n)是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。
然后是IDA*。A*算法和迭代加深算法的结合。
1.搜索框架? A*(IDA*)算法。搜索直到终态结束,或该状态非法进行下一状态。 2.状态? 直接记录每个状态的maze、x位置信息以及A*所需f、g、h。同时使用hash值指向所有状态。 3.状态转移? x与上下左右互换,更新状态信息。 4.剪枝? 一个简单有效的剪枝逆序数为奇则必定无解。 5.hash以及h? hash可通过各位置逆序数乘位置散列值之和散列。h为所有数字块的曼哈顿距离和。
具体而言。A*通过构造优先队列实现搜索。构造优先队列时当f相同时按照g值从大到小排序,这样又是一个很给力的减枝。
#include <cstdio>
#include <queue>
#include <algorithm>
#include <cstring>
using namespace std;

//A*: 因为每次移动都会影响一个点的曼哈顿距离(不算x)
//构造h()为所有数字块的曼哈顿距离和,用逆序数hash(算x)
//根据逆序数奇偶性(不算x)减掉无法到达的情况,
struct Node

	char maze[3][3];
	int r, c, f, g, h;		//(x, y); f = g + h
							/*f(n) 是从初始点经由节点n到目标点的估价函数,
							g(n) 是在状态空间中从初始节点到n节点的实际代价,
							h(n) 是从n到目标节点最佳路径的估计代价。*/
	Node() 
	Node(const Node & rhs)
	
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c, g = rhs.g, f = rhs.f, h = rhs.h;
	
	//估价函数小的先出队,相同则实际代价大的先出队
	friend bool operator < (const Node & a, const Node & b)
	
		if (a.f == b.f) return a.g < b.g;
		return a.f > b.f;
	
st;

const int dir[4][2] =   1,0 , 0,1 , -1,0 , 0,-1  ;//方向
char dr[5] = "drul";										//方向查找表
int pos[9][2] = 0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2;		//位置信息		
int fac[9] =  1, 1, 2, 6, 24, 120, 720, 5040, 40320 ;		//hash散列因子
char path[370000];
bool vis[370000];
int pre[370000];
int shash;

int h(const Node s)			//估价函数,偏移步数

	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			if (s.maze[i][j] == 'x') continue;
			int c = s.maze[i][j] - '1';
			int x = pos[c][0], y = pos[c][1];
			ans += (abs(x - i) + abs(y - j));
		
	return ans;


int hash_node(const Node s) //hash散列,储存状态

	int ans = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			int cnt = 0;
			str[i * 3 + j] = s.maze[i][j];
			for (int k = 0; k < i * 3 + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += fac[i * 3 + j] * cnt;
		
	return ans;


bool check_h()				//逆序为奇返回true

	int cnt = 0;
	char str[10];
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = st.maze[i][j];
			if (st.maze[i][j] == 'x') continue;
			for (int k = 0; k < i * 3 + j; k++)
			
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			
		
	return cnt & 1;


bool bfs()

	priority_queue<Node> q;
	q.push(st);
	memset(vis, false, sizeof(vis));
	vis[shash] = true;
	while (!q.empty())
	
		Node u = q.top(); q.pop();
		int ihu = hash_node(u);
		for (int d = 0; d < 4; d++)
		
			Node v(u);
			v.r = u.r + dir[d][0], v.c = u.c + dir[d][1];
			if (v.r < 0 || v.r >= 3 || v.c < 0 || v.c >= 3) continue;
			//更新地图
			v.maze[u.r][u.c] = v.maze[v.r][v.c];
			v.maze[v.r][v.c] = 'x';					
			v.g++, v.h = h(v); v.f = v.g + v.h;	//更新f = g + h
			int ihv = hash_node(v);
			if (vis[ihv]) continue;
			vis[ihv] = true; pre[ihv] = ihu;
			path[ihv] = dr[d];
			if (ihv == 0) return true;			//搜索结束
			q.push(v);
		
	
	return false;


void print(int h)

	if (h != shash)
	
		print(pre[h]);
		putchar(path[h]);
	


char in[100];

int main()

	while (gets_s(in))
	
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if ((in[i] <= '8' && in[i] >= '1') || in[i] == 'x')
			
				st.maze[x][y] = in[i];
				if (in[i] == 'x') st.r = x, st.c = y;
				y++;
				if (y == 3) x++, y = 0;
			
		if (check_h())  puts("unsolvable"); continue; 
		st.g = 0; st.h = h(st); st.f = st.h;
		if ((shash = hash_node(st)) == 0)  puts(""); continue; 
		int thash = bfs();
		print(0);			//递归输出答案
		puts("");
	
	return 0;


而IDA*的话,和A*相同的h()函数。当前h深度下一层一层地进行搜索
#include <cstdio>
#include <algorithm>
using namespace std;

struct Node

	int r, c, f, g, h;
	char maze[3][3];
	Node()
	Node(const Node & rhs) 
		for (int i = 0; i < 3; i++)
			for (int j = 0; j < 3; j++)
				maze[i][j] = rhs.maze[i][j];
		r = rhs.r, c = rhs.c;
	
ts, s;

const int maxn = 370000;
const int dir[4][2] =   1,0 , 0,1 , -1,0 , 0,-1  ;
const char dr[5] = "drul";
const int fac[9] =  1,1,2,6,24,120,720,5040,40320 ;
const int pos[][2] =  0,0,0,1,0,2,1,0,1,1,1,2,2,0,2,1,2,2 ;
char path[maxn];
bool vis[maxn];
int deep;

int h(const Node st) 
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			if (st.maze[i][j] == 'x') continue;
			int c = st.maze[i][j] - '1';
			ans += abs(pos[c][0] - i) + abs(pos[c][1] - j);
		
	return ans;


int mhash(const Node st) 
	char str[10];
	int ans = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = st.maze[i][j];
			int cnt = 0;
			for (int k = 0; k < 3 * i + j; k++)
				if (str[k] > str[i * 3 + j]) cnt++;
			ans += cnt * fac[i * 3 + j];
		
	return ans;


bool check() 				//剪枝, 逆序数为奇返回1
	char str[10];
	int cnt = 0;
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
		
			str[i * 3 + j] = s.maze[i][j];
			if (s.maze[i][j] == 'x') continue;
			for (int k = 0; k < 3 * i + j; k++)
			
				if (str[k] == 'x') continue;
				if (str[k] > str[i * 3 + j]) cnt++;
			
		
	return cnt & 1;


int dfs(int d) 
	//搜索完成
	if (h(ts) == 0) return true;
	//超过当前的搜索深度   
	if (h(ts) + d > deep) return false;
	//当前r、c
	int r = ts.r, c = ts.c;
	for (int dd = 0; dd < 4; dd++)
	
		int nr = ts.r + dir[dd][0], nc = ts.c + dir[dd][1];
		if (nr < 0 || nc < 0 || nr > 2 || nc > 2) continue;
		ts.maze[r][c] = ts.maze[nr][nc];
		ts.maze[nr][nc] = 'x';
		int ihv = mhash(ts);
		if (vis[ihv]) 
			ts.maze[nr][nc] = ts.maze[r][c];
			ts.maze[r][c] = 'x';
			continue;
		
		vis[ihv] = true;
		ts.r = nr, ts.c = nc;
		path[d] = dr[dd];
		if (dfs(d + 1)) return true;
		vis[ihv] = false;
		ts.r = r, ts.c = c;
		ts.maze[nr][nc] = ts.maze[r][c];
		ts.maze[r][c] = 'x';
	
	return false;


int main() 
	char in[100];
	while (gets_s(in))
	
		for (int i = 0, x = 0, y = 0; in[i]; i++)
			if (in[i] == 'x' || (in[i] > '0' && in[i] < '9'))
			
				if (in[i] == 'x') s.c = y, s.r = x;
				s.maze[x][y++] = in[i];
				if (y == 3) y = 0, x++;
			
		if (check())  puts("unsolvable"); continue; 
		memset(vis, false, sizeof(vis));
		vis[mhash(s)] = true;
		ts = s; deep = 0;
		while (true)
		
			if (dfs(0)) break;
			deep++;
		
		for (int i = 0; i < deep; i++)
			putchar(path[i]);
		puts("");	
	
	return 0;

最后打卡一下。之前Ubuntu的ibus默认的中文输入法双拼的,当时就感觉是一堆乱码,玩毛。不几天前,尝试用双拼了。

以上是关于八数码的A*与IDA*算法-搜索进阶练习1的主要内容,如果未能解决你的问题,请参考以下文章

八数码问题强化版:十五数码问题idA*版本

HDU 1043 Eight八数码解题思路(bfs+hash 打表 IDA* 等)

八数码问题算法,谁有?

八数码问题的问题,有解条件以及求解算法(宽度优先搜索)

人工智能基于八数码问题对比搜索算法的性能

人工智能基于八数码问题对比搜索算法的性能