读书时间--回溯法浅析:逆向思维领略算法之美

下面将使用回溯思想解决若干经典问题并通过它们来说明使用回溯的基本思路 什么叫回溯法 回溯是一种比较简单、比较常用的搜索策略。 它的基本思想是假设某问题的解决步骤可能有N步,且每一步的解决方法又可能有M种,那么就按照某种顺序依次试探每一步中的各种方法,一旦某一步的所有方法都失效,那么就返回上一步继续试探上一步骤的其他M−1种方法。简而言之就是从一条路往前走,能进则进,不能进则退回来,换一条路再试。 通常用回溯法解决问题的一般步骤为:首先,定义一个解空间,它包含问题的解,也就是每一步所采用的各种方法。然后利用适于搜索的方法组织解空间,再利用深度优先法搜索解空间。并利用限界函数避免移动到不可能产生解的子空间。这里问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯法的一个重要特性。而其中全面访问所有可能的情况又可以分为两种:一是不考虑给定问题的特有性质,按事先定好的顺序,依次运用规则,即盲目搜索的方法;另一种则考虑问题给定的特有性质,选用合适的规则,提高搜索的效率,即启发式的搜索。 下面简单举几个例子来阐释回溯法 ---- 迷 宫 问 题 ---- 迷宫问题是应用回溯法解决的典型问题。迷宫早出现在古希腊神话中。传说在远古时代,麦诺安帝国国王弥诺斯在克里特岛建造了一座有无数宫殿的迷宫,迷宫中道路曲折纵横,谁进去都别想出来,而且在迷宫的纵深处还有一个牛头怪。弥诺斯要求雅典每隔 9 年送 7 对童男童女到克里特岛。这些童男童女就是供奉给牛头怪吃的。 来自雅典的王子忒修斯立志要为民除害,于是他与当年选献的童男童女一起来到克里特岛,忒修斯上岸后爱上了美丽的阿里阿德涅公主。当公主知道忒修斯的使命后,就送给他一把魔剑和一个线球,以免忒修斯受到牛头怪的伤害。 勇敢的忒修斯一进入迷宫,就将线球的一端拴在迷宫的入口处,然后放开线团,沿着曲折复杂的通道,向迷宫深处走去。后,他终于找到了牛头怪,并用阿里阿德涅公主给的剑,奋力杀死了牛头怪。然后,他带着童男童女,顺着线路走出了迷宫。在阿里阿德涅公主的帮助下,忒修斯等人一起逃出了克里特岛。 现假设迷宫由一系列交叉路口组成,从一个入口进入后可以沿着 3 个方向走:向左、向前或者向右。迷宫的路径由一系列标识路口的序号组成。为了求解迷宫问题,需要用到回溯的方法,当沿着某一路径一步步走向出口却发现进入死胡同之时,就回溯一步或多步,寻找其他可走的路径。 如下图所示为一个迷宫。从入口 1 的位置出发,沿东走到节点 2 时发现有两条路。标记节点 2,然后继续向东走,直到走到尽头无路可走时回溯到节点 2 尝试另外一种可能的走法,即向右转,沿南走到节点4,这时有3个选择,可以向右、向前或者向左。结果发现向左或向右都走不通,则再次退回到节点 4 向前,沿南走到节点 5。如此继续下去,则可以终到达出口 10 的位置。下面给出了求解迷宫问题的示例程序。 迷宫

