【问题标题】:How would I count the number of moves of this recursive function?我如何计算这个递归函数的移动次数?
【发布时间】:2020-02-23 00:39:16
【问题描述】:

我将如何为这个函数添加另一个参数来计算需要多少次移动,即调用了多少次移动函数?然后返回 (mycurrentanswer, count)。 count 在哪里是添加到我的函数中的新参数,并且必须最初添加为零?

def playHanoi(pos,a,b,c,n):
        if n == 0:
           return pos
        if n == 1:
           return move(pos, a, b)
        else:
           return playHanoi(move(playHanoi(pos,a,c,b,n-1),a,b),c,b,a,n-1)

这是我的移动功能:

def move(pos, a, b):
        if pos[a] == []:
           return pos
        elif pos[b] == [] or pos[b][-1] >= pos[a][-1]:
           pos[b].append(pos[a].pop(-1))
           return pos
        else:
           raise ValueError('This is an illegal move')

我希望功能是这样的

def playHanoi(pos,a,b,c,n,counter):
            if n == 0:
               return pos
            if n == 1:
               return move(pos, a, b)
            else:
               return (playHanoi(move(playHanoi(pos,a,c,b,n-1),a,b),c,b,a,n-1),j)

伪代码

  • 输入格式为{1: [5,4,3,2,1], 2: [], 3: []} 的字典(其中数字是该大小的圆盘,列表左侧是一堆底部),a、b、c 是 Hanoi 问题的极点,所以 1 ,2 和 3 以某种顺序排列。
  • 然后 n 是我想要从 Pile 1 移动到 3 的磁盘数量,即如果 n = 3,我的 pos 将变为 {1: [5,4], 2: [], 3: [3,2, 1]}。
  • 然后是最终组件计数器,它将计算多少“步数”,即我必须移动光盘以达到预期目标的次数。

playHanoi({1: [5,4,3,2,1], 2: [], 3: []},1,3,2,3,0) 的输出将是 ({1: [5,4], 2: [], 3: [3,2,1]}, #ofsteps)

【问题讨论】:

  • 你能添加一些伪代码来显示1)你要修改哪个函数,2)它会是什么样的效果?
  • 我习惯的Hanoi的实现有点不同,没有播放功能和移动功能。它们的参数也更少。您能否展示如何调用 playHanoi() 以使用地点 3 将具有 5 个磁盘的塔从地点 1 移动到地点 2?
  • 我不需要完成整个河内游戏,只需要从一定数量的光盘中获得一定数量的光盘。所有这些都有效,我现在需要计算这样做所需的步骤数
  • 请举例说明您现在如何在不计数的情况下调用该函数,以及您希望如何通过计数调用该函数以及您希望如何检索移动。
  • 我不想检索动作。在这个练习中,我只需要显示结束位置,这就是这样做的。但是现在从这个函数中需要添加一个额外的组件来计算步数。抱歉,我是 python 和堆栈溢出的新手,所以如果我没有提供帮助,我深表歉意。

标签: python recursion


【解决方案1】:

这里有一个解决方案,它返回移动并打印出对 pos 的更改 如果这是您的练习所需要的,请随意将变量重命名为一个字母名称。我个人更喜欢更长的变量名。

def hanoi(pos, p_from, p_to, p_help, n_disks, moves=0):
    if n_disks == 1:
        disk = pos[p_from].pop()
        pos[p_to].append(disk)
        moves += 1
        print("move %d: move a disk from %s to %s: pos: %s" % (moves, p_from, p_to, pos))
        return moves
    moves = hanoi(pos, p_from, p_help, p_to, n_disks-1, moves)
    moves = hanoi(pos,  p_from, p_to, p_help,1 , moves)
    moves = hanoi(pos, p_help, p_to, p_from, n_disks-1, moves)
    return moves

if __name__ == '__main__':
    pos = {1: [5,4,3,2,1], 2: [], 3: []}
    moves = hanoi(pos, 1,3,2,5,0)
    print("Total moves = %d" % moves)
    print("Final pos = ", pos)

输出将如下所示:

