多种方法求解八数码问题
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同为奇数或者同为偶数,那么该状态有解,否则无解。
之后节点的存储与推断是否有解,基本同样,不再赘述。
代码:
#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表的时间降低,程序的效率得到了提升
代码:
#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;elseindex = (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;
}
三、双向广搜
接下来採用一种更高速的方式。因为目标态和初始态都已知,能够採用从两态同一时候開始搜,当搜到同一个节点时,搜索结束,将两边的步数加起来输出。在这里我在每一个节点里,用一个值标记,此节点是由哪个状态訪问的,故仅仅需用一个队列交替扩展。如图所看到的,双向广搜少扩展很多节点,时间效率得到大幅提升。
代码:
#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;elseindex = (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还差。
代码:
基于不在位的启示方式:
#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;elseindex = (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距离启示:
#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;elseindex = (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*也是最方便的。
基于不在位启示:
#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距离启示:
#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) |
|
8672543x1(31) |
210ms |
102ms |
6ms |
322ms |
31ms |
1152ms |
7ms |
181440 |
181440 |
10034 |
147574 |
12290 |
|||
64785x321(31) |
216ms |
104ms |
7ms |
330ms |
20ms |
1248ms |
8ms |
181441 |
181441 |
10321 |
143918 |
7567 |
|||
8461375x2(27) |
204ms |
95ms |
2ms |
169ms |
13ms |
156ms |
5ms |
174213 |
174213 |
4115 |
68678 |
5595 |
为了更直观的对照结果,做一个简单MFC程序来显示结果。上面是搜索时间,以下是搜索的状态数。
转载于:https://www.cnblogs.com/mengfanrong/p/4060336.html
多种方法求解八数码问题相关推荐
- 7种方法求解八数码问题
[八数码问题]//https://vijos.org/p/1360 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要 ...
- 利用Python求解八数码难题
实验目的 实验内容 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题 ...
- 基于Python实现的AStar求解八数码问题
资源下载地址:https://download.csdn.net/download/sheziqiong/86776612 资源下载地址:https://download.csdn.net/downl ...
- matlab怎么求三次微分,matlab课设三阶微分方程多种方法求解.doc
matlab课设三阶微分方程多种方法求解 目录 一.课程设计题目及意义 -------- 1 页 二.课程设计任务及要求 --------2 页 三.课程设计详细过程及结果 --------3至10页 ...
- 人工智能作业 - A*算法程序求解八数码
文章目录 A*算法 简介 思路 A*伪代码 八数码问题 题目描述 注意事项 实验过程 解决思路 伪代码 二维压缩为一维 检查是否有解 其他 代码实现 h1和h2的对比 关于曼哈顿距离 参考链接 A*算 ...
- 人工智能:(C语言)采用状态空间法求解八数码问题
实验要求:八数码难题也称九宫问题,它是在3×3的方格棋盘上,分别放置了表有数字1.2.3.4.5.6.7.8的八张牌,初始状态S0,目标状态Sg,要求程序能输入任意的初始状态和目标状态,要求通过空格来 ...
- 多种方法求解“最大公约数”和“最小公倍数”
目录 一.最大公约数 1.枚举法 2.辗转相除法 二.最小公倍数 1.枚举法 2.扩大法 Hello,大家好,我是灰小猿,一个超会写bug的程序猿. 今天在这里记录一下在程序中求解两个数的最大公约数和 ...
- c语言怎么去掉空白行,多种方法求解去掉空格 (三两行代码---适合初学者)
---------------------------------------解法一 ---------------------------------------------- 解题思路: 其实本题 ...
- 【人工智能】八数码问题:广度搜索、深度搜索
应用广度搜索BFS和深度搜索DFS解决八数码问题,广度搜索和深度搜索都是盲目搜索,相关理论知识,算法过程:问题求解:状态空间图和盲目搜索. 参考:7种方法求解八数码问题 Python实现A*算法解决N ...
最新文章
- 【Python】自定义排序函数 - 示例
- NHibernate获取实体配置信息(表名,列名等等)
- php服务器怎么保活,think-queue消息队列
- 摩托罗拉linux专属游戏,摩托罗拉E680g Linux系统手机也可以玩游戏
- lstm 做航迹预测预测_用lstm预测酒店收入的第一步
- 神经网络建模的适用范围,常用神经网络模型
- java sqlserver 图书馆管理系统_基于JAVA+SQLServer的图书馆管理系统.doc
- 英文论文写作必备网站
- java对接支付宝三方登录
- 网站维护要做哪些工作
- Arduino学习笔记:基本直流电机驱动
- 【金融项目】尚融宝项目(四)
- Linux环境下如何安装达梦数据库
- 什么是数据库“存算分离”架构?
- 【ybt高效进阶1-5-2】【luogu P3456】山峰和山谷 / GRZ-Ridges and Valleys
- 卓帆宝库分享大学生如何轻松挣钱
- 【Linux】修改腾讯云轻量应用服务器的hostname
- 微信调整小程序和公众号流量主收入分成比例
- ubuntu gcc 安装 使用
- 【css】字体渐变色,边框渐变色等笔记,按钮渐变色等持续更新~~
热门文章
- Transformer 这么强,该从何学起?
- Remix:高分辨率目标检测,面向边缘设备、可调谐
- ECCV 2020 目标检测与跟踪赛事,清华大学主办 GigaVision 2020 欢迎报名~
- CVPR 2020丨8比特数值也能训练模型?商汤提出训练加速新算法
- OpenCV4.0 快速QR二维码检测测试示例
- pycharmlinux报错java,Linux安装PyCharm时提示cannot start PyCharm错误怎么办?
- 亚马逊上海人工智能研究院:深度图模型实习生
- 笔记 | 《机器学习》中计算学习理论(下)
- 华为诺亚方舟预训练语言模型NEZHA、TinyBERT开源代码
- 卡卡半智能扫地机器人_扫地机器人哪个牌子好?精选五款高智能的扫地机器人...