算法设计与分析期末复习(部分算法伪代码模板)

算法设计与分析期末复习

目录

一、重点概念(容易忽视的定义)

二、一些问题Q & A

三、部分伪代码模板

四、部分证明的思路模板

一、重点概念

1.回溯、分枝限界算法

解空间:实例I的满足显式约束条件的所有元组,构成I的解空间,即所有xi合法取值的元组的集合——可行解。

状态空间树:解空间的树结构称为状态空间树(state space tree)

(回溯法要回画部分状态空间树)

问题状态:树中的每一个结点代表问题的一个状态,称为问题状态(problem state) 。

状态空间:由根结点到其他结点的所有路径确定了这个问题的状态空间(state space) 。

解状态:是这样一些问题状态S,对于这些问题状态,由根到S的那条路径确定了这个问题解空间中的一个元组(solution states) 。

答案状态:是这样的一些解状态S,对于这些解状态而言,由根到 S的这条路径确定了问题的一个解(满足隐式约束条件的解)(answer states) 。

活结点:自己已经生成,但其儿子结点还没有全部生成并且有待生成的结点。

E-结点(expansion node):当前正在生成其儿子结点的活结点。

死结点:不需要再进一步扩展或者其儿子结点已全部生成的结点。

限界函数:在结点生成的过程中,定义一个限界函数,用来杀死还没有生成全部儿子结点的一些活结点——这些活结点已无法满足限界函数的条件,因此不可能导致问题的答案。

结点成本的估计函数 c(x)=f(h(x))+g(x)

g^(X) :是由X到达一个答案结点所需成本的估计函数。

LC-检索:选择 c^(.)值最小的活结点作为下一个E-结点的状态空间树检索方法。

最小成本的上界:定义U为最小成本解的成本上界,对具有c^(X)>U 的所有活结点可以被杀死,从而可以进一步使算法加速,减少求解的盲目性。

2.单源点最短路径问题

前驱子图为Gπ=(Vπ,Eπ),算法终止时,Gπ是一棵最短路径树。

松弛操作,对于每个结点v,维持一个属性v.d,记录从源点s到结点v的最短路径权重的上界,称v.d为s到v的最短路径估计。

3.贪心算法

贪心选择性质:可以通过做出局部最优(贪心)选择来构造全局最优解的性质。

4.最小生成树

横跨切割:如果一条边(u,v)∈E的一个端点在集合S中,另一个端点在集合V-S中,则称该条边横跨切割(S,V-S)。

尊重:如果边集A中不存在横跨该切割的边,则称该切割尊重集合A。

轻量级边:在横跨一个切割的所有边中,权重最小的边称为轻量级边。

5.函数增长

O(g(n)):存在正实数c和n0,使得对所有n≥n0,0≤f(n)≤cg(n)

Ω(g(n)):存在正实数c,n0,使得对所有n≥n0,0≤cg(n)≤f(n)

θ(g(n)):存在正实数c1,c2,n0,使得对所有n≥n0,0≤c1g(n)≤f(n)≤c2g(n)

6.动态规划

子问题图:用于描述子问题与子问题之间的依赖关系。

二、部分问题

  1. LIFO和FIFO分枝-限界法存在的问题?

    对下一个E-结点的选择规则过于死板。对于有可能快速检索到一个答案结点的结点没有给出任何优先权。

  2. 计算结点成本函数的困难?

    计算结点X的代价通常要检索子树X才能确定,因此计算C(X)的工作量和复杂度与解原始问题是相同的。

  3. 如何避免单纯考虑g^(X)造成的纵深检查?

    引进h(X)改进成本估计函数

  4. 当有多个答案结点时,LC是否一定找得到具有最小成本的答案结点呢?

    否。

    因为可能存在这样的结点X和Y:c(X)>c(Y),但c^(X) < c^(Y)。

    解决方式:对每一对c(X)<c(Y)的结点X和Y,有c^(X) < c ^(Y) 使得LC能够找到一个最小成本的答案

  5. 贪心算法的设计思想

    贪心算法是这样一种方法:分步骤实施,它在每一步仅作出当时看起来最佳的选择,即局部最优的选择,并寄希望这样的选择最终能导致全局最优解。

  6. LC-检索的基本思想

    寻找一种排序函数C(.),该函数能够让答案结点尽早生成。

  7. 最短路径三角不等式 δ(s,v)≤δ(s,u)+w(u,v)

    假设P是从源结点s到结点v的一条最短路径,则P的权重不会比任何从s到v的其他路径权重大。路径P的权重不会比这样一条特定路径的权重大:从源结点s到结点u的一条最短路径再加上边(u,v)而达到结点v的这条路径。

