前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >有很多种方法来解决八数码

有很多种方法来解决八数码

作者头像
全栈程序员站长
发布于 2022-07-18 07:48:10
发布于 2022-07-18 07:48:10
71400
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是全栈君

AI实验报告,改变了重定向。希望通过翼牛。

我很纳闷ida*然而,如何快速的双搜索。还找到了灵感不在位的基础上A*和Ida*来到慢。特别ida* 搜索31步骤甚至十几秒。我写的代码是有问题?忘记丹尼尔路过指点啊。!!!

另外声明一下,有些东西也是看网上各路牛人的blog学来的,因为比較杂,再次无法一一列出。总之再次感谢把自己的思考的结果放到网上与大家分享的大牛们。谢谢!

八数码问题

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字。不同棋子上标的数字不同样。棋盘上另一个空格,与空格相邻的棋子能够移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态。找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后。状态就会发生改变。解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

八数码问题一般使用搜索法来解。搜索法有广度优先搜索法、双向广度优先算法、深度优先搜索法、A*算法等。

这里通过用不同方法解八数码问题来比較一下不同搜索法的效果。

一、BFS

因为状态最多仅仅有9! =362880,最先想到的应该就是暴力搜索。在此对其进行一定的优化。首先将每一个状态,利用状态压缩的思想装换成两个int型变量,然后对于close表里的所有状态则採取一次所有初始化,再利用状态的进行排序,排序完毕后在之后的查询close表过程中就能够使用二分的思想,降低操作,每次查找所须要操作次数为logn<20次。Open表则用队列存储。

每个节点的存储

struct state{

int sta,pos;

}

全排列表示其状态。然后将状态压缩在一个int上。因为每一个数字仅仅能用三位2进制表示,所以会出现反复,在这里,1~8用二进制0~7表示,空位9也用0表示,为区分这两个数。再使用一个int。表示空位所在的位置。比如以下这个状态:

Int sta =

Int pos = 8(从0開始)

之后推断两个状态是否同样。能够使用位运算高速进行。

比如推断当前状态是否与目标态一致则为

if(!(a.sta^target.sta)&&a.pos ==target.pos)

{

printf(“%d\n”,depth);

return true;

}

怎样推断是否有解:

利用奇偶性推断所给出的初始状态有无解.

判别方法是:

以数组为一维的举样例.

将八数码的一个结点表示成一个数组a[9],空格用0表示,设暂时函数p(x)定义为:x数所在位置前面的数比x小的数的个数,

当中0空格不算在之内。那设目标状态为b[9],那r=sigma(p(x)) sigma()表示取全部的x:1-8并求和,

那对于初始状态a[9],t=sigma(p(x)),假设r和t同为奇数或者同为偶数,那么该状态有解。否则无解。

之后节点的存储与推断是否有解。基本同样,不再赘述。

代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#include <time.h>
#define FUCK puts("fuck!")
#define STOP system("pause")
using namespace std;
struct state{
	int sta,pos,step;
}st[400000],source,target;
int temp[10],tar[10],sou[10];
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int num;
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
int search(state a)
{
	int l,r,mid;
	l = 0;
	r = num-1;
	while(l<r)
	{
		mid = (l+r)>>1;
		if(a.sta<st[mid].sta)
			r = mid;
		else if(a.sta>st[mid].sta)
			l = mid;
		else
		{
			mid = mid - 2;
			while((a.sta^st[mid].sta)||(a.pos^st[mid].pos))
				mid++;
			l = r;
		}
	}
	return mid;
}
bool cmp(state a,state b)
{
	if(a.sta!=b.sta)
		return a.sta<b.sta;
	else                 
		return a.pos<b.pos;
}
int main()
{
	num = 0;
	freopen("in.txt","r",stdin);
	clock_t start,end;
	start = clock();
	memset(st,0,sizeof(st));
	for(int j=0;j<9;j++)temp[j] = j;
	do{
		convert(temp,st[num++]);
	}while(next_permutation(temp,temp+9));
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	sort(st,st+num,cmp);
	end = clock();
	printf("%dms\n",end-start);
	while(1)
	{
		int i = 0;
		for(int j=0;j<num;j++)st[j].step=0;
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		convert(sou,source);
		start = clock();
		i = search(source);
		queue<int>q;
		q.push(i);
		int index;
		int count = 0;
		while(!q.empty())
		{
			count ++;
			index = q.front();
			if(!(st[index].sta^target.sta)&&st[index].pos == target.pos)
			{
				printf("%d\n",st[index].step);
				break;
			}
			for(int j = 0; j < 4; j ++)
			{
				if(d[st[index].pos][j])
				{
					int flag = search(exchange(st[index],d[st[index].pos][j]));
					if(!st[flag].step)
					{
						st[flag].step = st[index].step + 1;	
						q.push(flag);
					}	
				}
			}
			q.pop();
		}
		while(!q.empty())q.pop();
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,count);
	}
	system("pause");
        return 0;
}