move 1: move a disk from 1 to 3: pos: {1: [5, 4, 3, 2], 2: [], 3: [1]}
move 2: move a disk from 1 to 2: pos: {1: [5, 4, 3], 2: [2], 3: [1]}
move 3: move a disk from 3 to 2: pos: {1: [5, 4, 3], 2: [2, 1], 3: []}
move 4: move a disk from 1 to 3: pos: {1: [5, 4], 2: [2, 1], 3: [3]}
move 5: move a disk from 2 to 1: pos: {1: [5, 4, 1], 2: [2], 3: [3]}
move 6: move a disk from 2 to 3: pos: {1: [5, 4, 1], 2: [], 3: [3, 2]}
move 7: move a disk from 1 to 3: pos: {1: [5, 4], 2: [], 3: [3, 2, 1]}
move 8: move a disk from 1 to 2: pos: {1: [5], 2: [4], 3: [3, 2, 1]}
move 9: move a disk from 3 to 2: pos: {1: [5], 2: [4, 1], 3: [3, 2]}
move 10: move a disk from 3 to 1: pos: {1: [5, 2], 2: [4, 1], 3: [3]}
move 11: move a disk from 2 to 1: pos: {1: [5, 2, 1], 2: [4], 3: [3]}
move 12: move a disk from 3 to 2: pos: {1: [5, 2, 1], 2: [4, 3], 3: []}
move 13: move a disk from 1 to 3: pos: {1: [5, 2], 2: [4, 3], 3: [1]}
move 14: move a disk from 1 to 2: pos: {1: [5], 2: [4, 3, 2], 3: [1]}
move 15: move a disk from 3 to 2: pos: {1: [5], 2: [4, 3, 2, 1], 3: []}
move 16: move a disk from 1 to 3: pos: {1: [], 2: [4, 3, 2, 1], 3: [5]}
move 17: move a disk from 2 to 1: pos: {1: [1], 2: [4, 3, 2], 3: [5]}
move 18: move a disk from 2 to 3: pos: {1: [1], 2: [4, 3], 3: [5, 2]}
move 19: move a disk from 1 to 3: pos: {1: [], 2: [4, 3], 3: [5, 2, 1]}
move 20: move a disk from 2 to 1: pos: {1: [3], 2: [4], 3: [5, 2, 1]}
move 21: move a disk from 3 to 2: pos: {1: [3], 2: [4, 1], 3: [5, 2]}
move 22: move a disk from 3 to 1: pos: {1: [3, 2], 2: [4, 1], 3: [5]}
move 23: move a disk from 2 to 1: pos: {1: [3, 2, 1], 2: [4], 3: [5]}
move 24: move a disk from 2 to 3: pos: {1: [3, 2, 1], 2: [], 3: [5, 4]}
move 25: move a disk from 1 to 3: pos: {1: [3, 2], 2: [], 3: [5, 4, 1]}
move 26: move a disk from 1 to 2: pos: {1: [3], 2: [2], 3: [5, 4, 1]}
move 27: move a disk from 3 to 2: pos: {1: [3], 2: [2, 1], 3: [5, 4]}
move 28: move a disk from 1 to 3: pos: {1: [], 2: [2, 1], 3: [5, 4, 3]}
move 29: move a disk from 2 to 1: pos: {1: [1], 2: [2], 3: [5, 4, 3]}
move 30: move a disk from 2 to 3: pos: {1: [1], 2: [], 3: [5, 4, 3, 2]}
move 31: move a disk from 1 to 3: pos: {1: [], 2: [], 3: [5, 4, 3, 2, 1]}
Total moves = 31
Final pos = {1: [], 2: [], 3: [5, 4, 3, 2, 1]}

在我之前的答案下面,这可能对某些人来说很有趣。

进行此类簿记的一种方法是添加一个带有持久对象的参数,例如字典。 该解决方案的实现方式是,如果对获取有关移动次数的信息不感兴趣,则不必传递状态。

def playHanoi(pos,a,b,c,n,state=None):
        if state is None:
            state = {"moves": 0}
        if n == 0:
           return pos
        if n == 1:
           return move(pos, a, b)
        else:
           return playHanoi(move(playHanoi(pos,a,c,b,n-1, state),a,b, state),c,b,a,n-1, state)

def move(pos, a, b, state):
        state["moves"] += 1
        if pos[a] == []:
           return pos
        elif pos[b] == [] or pos[b][-1] >= pos[a][-1]:
           pos[b].append(pos[a].pop(-1))
           return pos
        else:
           raise ValueError('This is an illegal move')


state = { "moves": 0 }
pos = playHanoi({1: [5,4,3,2,1], 2: [], 3: []}, 1,3,2,5, state)
print("I needed %d moves" % state["moves"])

在这种特殊情况下,我可以写:

def playHanoi(pos,a,b,c,n,state={"moves": 0})

并删除了 if 语句,该语句将 dict 分配给状态 if None,

但总的来说,这不是一个好习惯,因为对 dict 的更改仍然存在 Optional parameters in Python functions and their default values 知道,来自 SO 的相当多的代码被复制粘贴到真正的应用程序中,我不想这样做,因为脱离上下文这可能很危险。

没有磁盘簿记的简单版本(pos) 这是一个简单版本的 hanoi,它打印出要移动的磁盘并返回要移动的磁盘总数

def hanoi(n_disks, p_from, p_to, p_help, moves=0):
    if n_disks == 1:
        moves += 1
        print("move %d: move a disk from %s to %s" % (moves, p_from, p_to))
        return moves
    moves = hanoi(n_disks-1, p_from, p_help, p_to, moves)
    moves = hanoi(1, p_from, p_to, p_help, moves)
    moves = hanoi(n_disks-1, p_help, p_to, p_from, moves)
    return moves

moves = hanoi(5, "stackA", "stackB", "stackC")
print("Total moves = %d" % moves)

输出如下:

move a disk from stackA to stackB
move a disk from stackA to stackC
move a disk from stackB to stackC
move a disk from stackA to stackB
move a disk from stackC to stackA
move a disk from stackC to stackB
move a disk from stackA to stackB
move a disk from stackA to stackC
move a disk from stackB to stackC
move a disk from stackB to stackA
move a disk from stackC to stackA
move a disk from stackB to stackC
move a disk from stackA to stackB
move a disk from stackA to stackC
move a disk from stackB to stackC
move a disk from stackA to stackB
move a disk from stackC to stackA
move a disk from stackC to stackB
move a disk from stackA to stackB
move a disk from stackC to stackA
move a disk from stackB to stackC
move a disk from stackB to stackA
move a disk from stackC to stackA
move a disk from stackC to stackB
move a disk from stackA to stackB
move a disk from stackA to stackC
move a disk from stackB to stackC
move a disk from stackA to stackB
move a disk from stackC to stackA
move a disk from stackC to stackB
move a disk from stackA to stackB
Total moves = 31

经过持续讨论后的另一个(希望是最终的)解决方案:

pos 和 j(移动次数)被返回。 只使用了一个函数而不是两个函数。

如果您不想要它,只需删除函数内的print()。 它有助于理解代码的工作原理,但在您的情况下可能不需要。

另一个注释。就像在您的初始代码中一样,已传递给 playHanoi 函数的 dict 也被修改了。

在某些情况下,这是不可取的。 如果有兴趣,我可以提供一个不会修改 initial_pos 的版本。

def playHanoi(pos, a, b, c, n, j=0):
    if n == 1:
        disk = pos[a].pop()
        pos[b].append(disk)
        j += 1
        print("move %d: move a disk from %s to %s: pos: %s" % (j, a, b, pos))
        return pos, j
    pos, j = playHanoi(pos, a, c, b, n-1, j)
    pos, j = playHanoi(pos,  a, b, c,1 , j)
    pos, j = playHanoi(pos, c, b, a, n-1, j)
    return pos, j

initial_pos = {1: [5,4,3,2,1], 2: [], 3: []}
pos, j = playHanoi(initial_pos, 1,3,2,5,0)
print("Final pos", pos)
print("Total j = %d" % j)
print("look even initial_pos has been modified", initial_pos)

【讨论】:

  • 我最初必须将计数组件输入为0,所以我无法这样做
  • I have to input 是什么意思?请尝试更好地解释您希望如何调用该函数,我可能会根据您的确切要求调整该函数。但是请注意,我的上述建议是一种使用递归函数执行统计或累积信息的相当通用的方法。您初始化一个持久对象,将其传递给递归函数,然后读取状态。
  • 所以我的输入将是 playHanoi({1: [5,4,3,2,1], 2: [], 3: []}, 1,3,2,5,0 ) 并且它只会是一个零,它会出现在函数的最后一部分。零是计数,最初计数为 0,因为游戏还没有开始。
  • 所以一旦我定义了我的函数,我希望将前面评论中提到的输入到控制台中,它将输出 ({1: [], 2: [], 3: [5 ,4,3,2,1]}, #ofsteps)
  • 所以pos={1: [5,4,3,2,1], 2: [], 3: []}a=1b=3c=2n=50 应该是参数moves,这在你的函数中到目前为止还没有吗?
【解决方案2】:

我已经设法使用您提供的内容和我正在寻找的内容,并找到了我所追求的解决方案。我敢肯定有无数更优雅的方式,但是,这是需要的。

def playHanoi2(pos,a,b,c,n,j):
    def playHanoi3(pos,a,b,c,n,j):
        if n == 1:
            disk = pos[a].pop()
            pos[b].append(disk)
            j += 1
            return j
        j = playHanoi3(pos, a, c, b, n-1, j)
        j = playHanoi3(pos,  a, b, c,1 , j)
        j = playHanoi3(pos, c, b, a, n-1, j)
        return j
    j = playHanoi3(pos,a,b,c,n,j)
    return (playHanoi(pos,a,b,c,n),j)

【讨论】:

  • 我现在有一个问题,如果 n
  • 我真的不明白你为什么要使用两个函数以及为什么函数是嵌套的。我会用另一个建议来加强我的回答。
  • 答案末尾添加了另一个解决方案。这个“输出”(“返回”的有趣词) pos 和 j 我与问题中建议的变量名称相同。但我再说一遍,一个字母变量并不是一个好主意。特别是像j 这样的东西,它应该是移动的数量。这并不直观,好的程序几乎应该是自我记录的。
  • 只是一般性评论。在函数中声明函数是 Python 中不常做的事情。 (创建闭包和其他一些特殊情况是例外)而且我什至不确定您在任何情况下都必须创建多个函数(请看我改编的答案)
  • 您建议的解决方案的问题是, pos 是一个对象,它是通过引用传递的。这意味着,通过调用 playHanoi3 来更改该 pos。当您稍后调用 playHanoi() 时,它正在使用已修改的 pos。如果您愿意,我可以调整我的答案,即不修改输入 pos
猜你喜欢
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 2021-09-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-24
  • 1970-01-01
相关资源
最近更新 更多