三、部分伪代码模板框架

1.宽度优先搜索

描述:

①从结点v开始,首先访问结点v,给v标上已访问标记

② 访问邻接于v且目前尚未被访问的所有结点,此时结点v被检测,而v的这些邻接结点是新的未被检测的结点。将这些结点依次放置到一个称为未检测结点表的队列中。

③ 若未检测结点表为空,则算法终止;否则

④ 取未检测结点表的表头结点作为下一个待检测结点,重复上述过程。直到Q为空,算法终止

伪代码:

procedure BFS(v)
	//宽度优先搜索,它从结点v开始,所有已访问结点被标记VISITED(i)=1
	VISITED(v) <- 1		//VISITED(1:n)是一个标志数组,初始值为VISITED(i)=0,1≤i≤n
	u <- v
	将Q初始化为空			//Q是未检测结点的队列
	loop
		for 邻接于u的所有结点w do
			if VISITED(w)=0 then	//w未被访问
				call ADDQ(w,Q)		//ADDQ将w加入到队列Q的末端
				VISITED(w) <- 1		//同时标识w已被访问
			endif
		repeat
		if Q 为空 then return endif
		call DELETEDQ(u,Q)			//DELETEDQ取出队列Q的表头,并赋给变量u
	repeat
end BFS

宽度优先周游

procedure BFT(G,n)
	//G的宽度优先搜索
	int VISITED(n)
	for i <- 1 to n do VISITED(i) <- 0 repeat
	for i <- 1 to n do		//反复调用BFS
		if VISITED(i)=0 then call BFS(i) endif
	repeat
end BFT

向前边:BFS中由u到达未访问结点w的边(u,w)称为向前边

若G是连通图,则BFS终止时,T构成一棵生成树,称为图G的宽度优先生成树

2.深度优先搜索

描述

从结点v开始,首先访问v,给v标上已访问标记;然后中止对v的检测,并从邻接于v且尚未被访问的结点的中找出一个结点w开始新的检测。在w被检测后,再恢复对v的检测。当所有可到达的结点全部被检测完毕后,算法终止。

伪代码

procedure DFS(v)
	//已知n结点的图G=(V,E)以及初值置零的数组VISITED(1:n)
	VISITED(v) <- 1
	for 邻接于v的每个结点w do
		if VISITED(w)=0 then
			call DFS(w)
		endif
	repeat
END DFS

D_Search:深度搜索就是将BFS中的队列换成栈,和DFS还是有些不一样的地方

3.回溯法

描述

按深度优先的方法从开始结点进行搜索

  • 开始结点是第一个活结点,也是 E*-*结点。

  • 如果能从这个E*-结点移动到一个新结点,那么这个新结点将变成活结点和新的E-结点 (旧的E-*结点仍是一个活结点)。

  • 如果不能移到一个新结点,当前的E*-结点就“死”了,然后返回到最近被考察的活结点(回溯),这个活结点重新变成E-*结点。

  • 当找到了答案或者穷尽了所有的活结点时,搜索过程结束

迭代伪代码

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

递归伪代码

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

分枝限界法和回溯法的区别在于:

回溯法:深度优先,一般用于寻找所有解

分枝限界:广度优先,一般用于寻找最优解

4. LC-检索(更智能的分枝限界)

procedure LC(T,c^)
	//为找答案结点检索T,c^为结点成本估计函数
	if T是答案结点 then 输出T; return endif //T为答案结点,输出T
	E <- T
	将活结点表初始化为空
    loop
    	for E的每个儿子X do
    		if X是答案结点 then 输出从X到T的路径 ; return endif
    		call ADD(x) //X是新的活动结点,ADD将X加入活结点表中
    		PARENT(X) <- E 	//指示到根的路径
    	repeat
    	if 不再有活结点 then print("no answer code") ; stop endif
    	call LEAST(E)	//从活结点表中找c^最小的活结点,赋给X,并从活结点中删除
    repeat
end LC

四、部分证明思路总结

  • 对设计的贪心算法,证明能够找到最优解(证明贪心解是最优解)(贪心选择性质)

    通常先考查某个子问题的最优解,然后用贪心选择替换某个其它选择来修改此解,从而得到一个相似但更小的子问题。

    通过逐步替换证明贪心解不差于假设的最优解

    数学归纳法 k -> k+1

  • 最优子结构

    当前解最优,那它的子问题也必须是最优解。

    反证:如果子问题有更优解,那么该问题也能得到更优的解。

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