{

这一节继续对Pku1077进行讨论

附带在Zju1217上的测试

Zju1217数据更强 还是多测 比较能反映问题

}

8数码问题另一种比较优秀的算法是A*算法

A*算法是一种基于贪心思想的搜索算法

A*算法总能在访问较少的节点的同时找到一组较优解


我们搜索总是在遍历一颗搜索树

譬如BFS就是进行层次遍历 DFS是先序遍历

BFS优先选择深度低的节点扩展 DFS优先选择深度高的扩展

我们运用BFS解决8数码问题是因为我们要求得步数最少的方案

BFS优先选择深度低的节点扩展 正好符合我们对于解的优劣的判断

正因为如此 找到的第一个解就是最优解

事实上仅仅用深度判断优劣是远远不够的

我们可以构造启发函数来判别一个状态的优劣

优先扩展优秀的节点 以便更快地得到一组较优解

这就是A*算法的基本内容

举个例子 1 2 3 4 5 6 7 x 8 显然优于 8 7 6 5 4 3 2 1 x

经过启发函数的计算 我们应当比较出前者的

然而这只是我们的主观感受

我们必须对其量化

启发函数的作用是估计一个节点到目标节点的距离 再加上节点深度

就可以大致了解到当前状态的优劣

"估计距离"并非真的要算出节点之间的距离

可以用其他的一些函数来判断到底离目标节点近还是远

由于只是估计 所以启发函数是有好有坏的

好的启发函数让你很快搜到解 还能是最优解

坏的启发函数只会让你陷入泥沼 不能自拔

设计一个好的启发函数是A*算法的关键

 

对于8数码问题 启发函数有很多种

很直观地 我们可以把在目标位置上的数的个数作为启发函数

这个启发函数还是有点简陋

我们可以很容易构造出一个"陷阱": 8 2 3 4 5 6 7 1 x

看起来有6个数已经归位了 但是这却是一个需要走很多步的状态

更好的启发函数是把各个数与目标位置的曼哈顿距离累加来确定状态的启发值

经过测试这样的启发函数往往可以帮助我们在第一次得到解时就能是最优解

可以说 这个启发函数很优秀

 

具体实现时 我们需要使用一些数据结构来存储搜索出的状态

  *用一张表{q:array of string[9];prev,dep,ans,b:array of longint}来保存所有的状态

  *用Hash表{Hash,tab,nxt:array of longint}和BKDRhash函数来判重

  *用堆来实现优先队列保存需要扩展的节点和节点的启发值{H,f:array of longint}

具体算法不是很难

  和BFS类似 只不过我们不是取深度最小的节点扩展

  *而是取出并删除优先级最高的节点(堆顶)扩展

  *然后将扩展出的不重复节点放入堆和表中 记录hash值

  *找到一个解后直接递归输出解 尽管可能不是最优解

  但是当启发函数设计得好时 往往就是一个最优解

 

给出一些测试结果

Pku的数据很弱 用A*或是双向宽搜都是16ms

而且Pku测时测内存都不准

7593602 masterchivu 1077 Accepted 5420K 16MS Pascal 2895B 2010-09-08 23:47:24

贴个小号的A*结果吧

相比之下 Zju的测试很好很强大

(注意是多测)

我测了一下双向和A*

2294775 2010-09-09 21:23:53 Accepted 1217 FPC 5980 12328 Master_Chivu
2294769 2010-09-09 21:18:00 Accepted 1217 FPC 5160 11544 Master_Chivu

其实差不多 因为A*在维护堆的时候要log2N的复杂度

这里有个很好的测试分析 http://wenku.baidu.com/view/d18a5a0e7cd184254b353598.html

相当形象

相同条件下 裸BFS要扩展出7w个节点

双向BFS只要1000+个 A*只要500+个

足以见得孰优孰劣

最后贴一个A*的代码 写的很丑 没有写过程 全堆在main里了

 

