算法设计与分析复习--回溯法

算法设计与分析期末复习

主要参考:

​ 算法设计与分析(北京航空航天大学MOOC)

​ 算法设计与分析(北京大学MOOC)

​ 华中科技大学 计算机科学与技术学院 算法设计与分析 课堂教学PPT

五、回溯法

回溯算法的例子:4后问题、0-1背包问题、货郎问题

解的形式:向量

搜索空间:树,可能是n叉树,子集树、排列树等等,输的节点对应与部分向量,可行解在叶节点

搜索方法:深度优先,宽度有限,…,跳跃式遍历搜索树,找到解

约束条件、回溯判定

可用回溯法求解的问题:

问题的解可以用一个n元组(x1,…,xn)来表示,其中的xi取自于某个有穷集Si,并且这些解必须使得某一规范函数P(x1,…,xn)取极值或满足该规范函数条件

5.1 回溯算法基本思想

(1)适用:求解搜索问题和优化问题

(2)搜索空间:树,结点对应部分解向量,可行解在树叶上

(3)搜索过程:采用系统的方法隐含遍历搜索树

(4)搜索策略:深度优先,宽度优先,函数优先,宽深结合等

(5)结点分支判定条件:

满足约束条件–分支扩张解向量,不满足约束条件–回溯到该结点的父结点

显式约束条件:限定每个xi只能从一个给定的集合上取值

隐式约束条件:xi必须彼此相关的情况

(6)结点状态:动态生成

​ 白结点(尚未访问),灰结点(正在访问该结点为根的子树),黑结点(该结点为根的子树遍历完成)

(7)存储:当前路径

状态空间树:

  • 问题状态(problem state):树中的每一个结点确定所求解问题的一个问题状态。
  • 状态空间(state space):由根结点到其他结点的所有路径则确定了这个问题的状态空间。
  • 解状态(solution states):是这样一些问题状态S,对于这些问题状态,由根到S的那条路径确定了这解空间中的一个元组。
  • 答案状态(answer states):是这样的一些解状态S,对于这些解状态而言,由根到S的这条路径确定了这问题的一个解。
  • 静态树(static trees):树结构与所要解决的问题的实例无关。
  • 动态树(dynamic trees):根据不同的实例而使用不同的树结构。
  • 活结点:自己已经生成而其所有的儿子结点还没有全部生成的结点。
  • E-结点(正在扩展的结点):当前正在生成其儿子结点的活结点。
  • 死结点:不再进一步扩展或者其儿子结点已全部生成的生成结点。

构造状态空间树的两个方法:回溯法、分枝-限界方法

  • 回溯法------当前E-结点R,生成一个新的儿子C,则C就变成一个新的E-结点,对子树C完全检测后,R结点再次成为E-结点
  • 分枝-限界方法------一个E-结点一直保持到变成死结点为止

5.2 回溯算法的适用条件

多米诺性质:

  • 通俗表达:
  • 若x1到xi-1满足,就可以继续向下搜索
  • 若不满足,则x1到xi也不满足,就回溯

算法设计与分析复习--回溯法_第1张图片
算法设计与分析复习--回溯法_第2张图片

5.3 回溯算法的设计步骤

(1)定义解向量和每个分量的取值范围

解向量为

确定xi的取值集合为Xi,i=1,2,…,n

(2)在确定如何计算xk取值集合Sk,Sk包含于Xk

(3)确定结点儿子的排列规则

(4)判断是否满足多米诺性质(个人感觉算法实现中华科ppt表述 更为直观)

(5)确定每个结点分支的约束条件

(6)确定搜索策略:深度优先,宽度优先等

(7)确定存储搜索路径的数据结构

5.4 回溯算法的实现一般性描述

递归实现

(北大mooc表述)

  • Sk是xk的取值集合
算法 ReBack(k)
if k>n then 是解
else while Sk ≠ 空集 do
	xk <- Sk 中最小值
	Sk <- Sk-{xk}
	计算Sk+1
	ReBack(k+1)

另一种表述(华科ppt表述):

  • (x1, x2, …xi-1)是由根到一个结点的路径

  • T(x1, x2, …xi-1)是所有xi的集合:对于每一个xi , (x1, x2, …xi)是由根到结点xi的路径(也就是根据某一确定的x1到xi-1所得到的xi取值的集合)

  • 限界函数Bi,如果路径(x1, x2, …xi)不可能延伸到一个答案结点,则Bi(x1, x2, …xi)取假值,否则取真值(也就是添加上xi仍可能是答案,则为真,不可能为答案则为假)

procedure RBACKTRACK(k)
	global n,X(1:n)
	for 满足下式的每个X(k)
		X(k) ∈ T(X(1),...,X(k-1)) and B (X(1),...,X(k)) = true do
		if(X(1),...,X(k)) 是一条已抵达答案结点的路径
			then print(X(1),...,X(k)) endif
			call RBACKTRACK(k+1)
	repeat
end RBACKTRACK

迭代实现

Backtrack
输入: n
输出:所有的解
对于 i = 1,2,...,n 确定Xi				//确定初始取值
k <- 1
计算Sk
while Sk ≠ 空集 do 					 //满足约束分支搜索
	xk <- Sk中的最小值,Sk <- Sk-{xk} 
	if k是解
if k>1 then k <- k-1 goto (第7行)		  //回溯

另一种表述:

procedure BACKTRACK(n)
	integer k,n; local X(1:n)
	k <- 1
	while k>0 do
		if 还剩有没检验过的X(k)使得
			X(k) ∈ T(X(1),...,X(k-1)) and B(X(1),...,X(k)) = true
		then
			if(X(1),...,X(k)) 是一条已抵达一答案结点的路径
			then print(X(1),...,X(k)) endif
			k <- k+1
		else
			k <- k-1
		endif
	repeat
end BACKTRACK

5.5 搜索树结点数的估计

Monte Carlo方法

  1. 从根开始,随机选择一条路径,直到不能分支为止,即从x1,x2,…,依次对xi赋值,每个xi的值是从当时的Si中随机选取,直到向量不能扩张为止

  2. 假定搜索树的其他|Si|-1个分支与以上随机选出的路径一样,计数搜索树的点数

  3. 重复步骤1和2,将结点数进行概率平均

(如果没看明白,去北大MOOC里看例子讲解)

  • 目的:估计搜索树真正访问结点数

  • 步骤:

    • 随机抽样,选择一条路径

      用这条路径代替其他路径

      逐层累加树的结点数

    • 多次选择,取结点数的平均值

Monte Carlo
输入:n为皇后数,t为抽样次数
输出:sum,即t次抽样路长平均值
sum <- 0
for i <- 1 to t do		//凑杨次数t
	m <- Estimate(n)	//m为结点数
	sum <- sum + m
sum <- sum / t

一次抽样

  • m为本次取样得到的树节点总数

  • k为层数

  • r2位上层结点数

  • r1位本层结点数

  • r1 = r2*分枝数

  • n为数的层数

  • 从树根向下计算,随机选择,直到树叶

伪代码

Estimate(n)
m <- 1; r2 <- 1; k <- 1		//m为一次估计结点总数
while k ≤ n do
	if Sk = ∅ then return m //不能继续
    r1 <- |Sk|*r2			//r1为扩张后的结点总数
    m <- m+r1				//r2位扩张前的结点总数
    xk <- 随机选择Sk的元素		//随机选择一步
    r2 <- r1
    k <- k+1

左侧红线路径为一次抽样,右侧为扩张后的数(用于估计一次结点个数)

通过多次抽样,取平均值估计搜索树的结点数(Monte Carlo方法)

算法设计与分析复习--回溯法_第3张图片

5.6 实例伪代码

例1 装载问题

有n个集装箱,需要装上两艘载重分别为c1,c2的轮船,wi为第i个集装箱的重量,且w1+w2+…+wn≤c1+c2,。

问:是否存在一种合理的装载方案把这n个集装箱装上船?如果有,请给出一种方案。

求解思路

  • 输入:W=为集装箱重量,c1和c2为船的最大载重量

  • 算法思想:

    令第一艘船的装入量为W1(让第一艘船装的尽量多)

    1. 用回溯算法求使得c1-W1达到最小的装载方案
    2. 若满足w1+w2+…+wn-W1≤c2,则回答yes,否则回答no

伪代码

算法 Loading(W,c1)
Sort(W);
B <- c1; best <- c1; i <- 1;		//B为当前船1的空隙,best为最优解情况下船1的空隙
while i ≤ n do						//相当于赋值一个初值
	if 装入i后重量不超过c1
	then B <- B-w1 ; x[i] <- 1 ; i <- i+1
	else x[i] <- 0 ; i<- i+1
if B1 and x[i]=0 do
	i<- i-1;
if x[i] = 1							//左子树换到右子树
then x[i] <- 0
	B <- B + wi
	i <- i+1	

时间复杂度 可以达到O(2^n)

例2 n皇后问题

怎么判断是否形成了互相攻击的格局?

是否在同一列,是否在同一条对角线

procedure PLACE(k)						//判断当前X(k)是否可以放置
	global X(1:k); integer i,k			//是否会和前k-1行放置的皇后冲突
	i <- 1
	while i0 do
		X(k) <- X(k)+1						//第一个皇后放置的位置
		while X(k) <= n and not PLACE(k) do
			X(k) <- X(k)+1
		repeat
		if X(k) <= n						//如果可以放置
		then if k=n
			then print(X)
			else k <- k+1;X(k) <- 0			//还没到最后一行,继续下一行
		else k <- k-1;						//如果不能放置
		endif
	repeat
end NQUEENS
例3 子集和数问题

n个数里面存在某些个数的和等于M

元组大小固定

W(k)是按照从小到大的顺序排好序的(对理解伪代码中的剪枝很重要)

限界函数

在这里插入图片描述

伪代码

procedure SUMOFSUB(s,k,r)				//当前和s,当前元素位置k,k之后所有数的和
	global integer M,n; global real W(1:n); global boolean X(1:n)
	real r,s; integer k,j
	X(k) <- 1
	if s+W(k)=M then					//和等于了M,找到结果
		print(X(j),j <- 1 to k)
	else
		if s+W(k)+W(k+1)<=M then		//算上Wk和Wk+1也不够
			call SUMOFSUB(s+W(k),k+1,r-W(k))//那么就算上Wk,继续搜索
		endif
	endif
	if s+r-W(k)>=M and s+W(k+1) <= M	
	then X(k) <- 0
		call SUMOFSUB(s,k+1,r-W(k))
	endif
end SUMOFSUB

伪代码第12行解释:剪枝

如果算上k之后所有的还不够,那就不继续向下搜索了

由于是从小到大排序好的,如果k+1也超过了,那么后面的全都超过了,也不继续搜索

两个条件都满足的条件下才进行继续搜索

你可能感兴趣的:(算法设计与分析,算法,回归算法)