二、BFS+hash

採用第一种方法须要花较多时间初始化。且查找close表较为耗时。能够採用hash函数来优化,在这里仅仅使用一个简单的哈希函数,即模一个大质数。这样查找close表的时间降低。程序的效率得到了提升

代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos,step;
}st[MAXN],source,target;
int temp[10],tar[10],sou[10];
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int num;
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
int search(state a)
{
	int index = a.sta%MAXN;
	bool flag = true;
	while(flag)
	{
		if(!st[index].sta)
		{
			st[index].sta = a.sta;
			st[index].pos = a.pos;
			flag = false;
		}
		else if(!(st[index].sta^a.sta)&&!(st[index].pos^a.pos))
			flag = false;
		else
			index = (index+1)%MAXN;
	}
	return index;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		memset(st,0,sizeof(st));
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		convert(sou,source);
		start = clock();
		i = search(source);
		queue<int>q;
		q.push(i);
		int index;
		int count = 0;
		while(!q.empty())
		{
			count ++;
			index = q.front();
			if(!(st[index].sta^target.sta)&&st[index].pos == target.pos)
			{
				printf("%d\n",st[index].step);
				break;
			}
			for(int j = 0; j < 4; j ++)
			{
				if(d[st[index].pos][j])
				{
					int flag = search(exchange(st[index],d[st[index].pos][j]));
					if(!st[flag].step)
					{
						st[flag].step = st[index].step + 1;	
						q.push(flag);
					}	
				}
			}
			q.pop();
		}
		while(!q.empty())q.pop();
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,count);
	}
	system("pause");
        return 0;
}

三、双向广搜

接下来採用一种更高速的方式。因为目标态和初始态都已知。能够採用从两态同一时候開始搜。当搜到同一个节点时。搜索结束,将两边的步数加起来输出。在这里我在每一个节点里,用一个值标记,此节点是由哪个状态訪问的,故仅仅需用一个队列交替扩展。

如图所看到的,双向广搜少扩展很多节点,时间效率得到大幅提升。

代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos,step;
	int visit;
}st[MAXN],source,target;
int temp[10],tar[10],sou[10];
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int num;
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
int search(state a)
{
	int index = a.sta%MAXN;
	bool flag = true;
	while(flag)
	{
		if(!st[index].sta)
		{
			st[index].sta = a.sta;
			st[index].pos = a.pos;
			flag = false;
		}
		else if(!(st[index].sta^a.sta)&&!(st[index].pos^a.pos))
			flag = false;
		else
			index = (index+1)%MAXN;
	}
	return index;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		memset(st,0,sizeof(st));
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		convert(sou,source);
		start = clock();
		i = search(source);
		queue<int>q;
		q.push(i);
		i = search(target);
		st[i].visit = 1;
		st[i].step = 1;
		q.push(i);
		if(!(source.sta^target.sta)&&!(source.pos^target.pos))
		{
			printf("0\n");
			while(!q.empty())q.pop();
				continue;
		}
		int index;
		int count = 0;
		bool isSolve = false;
		while(!q.empty()&&!isSolve)
		{
			count ++;
			index = q.front();
			for(int j = 0; j < 4; j ++)
			{
				if(d[st[index].pos][j])
				{
					int flag = search(exchange(st[index],d[st[index].pos][j]));
					if(!st[flag].step)
					{
						st[flag].step = st[index].step + 1;	
						st[flag].visit = st[index].visit;
						q.push(flag);
					}	
					else
					{
						if(st[flag].visit^st[index].visit)
						{
							isSolve = true;
							printf("%d\n",st[index].step+st[flag].step);
						}
					}
				}
			}
			q.pop();
		}
		while(!q.empty())q.pop();
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,count);
	}
	system("pause");
        return 0;
}