由此例可以简单总结出应用回溯法的一般思路。即首先必须明确定义问题的解空间。而问题的解空间应当至少包含该问题的一个解。对于迷宫问题,各路口的集合就是问题的解空间。对于每个节点遍历尝试的可能性多不超过 3 种,即左转、右转和向前,仅当这 3 种可能性都被否定时才回溯至前一状态。在定义了问题的解空间之后还应当考虑如何将解空间进行有效的组织,以使得回溯法能够方便地搜索这些子空间中的节点。在必要的时候还应当注意优化搜索的策略以提高算法的实时性。 当以上准备完成之后就从出发点开始,以深度优先的方式对整个解空间进行搜索。该出发点随即被更新为当前的扩展节点。也就是从一个可能的路径进行深入以产生下一个新的节点,并将新的节点更新为扩展节点。一旦当前的扩展节点既不能得出整个问题的一个解,也不能再继续向更深的方向进行搜索,那么就返回上一个节点并将上一个节点重新更新为新的扩展节点,再尝试另一种可能性。回溯法正是采用这种工作方式以递归为基础在解空间内开展系统的搜索工作,直到求出问题的解或者表明问题无解为止。需要说明的是因为回溯法是对解空间的深度优先搜索,所以可以考虑使用树结构的递归遍历方式完成搜索工作。当然这并非是唯一的途径,也可以考虑使用树结构的非递归遍历方法,那样整个回溯过程将以迭代的形式完成。 ---- 八皇后问题 ---- 八皇后问题是一个古老而著名的问题,它是回溯法的典型例题。该问题早是由德国棋手马克斯•贝瑟尔(Max Bezzel)于 1848 年提出。之后陆续有数学家对其进行研究,其中包括德国数学家卡尔•弗里德里希•高斯(Karl Friedrich Gauss)和格奥尔格•康托(Georg Cantor),该文是这样描述的:在 8 行 8 列的国际象棋棋盘上摆放着 8 个皇后。若 2 个皇后位于同一行、同一列或同一对角线上,则称它们为互相攻击。在国际象棋中皇后是强大的棋子,因为它的攻击范围大,下图显示了一个皇后的攻击范围。

现在要求使这 8 个皇后不能相互攻击,即任意 2 个皇后都不能处于同一行、同一列或同一对角线上,问有多少种摆法。高斯认为有 76 种方案。1854 年在柏林的象棋杂志上不同的作者发表了 40 种不同的解,后来有人用图论的方法解出 92 种结果。现代教学中,把八皇后问题当成一个经典递归算法例题。下图显示了两种 8 个皇后不相互攻击的情况。

现在来看如何使用回溯法解决八皇后问题。这个算法将在棋盘上一列一列地摆放皇后直到 8 个皇后在不相互攻击的情况下都被摆放在棋盘上,算法便终止。当一个新加入的皇后因为与已经存在的皇后之间相互攻击而不能被摆在棋盘上时,算法便发生回溯。一旦发生这种情况,就试图把后放在棋盘上的皇后移动到其他地方。这样做是为了让新加入的皇后能够在不与其他皇后相互攻击的情况下被摆放在棋盘的适当位置上。 如图下图的情况(需要发生回溯的情况),尽管第 7 个皇后不会与已经放在棋盘上的任何一个皇后发生攻击,但仍然需要将它移除并发生回溯,因为无法为第 8 个皇后在棋盘上找到合适的位置。 算法的回溯部分将尝试移动第 7 个皇后到第 7 列的另外一点来为第 8 个皇后在第 8 列寻找一个合适的位置。如果第7个皇后由于在第7列找不到合适的位置而无法被移动,那么算法就必须去掉它并回溯到第 6 列的皇后。终算法不断重复着摆放皇后和回溯的过程直到找到问题的解为止。

由于回溯法也是在试图搜索整个解空间中的所有可能的选择,于是有人会误认为回溯法与穷举法差不多,但事实上回溯法要较穷举法在效率上高出很多。这里就以一种简单的估算方法来对八皇后问题进行一下分析。首先采用穷举法,那么可以容易得出该问题解空间的节点总数为 109601。然后在使用回溯法的前提下随机选取 20 条路径,分别估计它们的节点个数并求得总数的平均值。因为已知八皇后问题共有 92 种解,所以选择 20 种随机路径进行估计所得出的结果已经可以较为接近实际数值。经过计算得出回溯法产生的节点数的平均值约为 1740,这相对于 109601 不足 2%。可见回溯法作为一种跳跃性和系统性相结合的搜索方法是具有较高效率的。 下面给出了求解八皇后问题的示例程序。

相关图书

《算法之美:隐匿在数据 结构背后的原理(C++版)》 45个常用算法、22个经典问题 点拨面试热点,生活化实例详解 通俗易懂,系统全面 左飞 著 
2016年3月出版

√ 探秘算法世界、求索数据结构之道 √ 汇集经典问题、畅享编程技法之趣 √ 点拨求职热点、敲开业界名企之门

时间: 2024-10-31 22:40:51

读书时间--回溯法浅析:逆向思维领略算法之美的相关文章

五大常用算法——分治法,动态规划,回溯法,分支界限法,贪心算法

分治算法一.基本概念    在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)--     任何一个可以用计算机求解的问题所需的计算时间都与其规模有关.问题的规模越小,越容易直接求解,解题所需的计算时间也越少.例如,对于n个元素

回溯法 -数据结构与算法

