15Puzzle 强行A*求解 C++

对于当前状态(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);
		}
	}
}



你可能感兴趣的:(15Puzzle 强行A*求解 C++)