四、A*

主要有两种可行的启示函数 :出如今教科书上当典型的不在位数(difference) ,以及曼哈顿路径长(manhattan).

在节点中。加一个int型变量存储此节点的估价。此时的open表次用优先级队列存储。事实上质上是一个小顶堆,这样每一次调整的复杂度将为logn。能更快的得到离目标态近期的节点进行扩展。因为每次扩展的都是离目标近期的节点。所以时间效率有所提高。可是若启示函数效率不高,降低的扩展节点的时间可能还不足以抵过小顶堆调整的时间,结果就是时间效率可能比普通的bfs还差。

代码:

基于不在位的启示方式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos,step;
	int f;
}st[MAXN],source,target;
int temp[10],tar[10],sou[10];
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int num;
int h(state a)
{
	int temp = target.sta;
	int cnt=0;
	for(int i = 0;i < 9; i ++)
	{
		if(a.pos==target.pos)
		{
			if(!(((temp>>(3*i))&7)^((a.sta>>(3*i))&7)))
				cnt++;
		}
		else
		{
			if((!(((temp>>(3*i))&7)^((a.sta>>(3*i))&7)))&&((a.sta>>(3*i))&7))
				cnt++;
		}
	}
	return 9-cnt;
}
struct cmp
{
	bool operator () (int u, int v)
	{
		return st[u].f > st[v].f;		
	}
};
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
int search(state a)
{
	int index = a.sta%MAXN;
	bool flag = true;
	while(flag)
	{
		if(!st[index].sta)
		{
			st[index].sta = a.sta;
			st[index].pos = a.pos;
			flag = false;
		}
		else if(!(st[index].sta^a.sta)&&!(st[index].pos^a.pos))
			flag = false;
		else
			index = (index+1)%MAXN;
	}
	return index;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		memset(st,0,sizeof(st));
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		convert(sou,source);
		start = clock();
		i = search(source);
		st[i].f = h(st[i]);
		priority_queue<int,vector<int>,cmp>q;
		q.push(i);
		int index;
		int count = 0;
		while(!q.empty())
		{               
			count++;
			index = q.top();
			q.pop();				//!!!!
			if(!(st[index].sta^target.sta)&&st[index].pos == target.pos)
			{
				printf("%d\n",st[index].step);
				break;
			}
			for(int j = 0; j < 4; j ++)
			{
				if(d[st[index].pos][j])
				{
					int flag = search(exchange(st[index],d[st[index].pos][j]));
					if(!st[flag].step||st[flag].step > st[index].step + 1)
					{
						st[flag].step = st[index].step + 1;	
						st[flag].f = st[flag].step + h(st[flag]);
						q.push(flag);
					}	
				}
			}
		}
		while(!q.empty())q.pop();
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,count);
	}
	system("pause");
        return 0;
}