1.回溯法算法思想:   定义:         回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为"回溯点". 1.回溯法适用:有许多问题,当需要找出它的解集(全部解)或者要求回答什么解是满足某些约束条件的最优解时,往往要使用回溯法. 2.有组织的穷举式搜索:回溯法的基本做法是搜索或者有的组织穷尽搜索.它能避免搜索所有的可能

常用算法:C#用回溯法找出n个自然数中取r个数的全排列

回溯法也称为试探法,该方法首先暂时放弃关于问题规模大小的限制,并将问题的候选解按某种顺序逐一枚举和检验.在回溯法中,放弃当前候选解,寻找下一个候选解的过程称为回溯. 本实例是用回溯法输出n个自然数中以r个数全排列.代码如下: <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />public void Arrange(int n, int r) int i = 0, j; st

优化-01背包回溯法计算起来非常慢,有木有算法大大帮忙看看

问题描述 01背包回溯法计算起来非常慢,有木有算法大大帮忙看看 #include #include #include #include #include #include using namespace std; int n;//物品数量 double c;//背包容量 double v[9000];//各个物品的价值 double w[9000];//各个物品的重量 double cw = 0.0;//当前背包重量 double cp = 0.0;//当前背包中物品价值 double best

算法——回溯法

回溯法 回溯法有"通用的解题法"之称.用它可以系统地搜索一个问题的所有解或任一解.回溯法是一种即带有系统性又带有跳跃性的搜索算法.它在问题的解空间树中,按深度优先策略,从根节点出发搜索解空间树.算法搜索至解空间树的任一结点时,先判断该节点是否包含问题的解.如果不包含,则跳过对以该节点为根的子树的搜索,逐层向其它祖先节点回溯.否则,进入该子树,继续按照深度优先策略搜索.回溯法求问题的所有解时,要回溯到根,且根节点的所有子树都已被搜索遍才结束.回溯法求问题的一个解时,只要搜索到问题的一个解

算法详解之回溯法具体实现_C 语言

理论辅助: 回溯算法也叫试探法,它是一种系统地搜索问题的解的方法.回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试.用回溯算法解决问题的一般步骤为: 1.定义一个解空间,它包含问题的解. 2.利用适于搜索的方法组织解空间. 3.利用深度优先法搜索解空间. 4.利用限界函数避免移动到不可能产生解的子空间. 问题的解空间通常是在搜索问题的解的过程中动态产生的,这是回溯算法的一个重要特性. 还是那个基调,不喜欢纯理论的东西,喜欢使用例子来讲诉理论,在算法系列总结:动态规划(

并行计算思考----回溯法求解数独问题

 1.Intel Parallel Studio 环境下的并行程序设计 书官方网站的详情页: http://www.wrox.com/WileyCDA/WroxTitle/Parallel-Programming-with-Intel-Parallel-Studio-XE.productCd-0470891653.html 可以下载相关代码 2.在使用并行计算来优化自己的串行程序之前,我们需要思考以下几个方面的问题 什么情况下需要并行? 并行能够带来多少性能的提升? 编码和调试的时间成本?

python 回溯法 子集树模板 系列 —— 13、最佳作业调度问题

问题 给定 n 个作业,每一个作业都有两项子任务需要分别在两台机器上完成.每一个作业必须先由机器1 处理,然后由机器2处理. 试设计一个算法找出完成这n个任务的最佳调度,使其机器2完成各作业时间之和达到最小. 分析: 看一个具体的例子: tji 机器1 机器2 作业1 2 1 作业2 3 1 作业3 2 3 最优调度顺序:1 3 2 处理时间:18 这3个作业的6种可能的调度方案是1,2,3:1,3,2:2,1,3:2,3,1:3,1,2:3,2,1: 它们所相应的完成时间和分别是19,18,2

批处理作业调度-回溯法

问题描述: 给定n个作业,集合J=(J1,J2,J3).每一个作业Ji都有两项任务分别在2台机器上完成.每个作业必须先有机器1处理,然后再由机器2处理.作业Ji需要机器j的处理时间为tji.对于一个确定的作业调度,设Fji是作业i在机器j上完成处理时间.则所有作业在机器2上完成处理时间和f=F2i,称为该作业调度的完成时间和. 简单描述: 对于给定的n个作业,指定最佳作业调度方案,使其完成时间和达到最小.算法设计: 从n个作业中找出有最小完成时间和的作业调度,所以批处理作业调度问题的解空间是一棵