在 Python 中使用递归回溯解决 TweetMazes - 调试帮助请求

问题描述

我一直在自学 Python,作为第一步,看看我是否想将职业从教学完全转变为某种软件开发。到目前为止,一切进展顺利,我一直在做一些小项目来真正扩展我的能力。我最近和我的学生一起做了一些 TweetMazes,并意识到编写一个 Python 程序来解决这些迷宫对我来说是一个很好的挑战。 TweetMazes 是一维数字跳跃迷宫。他们在这里得到了很好的描述。我很快意识到人们解决这些迷宫的方法是在他们的大脑中实现递归回溯,所以我决定制作一个递归回溯算法来解决这些问题。我发现递归很吸引人,而且也很难理解。经过大约一周的代码折腾,我得到了一些既有效又失败的东西,我无法调和,所以我希望有人能帮助我弄清楚发生了什么。

首先,一个 TweetMaze 的例子。这是我编写的用作代码测试用例的简短代码。这是一个包含 6 个数字的列表:

[4,2,3,0]

你从索引 0 开始,它的值为 4。你试图到达列表中的最后一个元素,这是唯一一个值为 0 的元素。列表中每个元素的值告诉你可以向左或向右跳跃多少空间。所以你会像这样解决迷宫:

1. starting at index 0,there's a value of 4: I can't move left,so I have to jump 4 spaces right to index 4.
2. Index 4 has a value of 3. I can't jump right,that's outside the puzzle,so I have to go left 3 spaces to index 1.
3. Again,I can only go right,so I move 2 spaces right to index 3.
4. Index 3 has a value of 2,and I can jump left or right. Pretty obvIoUsly,I jump right 2 spaces to index 5,solving the puzzle. If I had instead gone left,I would have entered a loop.

我可能将此解决方案表示为另一个列表,指示解决方案中采用的索引位置。所以对于上面的谜题,我在我的 python 程序中调用的解栈是:

Solution = [0,4,1,5]

我还制作了一个函数,将解决方显示为迷宫下方的一系列“#”符号,以显示所采取的步骤。上面的迷宫应该是这样的:

    [4,0]
     #
                 #
        #
              #
                    #

这就是事情变得奇怪的地方。如果我通过我的代码运行示例迷宫,一切正常,我得到预期的输出。然而,这是一个非常短的迷宫并且很容易解决,所以为了进一步测试我的代码,我制作了具有 7 个元素而不是 6 个元素的新迷宫。我运行了以下迷宫,代码终止并表现得好像它有一个解决方案,但这完全不是一个有效的解决方案:

    Maze: [3,5,0]        Solution: [0,6]
           #
                    #
                       #
                 #
                          #
              #
                             #

我希望到目前为止一切都清楚。算法很早就出错了。

1. It starts at index 0,and moves right by 3 to index 3. That's correct.
2. It moves right by 1 to index 4. That's a valid move,but that's not how the solution should go. As 
   far as I can tell,there's only one solution,and it moves left by 1 to index 2 at this point. 
   However,the move the program made is at least valid still,so I'll move on to its next step.
3. It's at index 4,which as a value of 4. That means it should move left or right by 4,but it doesn't. 
   It moves left by 2 to index 2. It's totally broken down at this point,and has made an invalid move 
   of the wrong step size. I'm absolutely baffled as to how it does this at all. 
   I just don't see in my code how it Could move with the wrong step size.
4. From this point,however,it's weirdly back on track and finishes the rest of the maze,making moves of the correct step size to get to the end. It's like it just inserted a wrong step
   and then carried on from the correct point. So basically,what on Earth is happening?

为了清楚起见,解决方案应该如下所示:

    Maze: [3,6]
           #
                    #
                 #
                          #
              #
                             #

此时事情变得更加奇怪,因为我决定添加一个解决方案步骤列表,该列表将以文本格式输出算法在每一层递归中所做的事情,以便我可以尝试查看它在做什么,以及那些解决方案步骤其实是对的!它们与解决方案列表的输出或“#”解决方显示不匹配!我将复制并粘贴输出

    Current position: 0. Stepping right by 3 to index 3.
    Current position: 3. Stepping left by 1 to index 2.
    Current position: 2. Stepping right by 3 to index 5.
    Current position: 5. Stepping left by 4 to index 1.
    Current position: 1. Stepping right by 5 to index 6.

    Solution stack shows these positions: [0,6]

    [3,0]
     #
              #
                 #
           #
                    #
        #
                       #

如果您按照所写的步骤进行操作,那就是正确的解决方案!但是解决方案堆栈和# 显示都不正确,显示了奇怪的额外步骤和错误移动。这不是发生这种情况的唯一谜题,因为我已经在其他 7 个元素和更长的谜题上对其进行了测试。我花了大约一个星期的时间通过递归回溯来调整我的方式,最终达到了我无法分辨到底发生了什么的地步,所以我希望有人能解释这种奇怪的行为。坦率地说,我对它的进展感到非常自豪,因为我一直遇到超过最大递归深度的奇怪问题,并设法克服了这些问题。不过,我想让它功能齐全,并了解为什么会出错。更重要的是,我喜欢了解你如何弄清楚到底出了什么问题。我对调试工具和方法还是很陌生,我在理解如何通过递归迭代跟踪程序流程时遇到了一些麻烦。我已经在 GitHub 上发布了我的代码,但因为我对使用 Github 还是很陌生,我也会在下面发布我的代码。非常感谢!

maze_string = '3531440'

'''copy and paste maze strings into the above to change which maze is being run.
Other maze strings to play with:
422230
313210
313110
7347737258493420
'''

maze = [int(i) for i in maze_string]

solution = [0]  #this is the solution stack. append to put on new steps,#pop() to backtrack.
    #algorithm doesn't work if I don't initialize the solution list with the 0 position. Hits max recursion depth.

solution_steps = []

def valid(position,direction):
    #pass in the current position,desired direction of move
    global maze
    if direction == 'right':
        return True if position + maze[position] < len(maze) else False
    elif direction == 'left':
        return True if position - maze[position] >= 0 else False
    
def solve(position):
    global solution
    global maze
    
    #if maze is solved,return True
    if maze[position] == 0:
        return True
    
    #try branches  
    if valid(position,'right') and (position + maze[position] not in solution):
        new = position + maze[position]
        solution.append(new)
        if solve(new):
            solution_steps.append(f'Current position: {position}. Stepping right by {maze[position]} to index {new}.')
            return True
        solution.pop()
        
    if valid(position,'left') and (position - maze[position] not in solution):
        new = position - maze[position]
        solution.append(new)
        if solve(new):
            solution_steps.append(f'Current position: {position}. Stepping left by {maze[position]} to index {new}.')
            return True
        solution.pop()
    
    solution.append('no solution')
    return False

def display_solution():
    global maze
    global solution
    print(maze)
    for i in solution:
        point = ' ' + 3*i*' ' + '#'
        print(point)
        
if __name__ == "__main__":
    
    solve(0)
    
    if solution != 'no solution':
        for step in reversed(solution_steps):
            print(step)
        print(f'\nSolution stack shows these positions: {solution}\n')   
        display_solution()
    else:
        print(solution)

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)