递归回溯
测试
回溯可以被认为是选择性的<一个target="_blank" rel="nofollow" href="//www.parkandroid.com/wiki/trees-basic/">树/图形一个>遍历方法。树是表示某个初始起始位置(父节点)和最终目标状态(其中一个叶子)的一种方式。回溯使我们能够处理这样的情况:一种原始的蛮力方法可能会爆炸成无数可供考虑的选择。回溯是一种精细的蛮力。在每个节点上,我们排除明显不可能的选项,然后递归地只检查那些有可能的选项。这样,在树的每个深度,我们都减少了将来需要考虑的选择数量。
假设你到了一片坏叶子。您可以通过撤销您最近的选择来继续搜索良好的叶子,并尝试在该组选项中的下一个选项。如果您的选项耗尽,请撤销此处的选择,并在该节点上尝试其他选择。如果您最终以剩余选项的obot,则没有良好的叶子。
回溯是解决约束满足问题的关键,如填字游戏、口头算术、数独和许多其他谜题。它还可用于求解背包问题、文本解析等组合优化问题。
关于回溯的有趣是我们只需要备份,以达到以前未开发的替代方案的先前决策点。一般来说,这将是最近的决定点。最终,越来越多的这些决策点将得到充分探索,我们将不得不进一步回顾。如果我们一直返回我们的初始状态并从那里探索了所有替代方案,我们可以得出结论是无法解决的特定问题。在这种情况下,我们将完成详尽递归的所有工作,并已知没有可行的解决方案。
置换
给定的一组项目的置换是元件的一定重新排列。可以显示一个数组 一个长度 N有 n!排列。例如数组['J','O','N']有以下排列:
1 2 3 4 5 6 |
|
这里应用的回溯算法相当直接,因为调用不受任何约束。我们不是从一个不需要的结果返回,我们只是返回到以前的状态,而不过滤掉不需要的输出。下面的图片和代码对此进行了更详细的阐述:
如图所示,算法基于交换。实施后,在打印置换之后,回溯部分将物品交换给上一个位置。
下面的python代码展示了如何做到这一点:
12 3 4 5 6 7 8 9 10 11 12 13 |
|
它输出
1 2 3 4 5 6 7 |
|
迷你数独
Sudoku是一个逻辑拼图,其中目标是用数字填充网格,以便每个列,每行和构成网格的子网格中的每个子网格都包含来自的所有数字 1来 n。相同的单个整数可能在同一行,列或子网格中出现两次。
让我们看一个简化的 3.×3.迷你版原始数独拼图。在这里,每个单元是一个含有的子级 1元素与是平凡的不同。这意味着我们只需要检查行和列是否包含整数 1, 2和 3.没有重复。
下面是一个迷你数独谜题的例子(左)和它的解决方案(右)
现在应该是显而易见的,这种拼图是递归逆发的成熟。让我们现在铺设伪电讯,这将有助于我们解决它。
12 3 4 5 6 7 8 9 10 11 12 13 |
|
上面的代码是回溯的一个经典示例。如果给定的板董事会
应该在函数之外。
实现一个实际的迷你 3.×3.解算器,并使用它打印解\s到下面的难题
python中的示例解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64从itertools.进口*从复制进口复制defis_distinct(列表):'''辅助函数为is_solved检查列表中的所有元素是否不同(虽然忽略0s)'''使用=[]为我in.列表:如果我==.0:继续如果我in.使用:返回假使用.附加(我)返回真实defis_valid.(brd):" '检查3x3迷你数独是否有效' "为我in.范围(3.):行=[brd[我] [0],brd[我] [1],brd[我] [2]如果不is_distinct(行):返回假col=[brd[0] [我],brd[1] [我],brd[2] [我]如果不is_distinct(col):返回假返回真实def解决(brd,空虚=9):'''解决了mini-Sudoku复兴开发银行是董事会Empty是空单元格的数量'''如果空虚==.0:#base案例返回is_valid.(brd)为行,colin.产品(范围(3.),重复=2):#遍历每个单元格细胞=brd[行] [col]如果细胞! =0:#如果不是空跳跃继续brd2=复制(brd)为测试in.[1,2,3.]:brd2[行] [col]=测试如果is_valid.(brd2)和解决(brd2,空虚-1):返回真实#backtrack.brd2[行] [col]=0返回假董事会=[[0,0,0],[1,0,0],[0,3.,1]]解决(董事会,9-3.)为行in.董事会:#prints一个解决方案打印行
它输出
1 2 3 4>>> [3,1,2] [1,2,3] [2,3,1]
路径发现
回溯的更实用和众所周知的例子是路径查找。机器人可以例如通过重复在路径上进行迷宫并从导致的位置返回到迷宫中的路径。这当然要求我们以算法与之兼容的方式来代表迷宫。常用方法是使用a 2−d矩阵和其中的值表示障碍物或路径。以下是迷宫解决问题的简化版本,应该有助于澄清回溯算法。
简化路径发现问题
给予A. N×N矩阵的块与一个源的左上块,我们想要找到一个路径从源到目的地(右下块)。我们只能向下和向左移动。路径是 1墙是由 0.
下面是一个迷宫的示例(黑色单元格不可访问)
1 2 3 4 |
|
解决方案是如下:
我们现在可以概述一个回溯算法,返回包含坐标形式的路径的数组。例如,对于上面的图片,解决方案是 (0,0)→(1,0)→(1,1)→(2,1)→(3.,1)→(3.,2)→(3.,3.)
1 2 3 4 5 6 |
|
python中的实现如下所示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 18 19 20 21 22 23 23 24 |
|
8 - 皇后书
与排列问题相反,这里我们将看到一个回溯的例子,它涉及到检查许多约束条件。这听起来不太好,但有大量的限制实际上允许我们大大减少搜索空间,当我们回溯。这也意味着在运行时和性能方面有了实质性的改进。
一个解决方案的例子
图像信用(维基百科):
在计算机科学中回溯的一个非常常见的例子是放置问题 N在棋盘上的皇后,没有两个皇后互相攻击的方式。棋盘由 8×8细胞。皇后队可以垂直,水平和对角线移动。问题在计算解决方案的数量,而不是枚举每个单独的解决方案。
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
由于国际象棋的性质,在覆盖国际象棋板时,我们会在我们找到一个广场时削减搜索空间,我们不能给出我们的配置。回溯搜索在这里最有效,因为它消除了周围 95%搜索空间。上面的伪代码显示了如何做到这一点的细节。
当然,在实际编写实现时,我们担心的是数据结构和实际表示问题的有效方法。下面的python代码展示了如何处理回溯搜索的实现。
8-Queen的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17日18 19 20 21日22日23日24日25日26日27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65从itertools.进口*进口复制类董事会:"一个代表跳棋棋盘的类"def__init__(自我,n):#初始化类自我.董事会=[[没有为我in.范围(8)]为我in.范围(8)]自我.块=集()def__str__(自我):"允许我们打印电路板" "年代=”为我in.自我.董事会:年代+ =str(我)+'\ n'返回年代defPlaceQueen(自我,行,列):"将皇后放置在row,column "自我.块.添加((行,列))自我.董事会[行] [列]=“问”def清除(自我,行,列):“‘从给定的‘行’和‘列’删除一个‘皇后’' '''自我.董事会[行] [列]=没有自我.块.删除((行,列))defisattaching.(自我,piece1,piece2):" '检查piec1是否攻击piec2 ' "如果piece1[0]==.piece2[0]或piece1[1]==.piece2[1]:#检查它们是否在同一行或col返回真实“‘是时候检查他们是否在对角线进攻了这可以通过简单的代数运算有效地完成如果它们在同一对角线上满足包含两点'''的线的等式x1,日元,x2,y2=piece1[1],piece1[0],piece2[1],piece2[0]米=漂浮(y2-日元)/(x2-x1)如果腹肌(米)! =1.0:返回假其他的:b=y2-米*x2返回日元==.米*x1+bdefisAttackingAny(自我,一块):''检查一块是否atacked棋盘上的其他棋子"为piece1in.自我.块:如果自我.isattaching.(一块,piece1):返回真实返回假defNQueens(董事会,n):如果n==.0:返回[董事会]解决方案=[]我=0为piece1in.产品(范围(8),重复=2):如果piece1in.董事会.块:继续如果不董事会.isAttackingAny(piece1):我+ =1新鲜的=复制.深透视(董事会)新鲜的.PlaceQueen(piece1[0],piece1[1])解决方案+ =NQueens(新鲜的,n-1)返回解决方案
8-Queen的问题
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30'''作者:Bipin Oli'''defisvalid.(行,pos):如果pos> =N或行> =N:返回假如果posin.索尔:返回假为我in.范围(len(索尔)):如果pos==.索尔[我]+行-我或pos==.索尔[我]-(行-我):返回假返回真实def解决(pos,质量控制):质量控制- =1索尔.附加(pos)如果质量控制==.0:返回真实#试为我in.范围(N):如果(isvalid.(N-质量控制,我)):如果(解决(我,质量控制)):返回真实索尔.流行()质量控制+ =1返回假皇后区=int(输入().条带())N=皇后区索尔=[]为我in.范围(皇后区):如果(解决(我,N)):打印(索尔)索尔=[]