回溯法

回溯法


一、回溯法是什么?

  回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

二、回溯法基本思想

  回溯法对任一解的生成,一般都采用逐步扩大解的方式。每前进一步,都试图在当前部分解的基础上扩大该部分解。它在问题的状态空间树中,从开始结点(根结点)出发,以深度优先搜索整个状态空间。这个开始结点成为活结点,同时也成为当前的扩展结点。在当前扩展结点处,搜索向纵深方向移至一个新结点。这个新结点成为新的活结点,并成为当前扩展结点。如果在当前扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动(回溯)至最近的活结点处,并使这个活结点成为当前扩展结点。回溯法以这种工作方式递归地在状态空间中搜索,直到找到所要求的解或解空间中已无活结点时为止。
  为了避免无效搜索,算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能否得到问题的解)。如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;如果可行,则进入该子树,继续按深度优先策略搜索。
  剪枝函数包括两类:1. 使用约束函数,剪去不满足约束条件的路径;2.使用限界函数,剪去不能得到最优解的路径。

三、回溯法实现

  回溯法的实现方法有两种:递归和递推(也称迭代)。一般来说,一个问题两种方法都可以实现,只是在算法效率和设计复杂度上有区别。

1.递归实现

   思路简单,设计容易,但效率低,其设计范式如下:

void BackTrace(int t) 
{
	if(t>n)
		Output(x);
	else
		for(int i = f (n, t); i <= g (n, t); i++ ) 
		{
			x[t] = h(i);
			if(Constraint(t) && Bound (t))
				BackTrace(t+1);
		}
}  

   其中,t 表示递归深度,即当前扩展结点在解空间树中的深度;n 用来控制递归深度,即解空间树的高度。当 t>n时,算法已搜索到一个叶子结点,此时由函数Output(x)对得到的可行解x进行记录或输出处理。用 f(n, t)和 g(n, t)分别表示在当前扩展结点处未搜索过的子树的起始编号和终止编号;h(i)表示在当前扩展结点处x[t] 的第i个可选值;函数 Constraint(t)和 Bound(t)分别表示当前扩展结点处的约束函数和限界函数。若函数 Constraint(t)的返回值为真,则表示当前扩展结点处x[1:t] 的取值满足问题的约束条件;否则不满足问题的约束条件。若函数Bound(t)的返回值为真,则表示在当前扩展结点处x[1:t] 的取值尚未使目标函数越界,还需由BackTrace(t+1)对其相应的子树做进一步地搜索;否则,在当前扩展结点处x[1:t]的取值已使目标函数越界,可剪去相应的子树。

2.递推(迭代)实现

  算法设计相对复杂,但效率高。

void IterativeBackTrace(void) {
	int t = 1;
	while(t>0) {
		if(f(n, t) <= g( n, t))
			for(int i = f(n, t); i <= g(n, t); i++ ) {
				x[t] = h(i);
				if(Constraint(t) && Bound(t)) {
					if ( Solution(t))
						Output(x);
					else
						t++;
				}
			}
		else 
			t− −;
	}
}

  在上述迭代算法中,用Solution(t)判断在当前扩展结点处是否已得到问题的一个可行解,若其返回值为真,则表示在当前扩展结点处x[1:t] 是问题的一个可行解;否则表示在当前扩展结点处x[1:t]只是问题的一个部分解,还需要向纵深方向继续搜索。用回溯法解题的一个显著特征是问题的解空间是在搜索过程中动态生成的,在任何时刻算法只保存从根结点到当前扩展结点的路径。如果在解空间树中,从根结点到叶子结点的最长路径长度为 h(n),则回溯法所需的计算空间复杂度为 O(h(n)),而显式地存储整个解空间复杂度则需要O(2h(n))或O(h(n)!)。

四.子集树和排列树

1.子集树

  当给定的问题是从n个元素的集合S中找出满足某种性质的子集时,相应的解空间树称为子集树。例如,n个物品的0-1 背包问题所对应的解空间树是一棵子集树,该类树通常有2n个叶子结点,总结点数为2^(n+1)- 1,遍历子集树的任何算法需要的计算时间复杂度均为O(2n)。
  回溯法搜索子集树的一般算法描述如下:

void BackTrace(int t) 
{
	if(t>n)
		Output(x);
	else
		for(int i = 0; i <= n; i++) 
		{
			x[t] = i;
			if(Contraint(t) && Bound(t))
				BackTrace (t + 1);
		}
}

2.排列数

  当给定的问题是确定 n 个元素满足某种性质的排列时,对应的解空间树称为排列树。排列树通常有n! 个叶子结点,遍历排列树需要的计算时间复杂度为O(n!)。
  回溯法搜索排列树的算法模板如下:

void BackTrace(int t) 
{
	if(t>n)
		Output(x);
	else
		for(int i = 0; i <= n; i++) 
		{
			Swap(x[t], x[i]);
			if(Contraint (t) && Bound (t))
				BackTrace(t + 1);
			Swap(x[t], x[i]);
		}
} 
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值