基于manhattan距离启示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos,step;
	int f;
}st[MAXN],source,target;
int temp[10],tar[10],sou[10];
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int num;
int manhattan[10][10] = //第i个数及其所处不同位置的Manhattan路径长度
{
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1, 0, 1, 2, 1, 2, 3, 2, 3, 4},
{-1, 1, 0, 1, 2, 1, 2, 3, 2, 3},
{-1, 2, 1, 0, 3, 2, 1, 4, 3, 2},
{-1, 1, 2, 3, 0, 1, 2, 1, 2, 3},
{-1, 2, 1, 2, 1, 0, 1, 2, 1, 2},
{-1, 3, 2, 1, 2, 1, 0, 3, 2, 1},
{-1, 2, 3, 4, 1, 2, 3, 0, 1, 2},
{-1, 3, 2, 3, 2, 1, 2, 1, 0, 1},
{-1, 4, 3, 2, 3, 2, 1, 2, 1, 0}

};
int h(state a)
{
	int cnt=0;
	for(int i = 0;i < 9; i ++)
	{
		if(a.pos != i)
			cnt += manhattan[((a.sta>>(3*(8-i)))&7)+1][i+1];
	}
	return cnt;
}
class cmp
{
      public:
	bool operator () (int u, int v)
	{
		return st[u].f > st[v].f;		
	}
};
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
int search(state a)
{
	int index = a.sta%MAXN;
	bool flag = true;
	while(flag)
	{
		if(!st[index].sta)
		{
			st[index].sta = a.sta;
			st[index].pos = a.pos;
			flag = false;
		}
		else if(!(st[index].sta^a.sta)&&!(st[index].pos^a.pos))
			flag = false;
		else
			index = (index+1)%MAXN;
	}
	return index;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		memset(st,0,sizeof(st));
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		convert(sou,source);
		start = clock();
		i = search(source);
		st[i].f = h(st[i]);
		priority_queue<int,vector<int>,cmp>q;
		q.push(i);
		int index;
		int count = 0;
		while(!q.empty())
		{               
			count++;
			index = q.top();
			q.pop();				//!!!!
			if(!(st[index].sta^target.sta)&&st[index].pos == target.pos)
			{
				printf("%d\n",st[index].step);
				break;
			}
			for(int j = 0; j < 4; j ++)
			{
				if(d[st[index].pos][j])
				{
					int flag = search(exchange(st[index],d[st[index].pos][j]));
					if(!st[flag].step||st[flag].step > st[index].step + 1)
					{
						st[flag].step = st[index].step + 1;	
						st[flag].f = st[flag].step + h(st[flag]);
						q.push(flag);
					}	
				}
			}
		}
		while(!q.empty())q.pop();
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,count);
	}
	system("pause");
        return 0;
}

五、IDA*

因为普通的深搜在此问题上,要么搜索到错误的结果,要么须要搜索全部的状态。才干确定是否是最优,故在这里使用IDA*。

IDA*是一种迭代加深的深度搜索,若在此深度下没有搜到目标点,则将深度加一又一次搜索。

无须状态判重,无需估价排序,用不到哈希表。堆上也不必应用,空间需求变的超级少,实现也最简单。

在深搜过程中,依据启示函数做剪枝。能够使效率达到非常高。

另外在求路径的时候,IDA*也是最方便的。

基于不在位启示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos;
}source,target;
int temp[10],tar[10],sou[10];
int pathLimit;
int cnt;
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int h(state a)
{
	int temp = target.sta;
	int cnt=0;
	for(int i = 0;i < 9; i ++)
	{
		if(a.pos==target.pos)
		{
			if(!(((temp>>(3*i))&7)^((a.sta>>(3*i))&7)))
				cnt++;
		}
		else
		{
			if(!(((temp>>(3*i))&7)^((a.sta>>(3*i))&7))&&((a.sta>>(3*i))&7))
				cnt++;
		}
	}
	return 9-cnt;
}
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
bool IDAStar(state &a,int depth,int diff,int prepos)
{
	cnt++;
	if(!(a.sta^target.sta)&&a.pos == target.pos)
	{
		printf("%d\n",depth);
		return true;
	}
	if(depth >= pathLimit) return false;
	if( depth + diff > pathLimit ) return false;  
	for(int j = 0; j < 4; j ++)
	{
		if(d[a.pos][j] == prepos+1) continue;
		if(d[a.pos][j])
		{
			state next = exchange(a,d[a.pos][j]);
			if(IDAStar(next,depth+1, h(next),a.pos))
				return true;
		}
	}
	return false;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	int diff = 0;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		start = clock();
		cnt = 0;
		convert(sou,source);
		pathLimit = h(source);
		diff = pathLimit;
		while(!IDAStar(source,0,diff,-1))pathLimit++; 
		end = clock();
		printf("Time:%dms\nstate number:%d\n",end-start,cnt);
	}
	system("pause");
        return 0;
}

