第六章 回 溯 法
§ 1. 回溯法的基本思想
回溯法有“通用的解题法”之称。应用回溯法解问题时,首先应
该明确问题的解空间。一个复杂问题的解决往往由多部分构成,即,
一个大的解决方案可以看作是由若干个小的决策组成。 很多时候它们
构成一个决策序列。 解决一个问题的所有可能的决策序列构成该问题
的解空间。 解空间中满足约束条件的决策序列称为可行解。 一般说来,
解任何问题都有一个目标, 在约束条件下使目标达优的可行解称为该
问题的最优解。在解空间中,前 k 项决策已经确定的所有决策序列之
集称为 k 定子解空间。 0 定子解空间即是该问题的解空间。
例 1.旅行商问题 : 某售货员要到若干个城市去推销商品。已知各
个城市之间的路程(或旅费) 。他要选定一条从驻地出发,经过每个
城市一次,最后回到驻地的路线,使得总的路程(或总旅费)最短。
我们用一个带权图 G(V, E)来表示,顶点代表城市,边表示城市之
间的道路。图中各边所带的权即是城市间的距离(或城市间的旅费) 。
30
5
4
6 10
20
具有四个顶点的带权图
则旅行商问题即是: 在带权图G 中找到一条路程最短的周游路线,即
A
C D
B
权值之和最小的Hamilton圈 。
如果假定城市 A是驻地。则推销员从A地出发,第一站有3 种选
择:城市B、C 或城市D;第一站选定后,第二站有两种选择:如第
一站选定B,则第二站只能选C、D 两者之一。当第一、第二两站都
选定时,第三站只有一种选择:比如,当第一、第二两站先后选择了
B 和C 时,第三站只能选择D。最后推销员由城市D 返回驻地A。推
销员所有可能的周游路线可由下面的图反映出来 。
例 2. 定和子集问题: 已知一个正实数的集合 },,,{
2
1
n
wwwAL=
和正实数 M.试求 A 的所有子集 S,使得 S 中的数之和等于 M。
这个问题的解可以表示成 0/1 数组 (x
1
, x
2
, . . . , x
n
),依据 w
i
是否
属于 S, x
i
分别取值 1 或 0。故解空间中共有 2
n
个元素。它的树结构
是一棵完全二叉树 。
例 3. 4 皇后问题 : 在 4× 4 棋盘上放置 4 个皇后,且使得每两个之
间都不能互相攻击,即任意两个皇后都不能放在同一行、同一列及同
一对角线上。
将 4 个皇后分别给以 1 到 4 的编号, 这个问题的解决就是要安排
4 个皇后的位置。因而,每个决策序列由 4 个决策组成: P
1
, P
2
, P
3
,
P
4
,这里 P
k
代表安排第 k 个皇后的位置,因而有 16 种可能。该问题
的解空间有 16
4
个决策序列。这时的约束条件是 : 任意两个皇后均不
能位于同一行、 同一列及同一个对角线上 。 注意到这个解空间比较大,
从中搜索可行解较为困难。现在把约束条件中的“任意两个皇后均不
在同一行 ” 也放在问题中考虑,即:将 4 个皇后放在 4× 4 棋盘的不
同行上,使得每两个皇后均不能位于同一列、同一对角线上 。则解
空间中共有 4
4
个决策序列,因为此时可以假定第 k 个皇后处于第 k
行上。此时的约束条件为: 任意两个皇后均不能位于同一列及同一个
对角线上。 事实上,我们还可以用另一种方法描述,使得解空间进一
步缩小。将问题陈述为:将 4 个皇后放在 4× 4 棋盘的不同行、不同
列上,使得 任意两个皇后均不能处在同一对角线上 。这时的解空间应
当由 4!个决策序列构成。因为此时每个决策序列实际上对应于 {1, 2,
3, 4}的一个排列。我们可以用树来描述解空间。
从例3来看,解空间的确定与我们对问题的描述有关。如何组织
解空间的结构会直接影响对问题的求解效率。 这是因为回溯方法的基
本思想是通过搜索解空间来找到问题的可行解以至最优解。 当所给的
问题是从 n 个元素的集合S 中找出满足某种性质的子集时, 相应的解
空间树称为子集合树。此时,解空间有
n
2 个元素,遍历子集树的任
何算法均需 )2(
n
? 的计算时间。如例2。当所给的问题是确定 n个元
素的满足某种性质的排列时,相应的 解空间树称为排列树,此时,解
空间有 !n 个元素。遍历排列树的任何算法均需 )!(n? 计算时间,如例
1 和例3。本章只讨论具有上两类解空间树的求解问题。
确定了解空间的组织结构后,回溯法 就从开始顶点(解空间树的
根顶点)出发,以深度优先的方式搜索整个解空间。这个开始顶点就
成为一个活顶点, 同时也成为当前的扩展顶点。 在当前的扩展顶点处,
搜索向纵深方向移至一个新顶点。这个 新顶点就成为一个新的活顶
点,并且成为当前的扩展顶点。如果在当前的扩展顶点处不能再向纵
深方向移动,则当前的扩展顶点就成为死顶点。此时应往回移动(回
溯)至最近一个活顶点处,并使这个活顶点成为当前扩展顶点。回溯
法即以这种工作方式递归地在解空间中搜索, 直至找到要求的解或解
空间中已无活结点时为止。事实上,当我们将问题的有关数据以一定
的方式存储好以后(例如,旅行商问题存储赋权图的邻接矩阵、定和
子集问题是存储已知的 n+1 个数、4 皇后问题用整数对(i,j)表示棋
盘上各个位置) ,不必先建立一个解空间树,然后再搜索该树寻找所
需要的解。回溯法实际上在搜索的同时逐步生成解空间树(实际只需
要一部分) 。也就是说,对于实际问题我们不必要搜索整个解空间。
为了使搜索更加有效, 我们常常在搜索过程中加一些判断以决定搜索
是否该终止或改变路线。通常采用两种策略来避免无效的搜索,提高
回溯法的搜索效率。 其一是使用约束函数在扩展顶点处剪去不满足约
束的子树;其二是用限界函数(后面将阐述)剪去不能得到最优解的
子树。这两种函数统称为剪枝函数。
运用回溯法解题通常包括以下三个步骤:
1).针对所给问题,定义问题的解空间;
2).确定易于搜索的解空间结构;
3).以深度优先的方式搜索解空 间,并且在搜索过程中用剪枝函
数避免无效的搜索。
§2.定和子集问题和 0/1 背包问题
定和子集问题可以描述成下列的数 学问题:确定所有向量
),,,(
21 n
xxxxL= 满足:
Mxw
nix
i
ni
i
i
=
=∈
∑
≤≤1
,,2,1},1,0{L
(7.1)
如果让w
i
表示第i 件物品的重量,M 代表背包的容量,则0/1 背
包问题可以描述为:确定一个向量 ),,,(
21 n
xxxxL= 满足
i
ni
i
i
ni
i
i
xpts
Mxw
nix
∑
∑
≤≤
≤≤
≤
=∈
1
1
max..
,,2,1},1,0{L
(7.2)
这是一个优化问题,与定和子集问题比较,它多出优化函数,但只要
求确定一个最优解,定和子集问题要求确定所有可行解。我们先来处
理定和子集问题。
(一).定和子集问题
用回溯法求解的过程也即是生成解空间树的一棵子树的过程, 因为期
间将剪掉不能产生可行解的子 树(即不再对这样的子树进行搜索) 。
按照前面关于回溯算法的基本思想的说明, 搜索是采用深度优先的路
线,算法只记录当前路径。假设当前扩展顶点是当前路径的k 级,也
就是说当前路径上,x
i
,1 ≤i≤k 已经确定,算法要决定下一步搜索目
标。此时,有两种情况:
Mxw
i
ki
i
=
∑
≤≤1
与 Mxw
i
ki
i
<
∑
≤≤1
前一种情况说明当前路径已经建立一个可行解, 当前扩展顶点即是一
个答案顶点。此时应停止对该路径的继续搜索,返回到前面最近活顶
点。后一种情况出现,算法需要判断是否需要继续向前搜索。显然,
只有当 Mwxw
nik
ii
ki
i
≥+
∑∑
≤≤+≤≤ 11
时才有必要继续向前搜索。如果集合
},,,{
2
1
n
wwwAL= 中的元素是按不降的次序排列的,则上述继续搜
索的条件还可以加强为
MwxwandMwxw
ki
ki
i
nik
ii
ki
i
≤+≥+
+
≤≤≤≤+≤≤
∑∑∑
1
111
(7.3)
上述不等式记做B
k
。
定和子集问题的回溯算法伪代码
SumSubset(s,k,r) // 寻找W[1:n]中元素和为 M 的所有子集。
//W[1:n]中元素按不降次序排列, 进入此过程时, X[1], . . . ,
//X[k-1]的值已经确定。 记
∑
?≤≤
=
11
][][
kj
jXjWs ,
∑
≤≤
=
njk
jWr ][
//假定W[1] ≤ M,
∑
≤≤
≥
nj
MjW
1
][
1 global integer M, n; global real W[1:n];
2 global boolean X[1:n];
3 real r, s; integer k, j;
//由于B
k-1
=true,因此s + W[k] ≤ M
4 X[k]=1; // 生成左儿子。
5 if s + W[k]= M then
print (X[j],j from 1 to k);
6 else
7 if s + W[k] + W[k+1] ≤ M then
8 SumSubset(s+W[k], k+1, r-W[k]);
9 endif
10 endif
11 if s + r – W[k] ≥ M and s + W[k+1] ≤ M then
12 X[k]=0; //生成右儿子
13 SumSubset(s,k+1,r-W[k]);
14 endif
15 end SumSubset
因为假定 W[1] ≤ M,
∑
≤≤
≥
nj
MjW
1
][ ,所以程序开始执行时,
B
0
=true。同样,程序在递归执行过程中,总是以前提条件 B
k-1
=true
开始处理第k级顶点的儿子们的生成过程,由第3~10语句完成,并
在此过程中决定是否转动生成下一级顶点的儿子的生成过程, 由11~
14语句完成。语句 11是一个判断条件,如果这个条件不能满足,则
没有必要生成k级顶点的右儿子。而在左儿子被生成后,语句4 和语
句7决定是否沿着这个左儿子继续搜索。 所以该算法所生成的子树的
叶顶点或是某个左儿子,此时找到一个可行解;或者是某个右儿子,
此时由根顶点到该顶点的路径不能延伸为可行解。
例 子 : n=6,M=30; W[1:6]=(5 ,10,12,13,15,18). 由 算 法
SumSubset所生成的解空间树的子树参看文档SumSubset树。
(二).0/1背包问题
0/1背包问题是一个优化问题,因此 不仅可以使用约束函数,而且可以
使用限界函数做剪枝函数.如果当前背 包中的物品总重量是 cw,前面
k-1 件物品都已经决定好是否放入包中,那么第 k 件物品是否放入包
中取决于不等式cw + w
k
≤ M 是否满足.这即是搜索的约束函数.另外,
我们可以如下确定限界函数.回忆可分割的 0/1 背包问题的贪心算法,
当物品的按照
11
//
++
≥
iiii
wpwp 的规则排序时,最优解是
)0,,0,,1,,1(),,,,,,(
111
LLLLtxxxxx
nlll
=
+?
的形式,其中, 10 ≤≤t .
设当前背包中物品的总效益值时cp,如下方式确定限界函数:
构造限界函数
BoundF(cp,cw,k,M) //返回效益值的当前限界
gloal n, p[1:n],w[1:n];
integer k,i; real b,c,cp,cw,M;
B=cp;c=cw;
for i from k+1 to n do
c=c+w[i];
if c < M then b=b+p[i];
else return (b+(1-(c-M))/w[i])*p[i];
endif
endfor
return (b);
end BoundF
这样确定的限界函数表明,从当前确定的 k-1 定子解空间中寻求到的
可行解所能达到的效益值以当前限界函数的值为 上界.所以,如果搜
索最优解的算法在此之前已经知道大于 这个限界值的效益,则没有必
要在当前确定的 k-1 定子解空间中搜索.据此我们给出 0/1 背包问题
的回溯算法.
0/1背包问题的回溯算法
BackKnap(M,n,W,P,fw, fp,X)//M是背包容量.n件物品,数W[1:n]
//和P[1:n] 分别表示重量和价值,fw 是背包的最后重量,fp 是
//背包的最大效益.X[1:n]中美国元素取0或1值.若物品k未放
//入背包,则X[k]=0;否则,X[k]=1.
1 integer n,k,Y[1:n],I,X[1:n];
2 real M,W[1:n],P[1:n],fw,fp,cw,cp;
3 cw=0; cp=0; k=1; fp=-1;
4 loop
5 while k≤n && cw+W[k]≤ M do //使用约束函数
6 cw=cw+W[k]; cp=cp+P[k]; Y[k]=1; k=k+1;
7 endwhile
8 if k>n then
9 fp=cp; fw=cw; k=n; X=Y;//修改解
10 else Y[k]=0;
11 endif
12 while BoundF(cp,cw,k,M)≤fp do
13 while k≠0 && Y[k]≠1 do
14 k=k-1;
15 endwhile
16 if k=0 then return endif
17 Y[k]=0;cw=cw-W[k];cp=cp-P[k];
18 endwhile
19 k=k+1;
20 endloop
21 end BackKnap
算法采用一个大的循环搜索各条可能的 路径.当一条路径的搜索到某
一步不能继续往下搜索时,此时或是因 为约束函数不满足,或是因为
限界函数不满足.它们分别在语句 5 ~7 的子循环和语句 12~18 的子循
环中达到 .这是程序的两个主要部分 ,其余主要时处理边界条件 ,如 k >
n 的验证等 . 第一种情况出现时 ,语句 8~11 给出部分处理 ,剩下的事情
交给语句 12~19 来处理 .这里给出了搜索退回的方案 .
§3.n-皇后问题和旅行商问题
在第一节已经给出了这两个问题的解空间.如果用
),,,(
21 n
xxxL表示解,则它是自然数 },,2,1{ nL的一个排列.对于旅
行商问题,我们可以假定 1
1
=x ,表示售货员的驻地是城市 1.采用回
溯方法,对于 n 皇后问题,主要任务是给出约束函数,对于旅行商问题
主要是限界函数,因为此时我们可以假 定带权图是完全图,这只要将
实际不相邻的两个顶点间用带有权 ∞的边连接即可.
(一).n-皇后问题
这里的约束条件是: 任何两个皇后都不能位于同一对角线上.如
果我们用 ),( ji 表示棋盘上第i行与第j列交叉的位置.则在同一条平
行于主对角线的对角线上的两点 ),( ji 和 ),( lk 满足关系式 lkji ?=? ;
而处于同一条平行于副对角线的对角线上的两点 ),( ji 和 ),( lk 则满足
关系式 lkji +=+ .将这两个关系式略作变形记得
|||| ljki ?=? (7.3.1)
反之,如果棋盘上的两点 ),( ji 和 ),( lk 满足关系式 (7.3.1),则它们一
定位于同一条对角线上.所以,如果 ),,,(
21 n
xxxL是 n 皇后问题的一
个可行解,则对任何 ki ≠ ,等式
||||
ki
xxki ?=? (7.3.2)
均不得成立.这即是约束条件. 如果假定前面的 k-1 个皇后位置已经
排好,现在要安排第 k 个皇后的位置,必须使得
ki
xx ≠ ,而且等式
(7.3.2)不成立, 1,,2,1 ?= kiL.算法可以这样设计,对于
k
x 所有可
能的取值,验证上述条件,对于满足条件的 k 取最大者.而对于每个取
值
k
x ,验证它是否满足上述两个条件,用一个 boolean 函数 Place 来
完成.
Place(k)//如果第k个皇后能放在第X[k]列,则返回true,否则返
//回false.X 是一个全程数组,进入此过程时已经设置了k 个值
global X[1:k]; integer i,k;
i=1;
while i<k do
if X[i]=X[k] or |X[i]-X[k]|= |i-k| then
return (false);
endif
i=i+1;
endwhile
return (true);
end Place
使用函数Place 能使求n-皇后问题的回溯算法具有简单的形式
求n-皇后问题可行解的回溯算法
nQueens(n)
integer k,n,X[1:n];
X[1]=0; k=1; //k是当前行,X[k]是当前列
while k>0 do
X[k]=X[k]+1;//转到下一列
while X[k]≤ n && Place(k)=false do
X[k]=X[k]+1;
endwhile
if X[k]≤ n then
if k=n then
Print(X);
else k=k+1; X[k]=0; //转到下一行
endif
else k=k-1;//回溯
endif
endwhile
end nQueens
程序 nQueens 处理 4- 皇后问题的执行过程参看文档”4-皇后结构
树”.
(二).旅行商问题
用 1,2,…,n 代表 n 个顶点,一个周游
121
iiii
n
L用数组 ),,,(
21 n
iiiL表
示,它是旅行商问题的一个可行解.如果可行解的前 k-1 个分量
121
,,,
?k
xxxL已经确定,则判定
kk
xxxx
121 ?
L能否形成一条路径,只
需做k-1 次比较:
,,,,
121 ?
≠≠≠
kkkk
xxxxxxL (7.3.3)
此即构成旅行商问题的约束条件.用 w(i,j)记边(i,j)的权值.cl 记
当前路径
121 ?k
xxxL的长度,即
∑
?≤≤
+
=
21
1
),(
ki
ii
xxwcl .如果当前知道
的最短周游的线路路长度为fl,则当
flkkwcl >?+ ),1( (7.3.4)
时,
kk
xxxx
121 ?
L不会是最短周游路线的一部分,在解空间树中,相
应的一枝被剪掉,(7.3.4)即是旅行商问题的限界条件.此外, 当 nk =
时,若
flkwkkwcl <+?+ )1,(),1( (7.3.5)
则算法需要更新 fl,令
)1,(),1( kwkkwclfl +?+= (7.3.6)
旅行商问题的约束条件函数
NextValue(k) //从顶点 1 出发的路径,如果第 k 个顶点是顶点
//X[k],则返回 true,否则返回 false.X 是一个全程数组,进入
//此过程时已经设置了 k 个值,其中 X[1]=1, X[k]是当前扩展
//顶点。
global X[1:k]; integer i,k;
i=1;
while i<k do
if X[i]=X[k] then
return (false);
endif
i=i+1;
endwhile
return (true);
end NextValue
使用函数NextValue能使求旅行商问题的回溯算法具有简单的形式
求旅行商问题的回溯算法
BackTSP(n,W) // 求图G的从顶点1出发的最短周游 (Hamilton圈)
//路线。W是G的邻接矩阵。X[k]是当前路径的第k个顶点,c是
//当前路径的长度,fl 是当前所知道的最短的周游(Hamilton
//圈)的长度
integer k,n,X[1:n];
real W[1:n,1:n],cl,fl;
X[2]=1; k=2; cl=0; fl=+∞;
while k>1 do
X[k]=(X[k]+1) mod n;//给X[k]预分配一个值
for j to n do
if NextValue(k)=true then exit; endif
X[k]=(X[k]+1) mod n;
endfor
if [fl ≤ cl+W[k-1,k]] or [k=n and fl<cl+W[k-1,k]
+W[X[n],1]] then k=k-1; //回溯
elif k=n && fl≥cl+W[k-1,k]+W[X[k],1] then
fl= cl+W[k-1,k]+W[X[k],1]; k=k-1; //回溯
else cl= cl+W[k-1,k]; k=k+1;//继续纵深搜索
endif
endwhile
end BackTSP
§4 图的着色问题
已知一个无向图G 和m 种颜色, 在只准使用这 m种颜色对图G的顶点
进行着色的情况下,是否有一种着色方法,使图种任何两个相邻的顶
点都具有不同的颜色?如果存在这样的着色方法,则说图 G 是 m-可
着色的,这样的着色称为图 G 的一种 m-着色。使得 G 是 m-可着色的
最小数m 称为图G 的色数。这一节所讨论的图的着色问题是:
给定无向图G 和m 种颜色,求出G 的所有m-着色 。
用 G 的邻接矩阵 W 表示图 G,W[i,j]=1 表示顶点 i 与 j 相邻(有
边相连) ,否则W[i,j]=0. m 种颜色分别用1,2,…,m 表示。图G 的
每一种m-着色都可以用一个 n-维数组 X[1:n]表示。 X[i]=k表示顶点
i 被着上颜色k。简单分析可知,解空间是一个完全的 m-叉树,共有
n+1 级。X 是可行解,当且仅当
W[i,j]=1 ? X[i]≠X[j], (7.4.1)
此即是约束条件。假如已经给前 j-1 个顶点着好颜色,即
X[1],…,X[j-1]已经确定,则确定第 j个顶点所着的颜色X[j]时,根
据约束条件,应该验证条件
W[i,j]=1 ? X[i]≠X[j], 1≤i<j (7.4.2)
是否满足。这可以由下面程序完成。
下一步选色算法
NextColor ( j ) //进入此过程前,X[1],…,X[j-1]已经确定且满足约
//束条件。本过程给 X[j]确定一个整数 k,0 ≤k≤m:如果还有一
//种颜色 k可以分配给顶点 j(满足约束条件) ,则令X[j]=k;
//否则,令X[j]=//0。
global integer m, n, X[1:n], W[1:n, 1:n];
integer j, k;
loop
X[j]=(X[j]+1) mod (n+1);
if X[k]=0 then return endif
for i from 1 to j-1 do //验证约束条件
if W[i, j]=1 && X[i]=X[j] then exit endif
endfor
if i=j then return endif //找到一种颜色
endloop
end NextColor
在程序 NextColor 开始执行之前,诸 X[k]均已经有值。这可在着色问
题的回溯算法初次调用该函数时赋予初值: X[i]=0,i=1,2,…,n。
图的着色问题回溯算法
GraphColor( k) // 采用递归。 W[1:n, 1:n]是图 G 的邻接矩阵, 1, 2,
// . . . , m 代表 m 种颜色。 k 是下一个要着色的顶点。
global integer m, n, X[1:n], W[1:n, 1:n] ;
loop
NextColor ( k ); // 确定 X[k]的取值
if X[k]=0 then exit endif //说明没有颜色可以分配给第 k 个点
if k=n then // 已经找到一种着色方法
print (X);
else GraphColor ( k+1 ) ; //递归
endif
endloop
end GraphColor
例子 : n= 4, m= 3,
无向图 G 如右图 G
1
4 3
2
x
1
= 1 2 3
x
2
= 2 3 1 3 1 2
x
3
= 1 3 1 2 2 3 1 2 2 3 1 3
x
4
=2 3 2 2 3 3 1 3 1 3 1 3 1 1 2 2 1 2
一个 4 顶点的图和所有可能的 3 着色
正好用 3 种颜色的解只有 12 个。
§5 回溯法的效率分析
回溯法是以深度优先的方式系统地搜索问题的解空间。 解空间是由
所有可能的决策序列 ),,,(
21 n
xxxL构成。在搜索过程中,采用剪枝函
数尽量避免无意义的搜索。通过前面的具体实例的讨论容易看出,一
个回溯算法的效率在很大程度上依赖于以下几个因素:
1). 产生
k
x 的时间;
2).
k
x 的取值范围;
3). 计算约束函数的时间;
4). 计算限界函数的时间;
5). 满足约条件及限界条件的
k
x 的个数。
一般来说,一个好的约束函数能够显著地减少所生成的顶点的数
目。但是,这样的约束函数往往计算量较大。因此在选择约束函数是
通常存在着顶点数与约束函数计算量之间的折中。 我们希望总的计算
时间较少。为了提高效率,通常采用所谓的“重排原理” 。对于许多
问题而言,在进行搜索试探时,选取
k
x 值的顺序是任意的。这提示
我们,在其它条件相当的前提下,让可取值最少的
k
x 优先。这将会
更有效。如文档“解空间树比较” 中的图,是同一个问题的不同的解
空间树,从中可以体会到这种策略的效力。
在第一个图中,若从第 1 层剪去一棵子树,则从所有应当考虑三元
组中一次消去 12 个三元组;在第二个图中,虽然同样从第 1 层剪去
一棵子树,却只从应当考虑的三元组中一次消去 8 个三元组。前者的
效果显然比后者好。
解空间的结构一经选定,影响回溯法效率的前三个因素就可以确
定,只剩下生成顶点的数目是可变的,它将随问题的具体内容以及定
的不同生成方式而变动。即使是对于同一问题的不同实例,回溯法所
产生的顶点的数目也会有很大变化。对于一个实例,回溯法可能只产
生 )(nO 个顶点。而对于另一个相近的实例,回溯法可能产生解空间
中的所有顶点。如果解空间的顶点数是
n
2 或 !n ,则在最坏情况下,
回溯法的时间耗费一般为 )2)((
n
npO 或 )!)(( nnqO 。其中, )(np 和
)(nq 均为 n的多项式。对于一个具体问题来说,回溯法的有效性往往
就体现在当问题实例的规模 n较大时,它能够用很少的时间求出问题
的解。而对于一个问题的具体实例,我们又很难预测回溯法的算法行
为。特别地,很难估计在解这一具体实例时所产生的顶点数。这是在
分析回溯法效率时遇到的主要困难。 下面所介绍的概率方法也许对解
决这个问题有所帮助。
当用回溯法解某一个具体问题实例时 ,可用蒙特卡罗方法估算回
溯法将要产生的顶点数目。 该方法基本思想是在解空间树上产生一条
随即路径,然后沿此路径来估算结空 间中满足约束条件的顶点数 m。
设 x 是所产生的随即路径上的一个顶点, 且位于解空间树的第 i 层上。
对于 x 的所有儿子顶点,用约束函数 检测出满足约束条件的顶点数
m
i
。路径上的下一个顶点是从 x 的 m
i
个满足约束条件的儿子顶点中
随机选取的。 这条路径一直延伸到一个叶顶点或一个所有儿子顶点都
不满足约束条件的顶点为止。通过这些 m
i
的值,就可估计出解空间
树中满足约束条件的顶点的总数 m。 在用回溯法求问题的所有可行解
时,这个数特别有用。因为在这种情况下,解空间所有满足约束条件
的顶点都必须生成。若只要求用回溯法找出问题的一个解,则所生成
的顶点一般只是 m 个满足约束条件的顶点中的一部分。此时用 m 来
估计回溯法生成的顶点数就过于保守了。
为了从 m
i
的值求出 m 的值,还需要对约束函数做一些假定。在估
计 m 时,假定所有约束函数是静态的,即在回溯法执行过程中,约
束函数并不随随算法所获得信息的多少而动态地改变。 进一步还假定
对解空间树中同一层的顶点所用的约束函数是相同的。 对于大多数回
溯法,这些假定都太强了。实际上,在大多数回溯法中,约束函数是
随着搜索过程的深入而逐渐加强的。在这种情形下,按照前面所做假
定来估计 m 就显得保守。如果将约束函数变化的因素也加进来考虑,
所得出的满足约束条件的顶点总数将会少于 m,而且会更精确些。
在静态约束函数假设下,第一层共有 m
0
个满足约束条件的顶点。
若解空间的同一层的顶点具有相同的出度, 则在第一层上的每个顶点
将会又 m
1
儿子满足约束条件。因此,第二层将会又 m
0
m
1
个满足约束
条件的顶点。同理,第三层上满足约束条件的顶点个数是 m
0
m
1
m
2
。
依此类推,第 i+1 层上满足约束条件的顶点的数目是 m
0
m
1
m
2
… m
i
.因
此,对于给定的实例,如果产生解空间树上的一条途径,并求出 m
0
,
m
1
, m
2
,…, m
i
,…,则可以估计出回溯法要生成的满足约束条件的
顶点数目m= m
0
+ m
0
m
1
+ m
0
m
1
m
2
+….
假定回溯法要找出所有的可行解。设 ),,,(
121 ?i
xxxL是解空间树
中由根到一个顶点的路径,而 ),,,(
121 ?i
xxxTL表示所有这样的
i
x ,
使得 ),,,,(
121 ii
xxxx
?
L能够成为解空间树中的一条路径。 B
k
表示第 k
层上约束函数, B
k
(X[1], …,X[k])=true 表示这条路径到目前为止
未违背约束条件。否则 B
k
(X[1], …,X[k])=false。
回溯法的递归流程
RecurBackTrack(k)//进入算法时,解向量X[1:n]的前k-1个分
//量X[1],…,X[k-1]已经赋值
global n, X[1:n];
for 每个满足
“X[k] ∈ T
k
(X[1], …,X[k-1]) && B
k
(X[1], …,X[k])=true”
的X[k] do
if (X[1], …,X[k]) 是一条抵达某一答案顶点路径 then
print (X[1], …,X[k]);
endif
RecurBackTrack(k+1)
endfor
end RecurBackTrack
根据前面的分析,我们不能给出回溯法递归流程生成的解空间树中
顶点数目m。
回溯法效率估计
Estimate //程序沿着解空间树中一条随机路径估计回溯法生成的顶
//点总数 m
m=1; r=1; k=1;
loop
T
k
={X[k]| X[k]∈ T
k
(X[1], …,X[k-1]) && B
k
(X[1], …,
X[k])=true};
If T
k
={} then exit endif
r=r*size(T
k
); m=m+r;
X[k]=Choose(T
k
); k=k+1;
endloop
return (m);
end Estimate
其中 Choose(T
k
)是从 T
k
中随机地挑选一个元素。
当用回溯法求解某个具体问题时, 可用算法 Estimate 估算回溯法
生成的顶点数。 若要估计得精确些, 可选取若干条不同的随机路径 (通
常不超过 20 条) ,分别对各随机路径估计顶点总数,然后以平均值作
为 m。
例如 8 皇后问题,回溯算法 nQueens 可以用 Estimate 来估计。
文档“效率分析例图 ”给出了算法 Estimate 产生的 5 条随机路径所相
应的 8× 8 棋盘状态。当需要在棋盘上某行放入一个皇后时,所放的
列时随机选取的。它与已在棋盘上的其它皇后互不攻击。在图中棋盘
下面列出了每层可能生成的满足约束条件的顶点数,即 m
0
, m
1
,
m
2
,…, m
i
,…,以及由此随机路径算出的顶点总数 m 的值。这 5
个总点数的均值为1702。注意到8 皇后解空间树的顶点总数是:
∑∏
= =
?+
7
0 0
)8(1
j
j
i
i =109601
1702/109601≈1.55%,可见,回溯法效率远远高于穷举法。