【问题标题】:Tower of Hanoi recursive algorithm for four towers in PythonPython中四个塔的汉诺塔递归算法
【发布时间】:2017-06-30 04:24:43
【问题描述】:

我在 python 中为四个凳子实现河内塔游戏的最佳算法时遇到了很多麻烦。

首先,在课堂上,我们解决了三个塔的河内塔算法,我们得到了以下算法:

  • 将除底部盘外的所有盘从源移至中间塔
  • 将底部磁盘从源移动到目标
  • 将除了底部圆盘之外的所有部分从中间凳子移到目标塔

该算法的实际代码(模型属于具有塔和磁盘的 Model 类,具有将磁盘从一个塔移动到另一个的 move 方法)产生:

def move_disks(n, source, intermediate, destination):

    """Move n disks from source to destination
    @param int n:
    @param int source:
    @param int intermediate:

    @param int destination:
    @rtype: None
    """
    if n > 1:
        move_disks(n - 1, source, destination, intermediate)
        move_disks(1, source, intermediate, destination)
        move_disks(n - 1, intermediate, source, destination)
    else: 
        model.move(source, destination)

现在对于四个凳子,我得到了以下内容:

  • 使用所有四个塔将 n- i 磁盘移动到中间凳子
  • 仅使用三个可用的塔将 i 个磁盘从原始塔移动到目标塔
  • 将 n-i 个最小磁盘从中间塔移动到目标塔

手动玩转磁盘和塔,如果 n>=3,我得到 n-i = 2,如果 n =2,我得到 i = 1。由于有 4 个可能的来源和目的地,在我自己的函数中,我有 5 个参数而不是 4 个:

def move_disks(n, source, intermediate, intermediate2, destination):

    """Move n disks from source to destination
    @param int n:
    @param int source:
    @param int intermediate:
    @param int intermediate2:
    @param int destination:
    @rtype: None
    """
    if n > 1:
        move_disks(n - i, source, intermediate 2 destination, intermediate)
        move_disks(1, source, intermediate, intermediate2, destination)
        move_disks(n - i, intermediate, intermediate2, source, destination)
    else:
        print("{} -> {}".format(source,destination)) # used to explicitly follow output in the console
        #model.move(source, destination) --> to be implemented when the function returns the right output

当我运行 n=3 时,我得到:

1 -> 3
1 -> 2
3 -> 2
1 -> 4
2 -> 3
2 -> 4
3 -> 4

它给出的移动数与三塔的解决方案相同。 四个凳子的最佳解决方案应该是:

1 -> 3
1 -> 2
1 -> 4
2 -> 4
3 -> 4

问题肯定来自我对算法的理解,所以我尝试在干擦板上跟踪函数调用几个小时,但无济于事。

对于我还应该做什么或寻找解决此算法的任何提示或技巧?老实说,我迷路了,有点气馁。

【问题讨论】:

  • sourceintermediate 和以下参数的用途是什么?除了在两个提供的 code-sn-ps 中作为递归调用的参数之外,它们在代码中没有使用。
  • 代码的工作方式如下:在基本情况下,它从源 -> 目标移动。如果不是在基本情况下,它必须从源 -> 中间移动,所以中间变成新的“目的地”等等。中级和中级 2 相同。
  • 我明白这一点。我的观点是,这些参数都没有以任何方式修改
  • @Paul:n 以外的参数只是塔的标签。递归发生在 n 上(其他参数只是重新排列)。
  • 对不起,我误解了你的问题。在这个特定代码的上下文中,我们有一个塔和磁盘的模型。 move_disks 只是一个旨在以最少的移动量解决游戏的函数,因此除了我们需要移动的磁盘数量之外,代码中没有任何参数被更改——但是塔上磁盘的位置发生了变化在 Model 类(本质上是带有值列表的字典)中通过基本案例中的 move 方法进行更改。

标签: python algorithm recursion


【解决方案1】:
def move4Poles(begin, end, temp1, temp2, disks):

    if disks == 1:
        print(begin, "to", end)
        return

    if disks == 2:
        move4Poles(begin, temp1, '_', '_', 1)
        move4Poles(begin, end, '_', '_', 1)
        move4Poles(temp1, end, '_', '_', 1)
        return

    if disks >= 3:
        move4Poles(begin, temp2, temp1, end, disks - 2)
        move4Poles(begin, end, temp1, '_', 2)
        move4Poles(temp2, end, temp1, begin, disks - 2)