基于manhattan距离启示:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <queue>
#define FUCK puts("fuck!")
#define STOP system("pause")
#define MAXN 388211
using namespace std;
struct state{
	int sta,pos;
}source,target;
int temp[10],tar[10],sou[10];
int pathLimit;
int d[9][4]={{0,4,0,2},{0,5,1,3},{0,6,2,0},{1,7,0,5},{2,8,4,6},{3,9,5,0},{4,0,0,8},{5,0,7,9},{6,0,8,0}};
int manhattan[10][10] = //第i个数及其所处不同位置的Manhattan路径长度
{
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1},
{-1, 0, 1, 2, 1, 2, 3, 2, 3, 4},
{-1, 1, 0, 1, 2, 1, 2, 3, 2, 3},
{-1, 2, 1, 0, 3, 2, 1, 4, 3, 2},
{-1, 1, 2, 3, 0, 1, 2, 1, 2, 3},
{-1, 2, 1, 2, 1, 0, 1, 2, 1, 2},
{-1, 3, 2, 1, 2, 1, 0, 3, 2, 1},
{-1, 2, 3, 4, 1, 2, 3, 0, 1, 2},
{-1, 3, 2, 3, 2, 1, 2, 1, 0, 1},
{-1, 4, 3, 2, 3, 2, 1, 2, 1, 0}

};
int h(state a)
{
	int cnt=0;
	for(int i = 0;i < 9; i ++)
	{
		if(a.pos != i)
			cnt += manhattan[((a.sta>>(3*(8-i)))&7)+1][i+1];
	}
	return cnt;
}
int convert(int a[],state &b)
{
	b.sta=0;
	for(int i = 0; i < 9; i ++) 
	{	
		if(a[i]!=0)
			b.sta |=((a[i]-1)<<(24-i*3));
		else
		{
			b.pos = i;
			b.sta |=(a[i]<<(24-i*3));
		}
	}
	return 1;
}
state exchange(state a,int pos)
{
	int temp = 7<<((9-pos)*3);
	state s;
	s.sta = a.sta;
	temp = temp & a.sta;
	temp = ((temp>>((9-pos)*3))<<((9-a.pos-1)*3));
	s.sta |= temp;
	s.sta &= ~(7<<((9-pos)*3));
	s.pos = pos-1;
	return s;
}
bool IDAStar(state &a,int depth,int diff,int prepos)
{
	if(!(a.sta^target.sta)&&a.pos == target.pos)
	{
		printf("%d\n",depth);
		return true;
	}
	if(depth > pathLimit) return false;
	if( depth + diff > pathLimit ) return false;  
	for(int j = 0; j < 4; j ++)
	{
		if(d[a.pos][j] == prepos+1) continue;
		if(d[a.pos][j])
		{
			state next = exchange(a,d[a.pos][j]);
			if(IDAStar(next,depth+1, h(next),a.pos))
				return true;
		}
	}
	return false;
}
int main()
{
	freopen("in.txt","r",stdin);
	clock_t start,end;
	int diff = 0;
	for(int j=0;j<8;j++)temp[j] = j+1;
	temp[8]=0;
	convert(temp,target);
	while(1)
	{
		int i = 0;
		char ch;
		while((ch=getchar())!='\n')
		{
			if(ch<='9'&&ch>='0')
				sou[i++] = ch - '0';
			else if(ch=='x')
				sou[i++] =0;
		}
		start = clock();
		convert(sou,source);
		pathLimit = h(source);
		diff = pathLimit;
		while(!IDAStar(source,0,diff,-1))pathLimit++; 
		end = clock();
		printf("Time:%dms\ndepthlimit:%d\n",end-start,pathLimit);
	}
	system("pause");
        return 0;
}

六、其它优化

状态还能够压缩到一个int上。全然採用位运算来完毕。

前面的hash函数还能够继续优化,对于全排列有一种很好的hash哈希函数叫康托展开。

首先看几个康托展开的实例(9的全排列):

1 2 3 4 5 6 7 8 9——展开为 0。

1 2 3 4 5 6 7 9 8——展开为 1。

1 2 3 4 5 6 8 7 9——展开为 2。

由这些最開始的方法我们能够发现一个规律:从第一个数開始,依次推断推断这些数是当前没有出现过的数的第几个(由0開始)。记为a1, a2, … ,a(n – 1)。

