对于当前状态(f,p)做了启发函数,由U=depth*factor1+getU(f,p)*factor2;
得出节点的U,始终先搜索U较小的情况
虽说老老实实 U=depth+HL(f,p) (HL为曼哈顿距离+线性冲突*2) 可以理论上比较容易地证明得出最优解,而且也能对付一些比较简单的情况,然而我并没有这么做, 在一系列疯狂凑参数+瞎几把优化后,竟然得出了非常实用的伪劣A*解
factor根据需求可调整为
21:11(求最优解,在70步内未发现反例,建议使用) ,但一些70步以上的case可以说是GG了,在luogu上我发了个个人题,放了7个随机中前30%难(按我的代码运行时间排)的case,和1个偶然找到的虽然只有55步,但可以让基于哈密顿距离的启发函数表现很差的case, 还有1个70步case (基本上由随机+向步数增加方向移动是到不了70步的),表现是(用21:11), 7.52ms/181.45MB, 耗时最长为55步的case 2.6s.
2:1 且修改fmp
为0,2,4,6,2,4,6,8…, (严格求最优解,效率感人)
17:10 (求大部分情况的最优解,对于有较小可能性多2步)
3:2 (很快速求解,目前没发现超过600ms的情况,可能会多2-6步)
目前我的方式是 21:11达到限制后转 17:10
对于15 14 9 13 11 10 0 12 7 6 5 8 3 1 2 4, 17:10的结果是75,不知道对不对。
也可以在hashset done
中加上步数信息以回溯得知具体解。
也许你会想:A*的内存不会炸?
那我只能表示,用int64存一个状态足矣
还有,启发函数用啥?
玄学魔改哈密顿距离+伪劣版线性冲突你值得拥有
下面是15Puzzle模拟器并加上了求解功能:
可在Windows或C4droid 由GCC编译 (C++11 以上)
操作: WSAD/2468移动,0/r随机打乱,1/h求解(只有长度),3/i开启输入
#define WINDOWS
#ifdef WINDOWS
#include
#endif
#include
#include
#include
#include
#include
#include
#include
/*
3 11 6 0 2 1 9 8 4 7 14 5 15 10 12 13 55
15 14 9 13 11 10 0 12 7 6 5 8 3 1 2 4 75??
*/
using namespace std;
typedef unsigned long long U64;
const U64* base=[]() {
U64 *b=new U64[16] {15};
for(int i=1; i<16; ++i) {
b[i]=(b[i-1]<<4);
}
return b;
}();
const U64 *rbase=[]() {
U64 *rb=new U64[16];
for(int i=0; i<16; ++i) rb[i]=~base[i];
return rb;
}();
U64 ac=0xfedcba9876543210llu;
const char U_=0,D_=1,L_=2,R_=3,RANDF=17,NEXTI=18,IMPORT=19,ILLACT=-1;
#define REV(DR) ((DR)^1)
#define U_ABLE(P) ((P)&12)
#define U_P(P) ((P)-4)
#define D_ABLE(P) ((P)<12)
#define D_P(P) ((P)+4)
#define L_ABLE(P) ((P&3))
#define L_P(P) ((P)-1)
#define R_ABLE(P) (~(P)&3)
#define R_P(P) ((P)+1)
#define up(F,P) ((rbase[P]|(F<<16))&F|base[P-4])
#define down(F,P) ((rbase[P]|(F>>16))&F|base[P+4])
#define left(F,P) ((rbase[P]|(F<<4))&F|base[P-1])
#define right(F,P) ((rbase[P]|(F>>4))&F|base[P+1])
const int fmp[4][4]= {{0,2,4,7},
{2,4,6,10},
{4,6,9,12},
{7,10,12,15}
};
int factor1=21,factor2=11;
int next_factor1=17, next_factor2=10;
const size_t MAXSIZE=20000000;
void output(U64 o) {
for(int i=0; i<4; ++i) {
for(int j=0; j<4; ++j) {
printf(" %02u",unsigned((o+1)&15));
o>>=4;
}
printf("\n\n");
}
}
char conv(char act) {
if(act=='2'||act=='w') return U_;
if(act=='8'||act=='s') return D_;
if(act=='4'||act=='a') return L_;
if(act=='6'||act=='d') return R_;
if(act=='r'||act=='0') return RANDF;
if(act=='h'||act=='1') return NEXTI;
if(act=='i'||act=='3') return IMPORT;
return ILLACT;
}
bool act(U64& f,char& p,char act) {
if(act==U_&&U_ABLE(p)) {
f=up(f,p);
p=U_P(p);
} else if(act==D_&&D_ABLE(p)) {
f=down(f,p);
p=D_P(p);
} else if(act==L_&&L_ABLE(p)) {
f=left(f,p);
p=L_P(p);
} else if(act==R_&&R_ABLE(p)) {
f=right(f,p);
p=R_P(p);
} else return false;
return true;
}
U64 xtime() {
static U64 t=0;
U64 r=clock()-t;
t+=r;
return r;
}
void randmove(U64& f,char& p,int step=1) {
while (step--) {
act(f,p,rand()&3);
}
}
int calculateLinear(U64 f) {
int count = 0,v;
for(int i=0; i<16; i+=4) {
v=0;
for(int j=i; j<i+4; ++j) {
int now=((f>>(j*4))&15);
if((now&12)==i&&now!=15){
count+=(now<v);
v=now;
}
}
}
for(int i=0; i<4; i++) {
v=0;
for(int j=i; j<=i+12; j+=4) {
int now=((f>>(j*4))&15);
if((now&3)==i&&now!=15){
count+=(now<v);
v=now;
}
}
}
return 4*count;
}
int getU(U64 F,int16_t p) {
int r=calculateLinear(F)-fmp[3-(p&3)][3-(p>>2)];
U64 comp=ac;
for(int i=16; i--;) {
int a=abs((int(F)&3)-(int(comp)&3));
F>>=2;
comp>>=2;
int b=abs((int(F)&3)-(int(comp)&3));
F>>=2;
comp>>=2;
r+=fmp[a][b];
}
return r;
}
struct site {
U64 f;
int16_t depth,U,op,p;
site() {}
site(U64 gf,int gp,int dpth,int16_t o){
f=gf;
p=gp;
op=o;
depth=dpth;
}
void gU(){
U=depth*factor1+getU(f,p)*factor2;
}
};
inline bool operator <(const site& b,const site& a) {
return a.U<b.U||(a.U==b.U&&a.f<b.f);
}
unordered_set<U64> done;
site doing[MAXSIZE];
int dsz;
int solve(U64 f,int16_t p) {
done.clear();
done.reserve(MAXSIZE);
site n(f,p,0,5),k;
k.depth=0;
n.gU();
doing[0]=n;
dsz=1;
U64 last=0;
while(dsz) {
if(done.size()>=MAXSIZE) return -1;
n=doing[0];
pop_heap(doing,doing+dsz);
--dsz;
if(n.f==last) continue;
else last=n.f;
done.insert(n.f);
k.depth = n.depth + 1;
if (n.f == ac) break;
if (n.op != 1 && U_ABLE(n.p)) {
k.f = up(n.f, n.p);
if (done.find(k.f) == done.end()) {
k.p = U_P(n.p), k.op = 0;
k.gU();
doing[dsz++]=k;
push_heap(doing,doing+dsz);
}
}
if (n.op != 0 && D_ABLE(n.p)) {
k.f = down(n.f, n.p);
if (done.find(k.f) == done.end()) {
k.p = D_P(n.p);
k.gU();
k.op = 1;
doing[dsz++]=k;
push_heap(doing,doing+dsz);
}
}
if (n.op != 3 && L_ABLE(n.p)) {
k.f = left(n.f, n.p);
if (done.find(k.f) == done.end()) {
k.p = L_P(n.p);
k.gU();
k.op = 2;
doing[dsz++]=k;
push_heap(doing,doing+dsz);
}
}
if (n.op != 2 && R_ABLE(n.p)) {
k.f = right(n.f, n.p);
if (done.find(k.f) == done.end()) {
k.p = R_P(n.p);
k.gU();
k.op = 3;
doing[dsz++]=k;
push_heap(doing,doing+dsz);
}
}
}
printf("\n%d,%d %d\n",(int)dsz,(int)done.size(),n.depth);
fflush(stdout);
return n.depth;
}
#ifdef WINDOWS
inline void clrscr() {
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hConsole, {0,0});
for(int i=0; i<20; ++i)
puts(" ");
SetConsoleCursorPosition(hConsole, {0,0});
}
#endif
int main() {
U64 now;
char nowp;
printf("%d",(int)sizeof(site));
getche();
now = ac;
nowp = 15;
int step = 0,in;
char move;
while (1) {
clrscr();
output(now);
calculateLinear(now);
printf("step: %d rating: %d", step,getU(now,nowp));
fflush(stdout);
move = conv(getche());
if (move == RANDF) {
step = 0;
randmove(now, nowp, 666233);
} else if (move == NEXTI && now != ac) {
xtime();
if(solve(now, nowp)<0){
int tf1=factor1,tf2=factor2;
factor1=next_factor1;
factor2=next_factor2;
solve(now, nowp);
factor1=tf1;
factor2=tf2;
}
printf("%d\n",xtime());
getche();
} else if(move==IMPORT) {
now=0;
for(int i=0; i<16; ++i) {
scanf("%d",&in);
clrscr();
if(in==0) nowp=i;
now|=(((U64)(in-1)&15)<<i*4);
output(now);
fflush(stdout);
}
} else {
step+=act(now, nowp, move);
}
}
}