回溯算法设计(1): 回溯法概述

问题的解空间

一个复杂问题的解决方案是由若干个小的决策步骤组成的决策序列,解决一个问题的所有可能的决策序列构成该问题的解空间

在这里插入图片描述

  • 解空间中满足约束条件的决策序列称为可行解
  • 在约束条件下使目标达到最优的可行解称为该问题的最优解
  • 问题的解由一个不等长或等长的解向量X={x1,x2,…,xn}组成,其中分量xi表示第i步的操作
  • 所有满足约束条件的解向量组构成了问题的解空间
  • 解空间一般用树形式组织,也称为解空间树状态空间

求解问题类型

  • 找所有解
  • 找最优解

什么是回溯法

在解空间树中,按照深度优先搜索的策略,从更解点开始出发搜索解空间树。

  1. 根结点是活结点也是当前的扩展节点
    1)活结点:自身已生成但其孩子解点没有全部生成但结点
    2)扩展节点:正在产生孩子结点的结点
  2. 当从状态si搜索到状态si+1后,如果si+1变为死结点,则从状态si+1回退到si,再从si找其他可能的路径
  3. 若用回溯法求问题的所有解时,需要回溯到根结点,且根结点的所有可行的子树都要已被搜索完才结束。若用回溯法求任一个解时,只要搜索到问题的一个解就可以结束

两种策略

回溯法搜索解空间时,通常采用两种策略避免无效搜索,提高回溯的搜索效率:

  • 约束函数在扩展结点处剪除不满足约束的子树
  • 限界函数剪去得不到问题解或最优解的子树。

这两类函数统称为剪枝函数

回溯法的一般步骤

  • 针对所给问题,定义问题的解空间;
  • 确定结点的扩展规则。
  • 深度优先方式搜索解空间树,并在搜索过程中可以采用剪枝函数来避免无效搜索。

状态空间树的产生方法

  • 深度优先的问题状态生成法:对一个扩展结点R,一旦产生了它的一个儿子C,就把C当做新的扩展结点。在完成对子树C(以C为根的子树)的穷尽搜索之后,将R重新变成扩展结点,继续生成R的下一个儿子(如果存在)
  • 回溯法:为了避免生成那些不可能产生最佳解的问题状态,要不断地利用限界函数(bounding function)来处死那些实际上不可能产生所需解的活结点,以减少问题的计算量

🌟回溯法=深度优先搜索+剪枝

回溯法的基本思想

  1. 适用:求解搜索问题和优化问题.
  2. 搜索空间:树,结点对应部分解向量,可行解在树叶上.
  3. 搜索过程:采用系统的方法隐含遍历搜索树.
  4. 搜索策略:深度优先,广度优先,函数优先,宽深结合等.
  5. 结点分支判定条件:满足约束条件—分支扩张解向量;不满足约束条件—回溯到该结点的父结点.
  6. 动态生成
    • 扩展结点(一个正在产生儿子的结点称为扩展结点)
    • 活结点(正在访问该结点为根的子树)
    • 死结点(该结点为根的子树遍历完成)
      7.存储:当前路径

回溯法的适用条件

多米诺性质

定义:
设X=<x1,x2,…, xn> 是问题的解,Xi=<x1,x2,…,xi>, Xi+1=<x1,x2,…,xi,xi+1>,Xi,Xi+1⊆X ,Xi和Xi+1分别是搜索到第i层和第i+1层的解.如果P(Xi+1)→P(Xi) , 即 P(Xi+1) 蕴含 P(Xi),则称该问题满足多米诺性质.

多米诺性质为回溯法的剪枝提供了数学依据。

两种典型的解空间树

子集树(subset tree):当所给的问题是从n个元素的集合s中找出满足某种性质的子集时,相应的解空间称为子集树。O(n^2)

在这里插入图片描述
排列树(permutation tree):当所给的问题时确定n个元素满足某种性质的排列时,相应的解空间称为排列树。O(n!)

在这里插入图片描述
子集树的效率往往大于排列树,选择自己树或排列树需要依据题目要求来确定。

回溯法的算法框架

解空间为子集树或N叉树

