zoukankan      html  css  js  c++  java
  • 0806博文——初识时间回溯算法

    leetcode上的一道题为我打开了时间回溯算法的大门:

    时间回溯算法之全排列

    题目

    给定一个没有重复数字的序列,返回其所有可能的全排列。

    示例:

    输入: [1,2,3]
    输出:
    [
    [1,2,3],
    [1,3,2],
    [2,1,3],
    [2,3,1],
    [3,1,2],
    [3,2,1]
    ]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/permutations
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    思路

    拿到这道题的时候我是想通过set()集合将其内部元素全部打乱随机化,然后再用一个for循环,每次循环用list()将集合强转成列表,然后if判断是否在列表lt中,如果不在,就往里面添加,如果在就continue进行下一次循环。

    在我的想法中,由于集合的随机属性,总会有某一时间点,将所有的顺序全部取到并强转成list存入lt中,那我只需要知道输入的列表的全排列数量就很容易完成这个算法。

    但是!长度为n的数列全排列的总数量为n!,如果再写一个阶乘的函数就增加了整个算法的复杂度。并且,在原有的想法中,输入的列表经过set转换后应该会随机化,但是实际并没有成功随机化。后来了解到是因为哈希冲突,小数字在集合中并不会乱序

    时间回溯算法

    在参考了评论与标准答案后,我发现大部分答案都出现了一种算法,叫时间回溯算法。本着倔强的不服输精神,我决定将其学会并且应用在本题中(毕竟也不是第一次看到这种解题法了,学会了可以增强我以后的解题思路)

    在所有搜索到的资料中,我觉得写的最好最易懂的是https://www.jianshu.com/p/dd3c3f3e84c0

    什么是时间回溯算法

    这篇博文用比较易懂的白话解答了什么是时间回溯算法:

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

    白话:回溯法可以理解为通过选择不同的岔路口寻找目的地,一个岔路口一个岔路口的去尝试找到目的地。如果走错了路,继续返回来找到岔路口的另一条路,直到找到目的地。

    时间回溯算法编程技巧

    时间回溯算法一般包含两个函数,一个核心函数用来递归并按顺序寻路。一个辅助函数用来检查解的正确与否(可理解为检查是否为死路),一般辅助函数返回布尔值,若为True则按照路线继续下一层的寻路,若为False则返回上一层。

    1. 核心函数是一个递归函数,首先应当编写递归结束的条件,也就是找到了问题的解的情况对应的代码(如增加解的个数,输出当前找到的解等)。其次应当遍历当前步骤可能的解,若当前步骤的解满足则添加当前解并进行递归。最后一定要记得递归退出意味着下一轮没有解符合要求,于是当前解也不符合要求,因此需要消除前面添加当前解的步骤的影响,如上文的“清零,以免回溯的时候出现脏数据”这个步骤(回溯思想的体现)。
    2. 每一步的解的检查需要一个辅助函数,这个可以根据题意进行编写,如下文四皇后算法中的check函数。

    实例一:四皇后问题

    原博文中用了两个实例来进一步解读算法,这里我就用其中一个四皇后算法来解析时间回溯算法到底是干了个什么。

    四皇后问题

    本质就是在一个4X4的国际象棋棋盘上如何放下四个皇后并且这四个皇后无法互相攻击。

    问题简化:下面我们将八皇后问题转化为四皇后问题,并用回溯法来找到它的解
    目的:在4x4棋盘上,使得4个皇后不能在同行同列以及同斜线上。

    step1
    尝试先放置第一枚皇后,被涂黑的地方是不能放皇后

    img

    step2
    第二行的皇后只能放在第三格或第四格,比方我们放第三格,则:

    此时我们也能理解为什么叫皇后问题了,皇后旁边容不下其他皇后。而在同一个房间放下四个皇后确实是个不容易的问题。

    img

    step3
    可以看到再难以放下第三个皇后,此时我们就要用到回溯算法了。我们把第二个皇后更改位置,此时我们能放下第三枚皇后了。

    img

    step4
    虽然是能放置第三个皇后,但是第四个皇后又无路可走了。返回上层调用(3号皇后),而3号也别无可去,继续回溯上层调用(2号),2号已然无路可去,继续回溯上层(1号),于是1号皇后改变位置如下,继续回溯。

    img

    这就是回溯算法的精髓,下面我们直接用代码的方式解决这个问题。

    四皇后问题的python解法

    由于原博文是用java写的代码,我需要自行将其转化为python的解法,这过程中也强化了我对时间回溯算法的理解。

    def check(current_row):  # 检查是否与之前放的queen冲突 这个current_row是当前行
        for i in range(0, current_row):
            if queen[current_row] == queen[i]:
                return False
            if abs(current_row - i) == abs(queen[current_row] - queen[i]) == 1:
                return False
            # if current_row-queen[i]==queen[current_row]-i:#列相同 因为是按current_row遍历,所以current_row不可能相同
            #     return False
        return True
    
    
    def n_queen_problem(current_row, n):#按行遍历
        for i in range(clo): #在每一行中按列遍历
            queen[current_row] = i   #将当前准备下子的位置放入queen(注意:此值会根据进度刷新)
            if check(current_row) == True: #若位置和之前下的子无冲突
                if current_row == n - 1 and len(queen) == n: 
                    res.append(queen.copy())  # 要用浅拷贝,不然会变
                    return queen.items()
                n_queen_problem(current_row + 1, n) #则将current_row+1,开始确定下一个皇后的位置
    
    n = 4  # n皇后问题
    clo = n  # 0 1 2 3 4 ....列数
    queen = {}  # # queen为字典 储存int类型的 current_row:colum
    res = list()
    n_queen_problem(0, n)  # 起始遍历行0  #返回值为什么是none 我也不知道
    print(res)
    
    

    代码解释:

    ​ 在写代码之前,我们先得理清最基本的问题,皇后的走法决定了任何两个皇后不可能在同一行或者同一列

    ​ 于是在我设计的算法中,整个核心算法主要是按行遍历的,然后按每一行中按列遍历。current_row代表了当前遍历到的行,在四皇后问题中,current_row的值按0,1,2,3取,并且在对应了目前正在确定第(current_row+1)个皇后的位置。

    ​ 回溯的实现主要是通过递归,这个只要将代码放入debug就可以理解了。

    ​ 如何确定自己已经取到了值呢,经过多次debug观察我确定了只有当current_row=n(本题=4)-1(也就是下第四个子的时候),并且通过了check检查确认了第四个子在这个位置是无冲突的,并且queen的长度为4时(也就是有四个位置存放在queen中),queen中的值就是可以解决四皇后位置的四个坐标,将这个坐标放入res即可。

    本解法未能解决的问题

    1:不知道为什么,如果不采用res存放答案,直接采用return的话,返回的是None

    2:虽然采用return的话返回的值时None,但是如果不写return queen.items的话居然会少一个解

    3:current_row=3时,不知为什么,还会加一使current_row=4,然后queen中会出现五个值(这也是为什么我在取解时设定了queen的长度)

    由于时间回溯算法实在太复杂,debug一步步看过去头都要大了,所以暂时放着,如果我以后对它有了更深的理解或许这些问题能迎刃而解。这也是为什么本文主要初识回溯算法。

    总结

    时间回溯算法很有意思,并且应用范围非常广。这是我在学python之后碰到的比较有意思的算法,弄懂了之后,许多题就突然变得简单了很多。

  • 相关阅读:
    Pandas matplotlib 无法显示中文
    Pandas matplotlib 无法显示中文
    Python2/3 list set性能测试
    Python2/3 list set性能测试
    汉语自然语言处理工具包下载
    汉语自然语言处理工具包下载
    多版本中文停用词词表 + 多版本英文停用词词表 + python词表合并程序
    多版本中文停用词词表 + 多版本英文停用词词表 + python词表合并程序
    利用 TensorFlow 实现上下文的 Chat-bots
    利用 TensorFlow 实现上下文的 Chat-bots
  • 原文地址:https://www.cnblogs.com/Sheppard/p/11311402.html
Copyright © 2011-2022 走看看