move4Poles('A', 'D', 'B', 'C', 4)


A to B
A to C
B to C
A to B
A to D
B to D
C to B
C to D
B to D

【讨论】:

  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
【解决方案2】:

我发现您的代码有两个问题。

首先是您使用了变量i,而没有在函数中定义它。它可能在您的环境中具有全局价值,但可能不适用于给定的n。您可能应该让您的函数弄清楚 i 应该是什么并将其分配为局部变量。

第二个问题是您总是使用相同的四塔函数进行递归,而您描述的算法应该在中间步骤中仅使用三塔。这可能意味着您应该保留原始函数(在第一个代码块中),并为您的四塔函数使用不同的名称。

如果我们将您的第一个函数命名为move_disks3,第二个函数命名为move_disks4,我们可以让它工作:

def move_disks3(n, source, intermediate, destination):
    """Move n disks from source to destination
    @param int n:
    @param int source:
    @param int intermediate:
    @param int destination:
    @rtype: None
    """
    if n > 1:
        move_disks3(n - 1, source, destination, intermediate)
        move_disks3(1, source, intermediate, destination)
        move_disks3(n - 1, intermediate, source, destination)
    else:
        print("{} -> {}".format(source,destination))

def move_disks4(n, source, intermediate, intermediate2, destination):
    """Move n disks from source to destination
    @param int n:
    @param int source:
    @param int intermediate:
    @param int intermediate2:
    @param int destination:
    @rtype: None
    """
    if n > 1:
        if n > 2: # I'm not sure this picks the optimal i in all cases, but it does for n=3
            i = 2
        else:
            i = 1
        move_disks4(n - i, source, intermediate2, destination, intermediate)
        move_disks3(i, source, intermediate2, destination)
        move_disks4(n - i, intermediate, intermediate2, source, destination)
    else:
        print("{} -> {}".format(source,destination))

我不确定我是否理解您关于最佳 i 值的陈述,因此如果我在此处出现问题,您可能需要进行一些更改。但是这段代码确实有效,并且确实为n = 3 提供了所需的结果(以及我测试的一些更高的n 值的合理结果):

>>> move_disks4(3, 1, 2, 3, 4)
1 -> 2
1 -> 3
1 -> 4
3 -> 4
2 -> 4

【讨论】:

  • 我在稍后的尝试中确实在本地定义了变量 i,但我没有想到直接在新函数中使用 move_disks3。这段代码肯定有效——唯一的问题是我选择的 i 是否对所有 n 都是正确的。非常感谢!
  • 是的,这个问题很微妙。调用move_discs4(i, source, itermediate2, intermediate1, destination) 实际上与我建议的调用move_discs3 的作用相同,只要i 只是12。如果i 更大,则需要使用move_discs3 调用,否则算法将无法正常工作。
  • 出于好奇,我对给定n 的最佳i 进行了一些调查。首先我使用动态编程,然后在我看到最优值所在的模式之后,我能够找到一个封闭形式的解决方案:i = int((sqrt(8*n + 1) - 1)/2)
  • 你能告诉我是否有可能找到 t 塔的通用解决方案?不是解决方案,只是一个基本的想法。
  • @AniketChowdhury:上面使用的算法应该适用于任何k 数量的钉子。选择一个i 值,然后将n-i 磁盘移动到中间钉(使用目标钉和所有其他中间钉)。接下来使用k-1 钉子将i 磁盘移动到最终钉子(不包括第一步中的中间钉子)。最后,使用所有k 钉将第一步中移动的n-i 磁盘从中间钉移动到目标钉。最佳i 值的计算可能取决于ki,我不确定更高k 的公式是什么。
猜你喜欢
  • 1970-01-01
  • 2012-09-11
  • 1970-01-01
  • 1970-01-01
  • 2016-02-29
  • 2013-11-11
  • 2021-07-06
  • 1970-01-01
  • 2011-10-20
相关资源
最近更新 更多