int x[n];			   //x存放解向量,全局变量
void backtrack(int i)		   //求解子集树的递归框架
{  if(i>n)			   //搜索到叶子结点,输出一个可行解
      输出结果;
   else
   {  for (j=下界;j<=上界;j++)   //用j枚举i所有可能的路径
      {  x[i]=j;		   //产生一个可能的解分量
         …			   //其他操作
         if (constraint(i) && bound(i))
            backtrack(i+1);	   //满足约束条件和限界函数,继续下一层
      }
   }
}

解空间为排列树

int x[n];			//x存放解向量,并初始化
void backtrack(int i)		//求解排列树的递归框架
{  if(i>n)			//搜索到叶子结点,输出一个可行解
	输出结果;
   else
   {  for (j=i;j<=n;j++)	//用j枚举i所有可能的路径
      {  …			//第i层的结点选择x[j]的操作
         swap(x[i],x[j]);	//为保证排列中每个元素不同,通过交换来实现
         if (constraint(i) && bound(i))
	     backtrack(i+1);	//满足约束条件和限界函数,进入下一层
         swap(x[i],x[j]);	//恢复状态
         …			//第i层的结点选择x[j]的恢复操作
      }
   }
}

回溯法效率分析

回溯算法在很大程度上依赖于一下因素:
回溯算法的效率在很大程度上依赖于以下因素:
(1)产生下一个x[k]的时间;
(2)满足显约束条件的x[k]值的个数;
(3)计算约束函数Bi的时间;
(4)满足约束函数Bi的所有x[k]的个数。

因此,在选择约束函数时通常存在生成结点数与约束函数计算量之间的折中。

回溯法与深度优先遍历的异同

  • 相同点:回溯法在实现上也是遵循深度优先的,即一步一步往前探索,而不像广度优先遍历那样,由近及远一片一片地搜索。
  • 不同点:
  • 访问顺序不一样:深度为遍历,本质为无须。回溯时求解过程,本质是有序的
  • 访问次数的不同:深度对访问过的结点不再访问。回溯中已访问过的顶点可能再次访问。
  • 剪枝的不同:深度优先遍历不含剪枝,而很多回溯算法采用剪枝条件剪除不必要的分支以提高效率。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
背包问题是一种经典的组合优化问题,它的目标是在给定的一些物品中选择一些物品,使得这些物品的总重量不超过背包容量,同时总价值最大。 回溯法是一种穷举搜索算法,它通过不断地尝试每种可能的情况,来找到问题的解。在背包问题中,我们可以使用回溯法来穷举每个物品的选择情况。 具体来说,我们可以定义一个`knapsack`函数,该函数用来递归地搜索每个物品的选择情况。该函数需要传入以下参数: - `i`:当前要选择的物品编号; - `value`:当前已选物品的总价值。 在函数中,我们首先判断是否已经选择完了所有物品,如果是,则更新最大价值;否则,对于第`i`个物品,分别尝试选择和不选择两种情况,并递归调用`knapsack`函数。如果选择了第`i`个物品,则更新当前已选物品的总重量和总价值,并将`i`加1;如果不选择第`i`个物品,则直接将`i`加1。在递归返回时,需要恢复现场,即将当前已选物品的总重量减去第`i`个物品的重量。 最后,我们可以在`main`函数中读入输入数据,初始化全局变量,调用`knapsack`函数,输出最大价值。 下面是背包问题用回溯法算法设计: ```python W = [2, 2, 6, 5, 4] # 每个物品的重量 V = [6, 3, 5, 4, 6] # 每个物品的价值 C = 10 # 背包容量 max_value = 0 # 当前最大价值 cur_weight = 0 # 当前已选物品的总重量 def knapsack(i, value): global max_value, cur_weight if i == len(W): # 已经选择完了所有物品 if value > max_value: max_value = value return if cur_weight + W[i] <= C: # 如果当前物品可以放进背包 cur_weight += W[i] knapsack(i+1, value+V[i]) # 选择当前物品 cur_weight -= W[i] # 恢复现场 knapsack(i+1, value) # 不选择当前物品 knapsack(0, 0) # 从第0个物品开始选择 print(max_value) ``` 在以上代码中,我们使用了全局变量来存储当前最大价值和当前已选物品的总重量。在`knapsack`函数中,我们使用`global`关键字声明这些变量为全局变量,以便在函数中进行修改。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值