1 {$I+,Q+,R+,S+}
2  const maxq=200000;
3 base=$FFFFF;
4 goal='12345678x';
5 gx:array['1'..'8']of longint=(1,2,3,4,5,6,7,8);
6 wx:array[1..9]of longint=(1,1,1,2,2,2,3,3,3);
7 wy:array[1..9]of longint=(1,2,3,1,2,3,1,2,3);
8 dx:array[1..4]of longint=(1,3,-1,-3);
9 d:array[1..4]of char=('r','d','l','u');
10  var q:array[1..maxq]of string[9];
11 prev,dep,ans,b,tab,nxt,h,f:array[1..maxq]of longint;
12 hash:array[0..base]of longint;
13 u,qs,hs,tt,ans0,x,i,j,p,tx,temp:longint;
14 flag:boolean;
15 ch:char;
16  procedure out(x:longint);
17  begin
18  if x=0 then exit;
19 out(prev[x]);
20  if ans[x]<>0 then write(d[ans[x]]);
21  end;
22  procedure work;
23  begin
24 i:=0;
25 q[1]:=''; qs:=1;
26  while not eoln do
27 begin
28 read(ch);
29 if ch=' ' then continue;
30 q[1]:=q[1]+ch;
31 inc(i);
32 if ch='x' then b[1]:=i;
33 end;
34 readln;
35 u:=0;
36  for i:=1 to 9 do
37 for j:=1 to i-1 do
38 if (q[1][i]<>'x')and(q[1][j]<>'x')and(q[1][j]<q[1][i])
39 then inc(u);
40  if odd(u)
41 then begin
42 writeln('unsolvable');
43 exit;
44 end;
45 f[1]:=0;
46 h[1]:=1; hs:=1;
47  for j:=1 to 9 do
48 if q[1][j]<>'x'
49 then f[1]:=f[1]+abs(wx[j]-wx[gx[q[1][j]]])
50 +abs(wy[j]-wy[gx[q[1][j]]]);
51 ans0:=0;
52  for i:=1 to 9 do
53 ans0:=(ans0*131+ord(q[1][i]))and base;
54 fillchar(hash,sizeof(hash),0);
55 tab[1]:=1; hash[ans0]:=1; tt:=1;
56 nxt[1]:=0;
57 while hs>0 do
58 begin
59 x:=h[1];
60 h[1]:=h[hs]; f[1]:=f[hs];
61 dec(hs);
62 i:=1;
63 while true do
64 begin
65 if i shl 1>hs then break;
66 if (i shl 1+1>hs)or(f[i shl 1]<f[i shl 1+1])
67 then if f[i shl 1]<f[i]
68 then begin
69 temp:=h[i]; h[i]:=h[i shl 1]; h[i shl 1]:=temp;
70 temp:=f[i]; f[i]:=f[i shl 1]; f[i shl 1]:=temp;
71 i:=i shl 1;
72 end
73 else break
74 else if f[i shl 1+1]<f[i]
75 then begin
76 temp:=h[i]; h[i]:=h[i shl 1+1]; h[i shl 1+1]:=temp;
77 temp:=f[i]; f[i]:=f[i shl 1+1]; f[i shl 1+1]:=temp;
78 i:=i shl 1+1;
79 end
80 else break;
81 end;
82 for i:=1 to 4 do
83 begin
84 tx:=b[x]+dx[i];
85 if (b[x]=3)and(i=1)or(b[x]=6)and(i=1)
86 or(b[x]=4)and(i=3)or(b[x]=7)and(i=3)
87 or(tx>9)or(tx<1)
88 then continue;
89 inc(qs);
90 q[qs]:=q[x];
91 q[qs][b[x]]:=q[x][tx];
92 q[qs][tx]:='x';
93 if q[qs]=goal
94 then begin
95 prev[qs]:=x; ans[qs]:=i;
96 out(qs); writeln;
97 exit;
98 end;
99 ans0:=0;
100 for j:=1 to 9 do
101 ans0:=(ans0*131+ord(q[qs][j]))and base;
102 flag:=false;
103 p:=hash[ans0];
104 while p<>0 do
105 begin
106 if q[tab[p]]=q[qs] then begin flag:=true; break; end;
107 p:=nxt[p];
108 end;
109 if flag
110 then dec(qs)
111 else begin
112 prev[qs]:=x;
113 dep[qs]:=dep[x]+1;
114 ans[qs]:=i;
115 b[qs]:=tx;
116 inc(tt); tab[tt]:=qs; nxt[tt]:=hash[ans0];
117 hash[ans0]:=tt;
118 inc(hs);
119 h[hs]:=qs; f[hs]:=dep[qs];
120 for j:=1 to 9 do
121 if q[qs][j]<>'x'
122 then f[hs]:=f[hs]+abs(wx[j]-wx[gx[q[qs][j]]])
123 +abs(wy[j]-wy[gx[q[qs][j]]]);
124 j:=hs;
125 while j>1 do
126 if f[j shr 1]>f[j]
127 then begin
128 temp:=f[j]; f[j]:=f[j shr 1]; f[j shr 1]:=temp;
129 temp:=h[j]; h[j]:=h[j shr 1]; h[j shr 1]:=temp;
130 j:=j shr 1;
131 end
132 else break;
133 end;
134 end;
135 end;
136 end;
137 begin
138 assign(input,'eight.in'); reset(input);
139 assign(output,'eight.out'); rewrite(output);
140 while not eof do work;
141 close(input); close(output);
142 end.
143

 

A*很好很强大 我们都要学习他

 

BOB HAN原创 转载请注明出处

相关文章: