【问题标题】:Understanding Python Recursion了解 Python 递归
【发布时间】:2019-10-10 16:00:29
【问题描述】:

我很难理解 python 递归。我们遇到了一个问题,我这辈子都无法理解。

问题: 编写一个 python 程序,将解决方案打印到文本区域中提供的游戏板。文本区域中的数字创建了一个游戏板。示例:

|1|4|2|3|6|1|1|2|

要赢得比赛,您需要从头开始,准确地结束。您只能移动单元格中的数字,但只要您留在棋盘上,就可以向左或向右移动。

讨论: 所以从任何给定的位置,你可以向右走,也可以向左走。所以要求是: 1) 如果 index board length -- 返回游戏不能获胜 2) 如果单元格值为零,则游戏结束,除非它是棋盘上的最后一个单元格 3) 每个 Cell 只能被访问一次。

到目前为止我所拥有的:

def playGame(_currentIndex):
    _path.append(_currentIndex)
    print("Current Path --> ")
    print(_path)
    _currentIndex=_gameBoard[_currentIndex]
    win = False
    while win == False:
        #Check Can win from current position directly?
        if _currentIndex-1 or _currentIndex+1 == end:
            print("Game can be won directly from current position!")
            print(_path)
            return True
        #Check for Loss:
        #Index Too High or Too Low
        if _currentIndex < 0 or _currentIndex > end:
            print("Game cannot be won")
            exit(code = 0)
        #Cell Value of Zero
        elif _gameBoard[_currentIndex] == 0:
            #If Cell Value Zero and Is Last Cell
            if _currentIndex == end:
                print("Game won!")
                print(_path)
                win = True
            else:
                print("Game cannot be won")
                exit(code=0)
        #Cell has been visited before
        elif (_currentIndex in _path):
            print("Game cannot be won")
            exit(code = 0)
        elif (playGame(-(_currentIndex))) or (playGame(-(_currentIndex))):
            win = True
        else:
            win = False

使用 [1,1] 的游戏板时,我的递归深度超出了我的范围,但不知道为什么?

编辑---所以我理解递归,如果它只做一件事,例如:

def rec(int):
    if int == 0:
        return 0
    else:
        num = int + (int - 1)
        print(num)
        rec(int - 1)


rec(3)

这工作正常并打印出来: 5 3 1

但是如果我想通过加法或减法直到 int == 0 或 int == 10 来做到这一点呢?

【问题讨论】:

  • 接近结尾的那一行是什么? elif (playGame(-(_currentIndex))) or (playGame(-(_currentIndex))):
  • 基本上就是将索引向左或向右移动。我们得到的伪代码是:我从当前位置“可以赢”游戏如果我“可以赢”到右边或我“可以赢”到左边。 def canWin( ...current... ): # 代码返回 True 或 False 如果你能赢或不能赢直接在这里 if canWin(...left...) or canWin(...right...):返回 True 否则返回 False
  • if _currentIndex-1 or _currentIndex+1 == end: 不正确。我不确定你打算在那里做什么,也许是if _currentIndex-1 == end or _currentIndex+1 == end:
  • @Fxguy1 仔细阅读,您正在测试if X or X
  • 一般来说,while variable == False: 的风格很差。最好使用while True:,当你想停止循环时使用break

标签: python recursion


【解决方案1】:

当没有特定状态的基本情况时,会发生递归深度错误。这通常表明您没有处理所有基本案例,或者您的基本案例不够充分。

由于我没有游戏板数据,我无法告诉你你没有捕捉到什么情况;然而,这意味着你永远不会从函数中返回,这意味着你被困在那个 while 循环中。

【讨论】:

  • Shon - 关键是这项工作与游戏板数据无关。无论游戏从哪里开始,它都应该可以工作。所以我仍然在为基本案例的整个想法而苦苦挣扎。我“认为”基本情况是从当前位置开始,向右还是向左会赢得比赛?
  • 如果你的当前索引是最后一个可能的索引,如果我正确理解了你的游戏,则基本情况。当你到达第二个 2 时你赢了吗? |1|4|2|3|6|1|1|2| &lt;- here。我认为您的代码的主要问题是几个ifs 没有正确编写。
  • 没错。如果 _currentIndex == 最后一个索引,则游戏获胜。否则无法赢得比赛。那么我如何从那里向后工作呢?基本上如果 _currentIndex != last index 然后检查向右移动或向左移动是否有效,然后重复。
  • @Fxguy1 我的意思不是你的函数应该只处理一个特定的板。在不知道你传递给触发递归深度错误的函数的数据的情况下,我无法告诉你什么基本情况没有被处理。
【解决方案2】:

第一个解决方案

给定一个输入谜题,q

  1. 如果当前索引i 超出范围或之前在mem(基本情况)中看到过,则返回False
  2. 否则(归纳)i范围内,并且以前没有见过。如果i 等于最后一个索引,则返回解,sln
  3. 否则(归纳)i范围内,以前没有见过,并且不是最终索引。将i 附加到memsln 并递归尝试求解i+q[i] i-q[i]

上面的解释对应下面编号的cmets-

def solve(q, i=0, mem=set(), sln=()):
  if i in mem or i<0 or i>=len(q):  #1
    return False
  elif i == len(q)-1:               #2
    return (*sln, i)
  else:                             #3
    return \
      solve(q, i+q[i], mem|{i}, (*sln, i)) \
    or \
      solve(q, i-q[i], mem|{i}, (*sln, i))
puzzle = [1,4,2,3,6,1,1,2]

print(solve(puzzle))
(0, 1, 5, 6, 7)

答案是到达最终索引的索引序列-

 0 1 2 3 4 5 6 7     <-- index
[1,4,2,3,6,1,1,2]    <-- puzzle
 ^ ^       ^ ^ ^     <-- (0, 1, 5, 6, 7)

必须使用mem 以避免多次检查相同的索引。即,某些输入谜题可能会导致求解器进入无限循环。如果我们之前已经访问过索引 i 一次,我们知道这是一个死胡同,所以我们返回 False

solve([3,0,1,1,1,0,3])
False

在这个例子中,我们看到解决方案反弹,然后最终落在最后一个索引 -

solve([3,6,1,2,4,0,3,1,0,9])
(0, 3, 1, 7, 6, 9)

所有解决方案

上面的程序只找到第一个解决方案。生成器允许我们对上面的程序进行微小的修改并让它找到所有解决方案 -

def solve(q, i=0, mem=set(), sln=()):
  if i in mem or i<0 or i>=len(q):
    return                                   # <- dead-end; backtrack
  elif i == len(q)-1:
    yield (*sln, i)                          # <- found solution    
  else:
    yield from \                             # <- hop right
      solve(q, i+q[i], mem|{i}, (*sln, i))
    yield from \                             # <- hop left
      solve(q, i-q[i], mem|{i}, (*sln, i))

puzzle = [1,4,2,3,6,1,1,2]

print(list(solve(puzzle)))
[(0, 1, 5, 6, 7)]

我们已经创建了所有可能的解决方案的列表。对于这个特殊的谜题,我们现在知道没有其他可能解决它。不过,让我们看一下user11302163刚刚发布的谜题-

puzzle = [10, 8, 6, 4, 2, 6, 1, 3, 5, 7, 9, 5, 1, 1, 2, 1, 1]

for sln in solve(puzzle):
  print(sln)
(0, 10, 1, 9, 16)
(0, 10, 1, 9, 2, 8, 13, 14, 16)
(0, 10, 1, 9, 2, 8, 13, 14, 12, 11, 16)
(0, 10, 1, 9, 2, 8, 13, 12, 11, 16)
(0, 10, 1, 9, 2, 8, 3, 7, 4, 6, 5, 11, 16)

生成器特别擅长解决这类问题。例如,也许我们想找到第一个 10 步或更多步的解决方案 -

for sln in solve(puzzle):
  if len(sln) >= 10:
    print(sln)
    break
(0, 10, 1, 9, 2, 8, 13, 14, 12, 11, 16)

生成器赋予调用者控制权,允许我们在对特定结果感到满意后立即暂停/停止计算。此时,不会尝试其他解决方案,可能会节省许多浪费的计算。

【讨论】:

  • 谢谢!我在一个方向上得到它,以及上面的第 1 步和第 2 步,这是我正在努力解决的第 3 步或来回弹跳的概念!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-27
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多