【问题标题】:How does this work? Weird Towers of Hanoi Solution这是如何运作的?奇怪的河内塔解决方案
【发布时间】:2010-02-05 19:37:39
【问题描述】:

当我发现 this 不寻常的河内塔的迭代解决方案时,我迷失了方向:

for (int x = 1; x < (1 << nDisks); x++)
{
     FromPole = (x & x-1) % 3;
     ToPole = ((x | x-1) + 1) % 3;
     moveDisk(FromPole, ToPole);
}

This post 在其中一个答案中也有类似的 Delphi 代码。

但是,就我的一生而言,我似乎找不到一个很好的解释来解释为什么会这样。

谁能帮我理解一下?

【问题讨论】:

  • @jva modulo 3 不能忽略高阶位,例如8 % 3 == 2 但 0 % 3 == 0

标签: algorithm language-agnostic bit-manipulation towers-of-hanoi


【解决方案1】:

河内塔的递归解决方案有效,因此如果要将 N 个磁盘从挂钩 A 移动到 C,首先将 N-1 从 A 移动到 B,然后将底部的一个移动到 C,然后移动再次从 B 到 C 的 N-1 个磁盘。本质上,

hanoi(from, to, spare, N):
  hanoi(from, spare, to, N-1)
  moveDisk(from, to)
  hanoi(spare, to, from, N-1)

很明显 hanoi( _ , _ , _ , 1) 走一步,而 hanoi ( _ , _ , _ , k) 走的步数和 2 * hanoi( _ , _ , _ , k-1) + 1 一样多. 所以解决方案的长度以 1, 3, 7, 15, ... 这与 (1

如果你查看解决方案本身,你会得到 N = 1

FROM   TO
          ; hanoi(0, 2, 1, 1)
   0    2    movedisk

对于 N = 2,你得到

FROM   TO
          ; hanoi(0, 2, 1, 2)
          ;  hanoi(0, 1, 2, 1)
   0    1 ;   movedisk
   0    2 ;  movedisk
          ;  hanoi(1, 2, 0, 1)
   1    2 ;   movedisk

对于 N = 3,你得到

FROM   TO
          ; hanoi(0, 2, 1, 3)
          ;  hanoi(0, 1, 2, 2)
          ;   hanoi(0, 2, 1, 1)
   0    2 ;    movedisk
   0    1 ;   movedisk
          ;   hanoi(2, 1, 0, 1)
   2    1 ;    movedisk
   0    2 ;  movedisk ***
          ;  hanoi(1, 2, 0, 2)
          ;   hanoi(1, 0, 2, 1)
   1    0 ;    movedisk
   1    2 ;   movedisk
          ;   hanoi(0, 2, 1, 1)
   0    2 ;    movedisk

由于解决方案的递归性质,FROM 和 TO 列遵循递归逻辑:如果您在列上取中间条目,则上面和下面的部分是彼此的副本,但数字是置换的。这是算法本身的一个明显结果,它不对挂钩数执行任何算术运算,而只是对它们进行置换。在 N=4 的情况下,中间行位于 x=4(上面标有三颗星)。

现在表达式 (X & (X-1)) 取消设置 X 的最小设置位,因此它映射例如数字从 1 到 7 如下所示:

   1 ->  0
   2 ->  0
   3 ->  2
   4 -> 0 (***)
   5 ->  4 % 3 = 1
   6 ->  4 % 3 = 1
   7 ->  6 % 3 = 0

诀窍在于,由于中间行始终是 2 的精确幂,因此恰好设置了一个位,当您添加中间行值时,中间行之后的部分等于它之前的部分(在这种情况下为 4 ) 到行(即 4=0+4, 6=2+6)。这实现了“复制”属性,中间行的添加实现了排列部分。表达式 (X | (X-1)) + 1 设置右边有 1 的最低零位,并清除这些位,因此它具有与预期相似的属性:

   1 ->  2
   2 ->  4 % 3 = 1
   3 ->  4 % 3 = 1
   4 -> 8 (***) % 3 = 2
   5 ->  6 % 3 = 0
   6 ->  8 % 3 = 2
   7 ->  8 % 3 = 2

至于为什么这些序列实际上会产生正确的挂钩编号,让我们考虑一下 FROM 列。递归解决方案从 hanoi(0, 2, 1, N) 开始,所以在中间行 (2 ** (N-1)) 你必须有 moveisk(0, 2)。现在根据递归规则,在 (2 ** (N-2)) 你需要有 moveisk(0, 1) 和在 (2 ** (N-1)) + 2 ** (N-2) moveisk ( 1、2)。这会为 from pegs 创建“0,0,1”模式,在上表中以不同的排列可见(检查 0,0,1 的第 2、4 和 6 行以及 0,0 的第 1、2、3 行) ,2,以及 1,1,0 的第 5、6、7 行,所有相同模式的置换版本)。