不难发现如1 2 3 4 5 6 8 7 9,由1至6都是当前没有出现过的第0个数。而8是7,8,9中的第1个(由0開始),9是7,9中的第1个。7是第0个。故a1 = a2 = … = a6 = 0,a7 = 1,a8 = 1,a9 =0。

之后排列数(康托展开的值)等于

a1 * (n – 1)! + a2 * (n – 2)! + … + ak *(n – k)! + … + an * 0!

再举几个样例:

3 5 7 4 1 2 9 6 8——展开为 98884。

5 6 7 8 1 2 3 4 9——展开为 184800。

往回转换也非常easy,分步取模就能够了,在此就不赘述了。

七、总结

BFS

BFS+HASH

DBFS

A*(diff)

A*(man)

IDA*(diff)

IDA*(man)

8672543×1(31)

210ms

102ms

6ms

322ms

31ms

1152ms

7ms

181440

181440

10034

147574

12290

64785×321(31)

216ms

104ms

7ms

330ms

20ms

1248ms

8ms

181441

181441

10321

143918

7567

8461375×2(27)

204ms

95ms

2ms

169ms

13ms

156ms

5ms

174213

174213

4115

68678

5595

为了更直观的控制结果,做一个简单的MFC程序显示结果。

以上是搜索时间。以下是国家在搜索数量。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/117777.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021年12月,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
MySQL中日期和时间函数学习--MySql语法
下面的例子使用了时间函数。以下询问选择了最近的 30天内所有带有date_col 值的记录:
用户1289394
2021/07/09
1.9K0
MySQL函数大全及用法示例(三)
dayofweek(date) 返回日期date是星期几(1=星期天,2=星期一,……7=星期六,odbc标准) mysql> select dayofweek('1998-02-03');   -> 3 weekday(date) 返回日期date是星期几(0=星期一,1=星期二,……6= 星期天)。 mysql> select weekday('1997-10-04 22:23:00');   -> 5 mysql> select weekday('1997-11-05');   -> 2 dayofmonth(date) 返回date是一月中的第几日(在1到31范围内) mysql> select dayofmonth('1998-02-03');   -> 3 dayofyear(date) 返回date是一年中的第几日(在1到366范围内) mysql> select dayofyear('1998-02-03');   -> 34 month(date) 返回date中的月份数值 mysql> select month('1998-02-03');   -> 2 dayname(date) 返回date是星期几(按英文名返回) mysql> select dayname("1998-02-05");   -> 'thursday' monthname(date) 返回date是几月(按英文名返回) mysql> select monthname("1998-02-05");   -> 'february' quarter(date) 返回date是一年的第几个季度 mysql> select quarter('98-04-01');   -> 2 week(date,first) 返回date是一年的第几周(first默认值0,first取值1表示周一是 周的开始,0从周日开始) mysql> select week('1998-02-20');   -> 7 mysql> select week('1998-02-20',0);   -> 7 mysql> select week('1998-02-20',1);   -> 8 year(date) 返回date的年份(范围在1000到9999) mysql> select year('98-02-03');   -> 1998 hour(time) 返回time的小时数(范围是0到23) mysql> select hour('10:05:03');   -> 10 minute(time) 返回time的分钟数(范围是0到59) mysql> select minute('98-02-03 10:05:03');   -> 5 second(time) 返回time的秒数(范围是0到59) mysql> select second('10:05:03');   -> 3 period_add(p,n) 增加n个月到时期p并返回(p的格式yymm或yyyymm) mysql> select period_add(9801,2);   -> 199803 period_diff(p1,p2) 返回在时期p1和p2之间月数(p1和p2的格式yymm或yyyymm) mysql> select period_diff(9802,199703);   -> 11 date_add(date,interval expr type) date_sub(date,interval expr type) adddate(date,interval expr type) subdate(date,interval expr type) 对日期时间进行加减法运算 (adddate()和subdate()是date_add()和date_sub()的同义词,也 可以用运算符+和-而不是函数 date是一个datetime或date值,expr对date进行加减法的一个表 达式字符串type指明表达式expr应该如何被解释  [type值 含义 期望的expr格式]:  second 秒 seconds
哲洛不闹
2018/09/14
8730
MySQL日期和时间函数汇总
同一个日期时间会有多种不同的表示方式,有的时候需要在不同格式之间相互转换。在MySQL中用的是date_format()函数:
三分恶
2020/12/11
3.7K0
小白博客 MySQL日期时间函数大全
DAYOFWEEK(date) 返回日期date是星期几(1=星期天,2=星期一,……7=星期六,ODBC标准) mysql> select DAYOFWEEK('1998-02-03'); -> 3 WEEKDAY(date) 返回日期date是星期几(0=星期一,1=星期二,……6= 星期天)。 mysql> select WEEKDAY('1997-10-04 22:23:00'); -> 5 mysql> select WEEKDAY('1997-11-05'); -> 2 DAYOFMO
奶糖味的代言
2018/04/11
1.8K0
MySQL 日期函数大全(更新中.....)
解析:以年-月-日这种格式输出。%r代码am还是pm。am表示凌晨和上午,pm表示下午和晚上。(0:00-12:00)。
贵哥的编程之路
2022/11/16
4.1K0
MySQL 日期函数大全(更新中.....)
【mysql】日期和时间函数
GET_FORMAT函数中date_type和format_type参数取值如下:
兮动人
2022/03/15
4.9K0
细节、MYSQL_DATE_FORMAT()_函数_详解(记得收藏)
(下一篇) 16 条 yyds 的代码规范 40 个 SpringBoot 常用注解
全栈程序员站长
2022/09/18
2.5K0
细节、MYSQL_DATE_FORMAT()_函数_详解(记得收藏)
Mysql 中的日期时间函数汇总
MySQL中内置了大量的日期和时间函数,能够灵活、方便地处理日期和时间数据,本节就简单介绍一下MySQL中内置的日期和时间函数。
跟着飞哥学编程
2023/02/10
18.7K0
Mysql 中的日期时间函数汇总
mysql时间按小时格式化_mysql时间格式化,按时间段查询的MySQL语句[通俗易懂]
下表显示了type和expr参数怎样被关联:type值 含义 期望的expr格式SECOND秒SECONDS
全栈程序员站长
2022/07/28
6.7K0
MySQL的日期时间计算速查表
最近写个SQL逻辑,涉及到计算各种日期和时间,MySQL提供了很丰富的函数来支持,记录一下,用的时候,有地方可查。
bisal
2023/04/08
1.9K0
mysql计算时间
一、MySQL 获得当前日期时间 函数 1.1 获得当前日期+时间(date + time)函数:now() mysql> select now(); +---------------------+ | now() | +---------------------+ | 2008-08-08 22:20:46 | +---------------------+ 除了 now() 函数能获得当前的日期时间外,MySQL 中还有下面的函数: current_timestamp() ,current_timestamp ,localtime() ,localtime ,localtimestamp -- (v4.0.6) ,localtimestamp() -- (v4.0.6) 这些日期时间函数,都等同于 now()。鉴于 now() 函数简短易记,建议总是使用 now() 来替代上面列出的函数。 1.2 获得当前日期+时间(date + time)函数:sysdate() sysdate() 日期时间函数跟 now() 类似,不同之处在于:now() 在执行开始时值就得到了, sysdate() 在函数执行时动态得到值。看下面的例子就明白了: mysql> select now(), sleep(3), now(); +---------------------+----------+---------------------+ | now() | sleep(3) | now() | +---------------------+----------+---------------------+ | 2008-08-08 22:28:21 | 0 | 2008-08-08 22:28:21 | +---------------------+----------+---------------------+ mysql> select sysdate(), sleep(3), sysdate(); +---------------------+----------+---------------------+ | sysdate() | sleep(3) | sysdate() | +---------------------+----------+---------------------+ | 2008-08-08 22:28:41 | 0 | 2008-08-08 22:28:44 | +---------------------+----------+---------------------+ 可以看到,虽然中途 sleep 3 秒,但 now() 函数两次的时间值是相同的; sysdate() 函数两次得到的时间值相差 3 秒。MySQL Manual 中是这样描述 sysdate() 的:Return the time at which the function executes。 sysdate() 日期时间函数,一般情况下很少用到。 2. 获得当前日期(date)函数:curdate() mysql> select curdate(); +------------+ | curdate() | +------------+ | 2008-08-08 | +------------+ 其中,下面的两个日期函数等同于 curdate(): current_date() ,current_date 3. 获得当前时间(time)函数:curtime() mysql> select curtime(); +-----------+ | curtime() | +-----------+ | 22:41:30 | +-----------+ 其中,下面的两个时间函数等同于 curtime(): current_time() ,current_time 4. 获得当前 UTC 日期时间函数:utc_date(), utc_time(), utc_timestamp() mysql> select utc_timestamp(), utc_date(), utc_time(), now() +---------------------+------------+------------+---------------------+ | utc_timestamp() | utc_date() | utc_time() | now() | +---------------------+------------+------------+----------
王念博客
2019/07/24
4.9K0
mysql函数
四、日期和时间函数 //返回当前的日期 curdate()或current_date() select curdate(); // 2014-12-05 select current_date() // 2014-12-05 //返回当前的时间 curtime()或current_time() select curtime() // 12:00:00 select current_time() // 12:00:00 //返回日期date加上间隔时间int的结果(int必须按照关键字进行格式
wangxl
2018/03/07
3.5K0
MySQL 获得当前日期时间(以及时间的转换)。[通俗易懂]
转载:http://blog.sina.com.cn/s/blog_6d39dc6f0100m7eo.html
全栈程序员站长
2022/11/10
5.3K0
MySql时间处理函数的学习与实践
日常业务开发中,我们经常需要跟SQl的日期打交道,比如查询最近30天的订单,查询某一个月的订单量,统计某天每小时的下单量等等,于是整理了以下MySql时间处理函数。
捡田螺的小男孩
2020/04/15
1.2K0
MySql时间处理函数的学习与实践
MySQL函数及用法示例(收藏大全)
1、字符串函数 ascii(str) 返回字符串str的第一个字符的ascii值(str是空串时返回0) mysql> select ascii('2');   -> 50 mysql> select ascii(2);   -> 50 mysql> select ascii('dete');   -> 100
java思维导图
2018/08/16
7890
MySQL单行函数详解
函数在计算机语言的使用中贯穿始终,函数的作用是什么呢?它可以把我们经常使用的代码封装起来,需要的时候直接调用即可。这样既提高了代码效率,又提高了可维护性。在 SQL 中我们也可以使用函数对检索出来的数据进行函数操作。使用这些函数,可以极大地提高用户对数据库的管理效率。
timerring
2023/02/16
1.3K0
MySQL单行函数详解
盘点MySQL中常用的函数
在平常使用MySQL的过程中,我们常常会使用到其中的函数。有些函数常用,就会非常熟悉,但有些不经常使用就会十分生疏。
半月无霜
2023/03/03
6460
MySQL 常用时间范围查询SQL样例
特殊说明: 第三方平台不会及时同步本文章最新内容,如果觉得本文资料不全,可以访问本人Java博客搜索:标题类似的关键字 上述文章均是我实际操作后产出,烦请各位,请勿直接盗用!转载记得标注原文链接:www.zanglikun.com
收心
2023/02/22
2.5K0
mysql中关于时间统计的sql语句总结
在之前写VR360时有一个统计页面(https://vr.beifengtz.com/p/statistics.html),在此页面的数据统计时用到了很多mysql中日期函数和时间统计sql语句,当时也是参考了一些资料才写出来的。在平时开发中,涉及到统计数据、报表甚至大数据计算时一定会使用这些日期函数,其他关系数据库也是类似的,我是以mysql为例,比较简单还免费嘛。话不多说,下面直接列出常用的时间统计sql语句,记录下来方便以后学习巩固。
beifengtz
2019/06/03
3.6K0
玩转Mysql系列 - 第10篇:常用的几十个函数详解
如果忽略mode参数,默认情况下WEEK函数将使用default_week_format系统变量的值。 要获取default_week_format变量的当前值,请使用SHOW VARIABLES语句如下:
路人甲Java
2019/09/18
3.1K0
相关推荐
MySQL中日期和时间函数学习--MySql语法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验