现在,在所有具有这种属性的函数中,它们围绕 2 的幂创建自身的副本但带有偏移量,作者选择了那些产生模 3 的正确排列。这不是一项非常困难的任务,因为三个整数 0..2 只有 6 种可能的排列,并且排列在算法中按逻辑顺序进行。 (X|(X-1))+1 不一定与河内问题密切相关,也不需要如此;它具有复制属性并且恰好以正确的顺序产生正确的排列就足够了。

【讨论】:

【解决方案2】:

antti.huima 的解决方案基本上是正确的,但我想要更严格的东西,而且它太大而无法放入评论中。如下:

第一个注意事项:在这个算法的中间步骤x = 2N-1,“from” peg为0,“to” peg为2N % 3。剩下 2(N-1) % 3 用于“备用”挂钩。 对于算法的最后一步也是如此,所以我们看到实际上作者的算法 是一个轻微的“欺骗”:他们将磁盘从 peg 0 移动到 peg 2N % 3,而不是固定的, 预先指定的“到”挂钩。不用做太多工作就可以改变这一点。

原来的Hanoi算法是:

hanoi(from, to, spare, N):
    hanoi(from, spare, to, N-1)
    move(from, to)
    hanoi(spare, to, from, N-1)

插入 "from" = 0, "to" = 2N % 3, "spare" = 2N-1 % 3,我们得到(抑制%3 个):

hanoi(0, 2**N, 2**(N-1), N):
(a)   hanoi(0, 2**(N-1), 2**N, N-1)
(b)   move(0, 2**N)
(c)   hanoi(2**(N-1), 2**N, 0, N-1)

这里的基本观察是: 在 (c) 行中,钉子恰好是 hanoi(0, 2N-1, 2N, N-1) 移动了 2N 的钉子-1 % 3,即 它们正是 (a) 行的钉子,加上了这个数量

我声称,当我们 运行 (c) 行,"from" 和 "to" 钉子是 (a) 行的相应钉子,偏移了 2N-1 % 3。这个 遵循简单、更一般的引理,在hanoi(a+x, b+x, c+x, N) 中,“from”和“to”钉子恰好从 hanoi(a, b, c, N) 中移动 x。

现在考虑函数
f(x) = (x &amp; (x-1)) % 3
g(x) = (x | (x-1)) + 1 % 3

为了证明给定的算法有效,我们只需要证明:

  • f(2N-1) == 0 和 g(2N-1) == 2N
  • 对于 0 N,我们有 f(2N - i) == f(2N + i) + 2N % 3 和 g(2N - i) == g(2N + i) + 2N % 3.

这两个都很容易展示。

【讨论】:

    【解决方案3】:

    这不是直接回答问题,但是太长了,无法发表评论。

    我总是通过分析您接下来应该移动的磁盘大小来做到这一点。如果您查看移动的磁盘,就会发现:

    1 disk  : 1
    2 disks : 1 2 1
    3 disks : 1 2 1 3 1 2 1
    4 disks : 1 2 1 3 1 2 1 4 1 2 1 3 1 2 1
    

    奇数尺寸总是以与偶数尺寸相反的方向移动,按照 (0, 1, 2, 重复) 或 (2, 1, 0, 重复) 的顺序排列。

    如果你看一下模式,要移动的环是移动次数和移动次数+1的xor的最高位集合。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-31
      • 1970-01-01
      • 1970-01-01
      • 2015-10-08
      • 2016-05-25
      • 2016-03-19
      • 1970-01-01
      相关资源
      最近更新 更多