河内配置在一定的举动

时间:2011-05-04 21:20:32

标签: algorithm

我很想知道towers of Hanoi拼图中给定移动的每个挂钩上有多少个磁盘。例如,给定n = 3个磁盘,我们有这个配置序列,以便最佳地解决难题:

   0 1 2
0. 3 0 0
1. 2 0 1 (move 0 -> 2)
2. 1 1 1 (move 0 -> 1)
3. 1 2 0 (move 2 -> 1)
4. 0 2 1 (move 0 -> 2)
5. 1 1 1 (move 1 -> 0)
6. 1 0 2 (move 1 -> 2)
7. 0 0 3 (move 0 -> 2)

所以给出5号移动,我想返回1 1 1,给出6号移动,我想要1 0 2等。

这可以通过使用经典算法轻松完成,并在一定数量的移动后停止,但我想要更高效的东西。我上面链接的维基百科页面提供了Binary solutions部分下的算法。我认为这是错误的。我也不明白他们如何计算n

如果您按照他们的示例转换磁盘位置,则返回到我想要的位置,它会为4 0 4磁盘提供n = 8并移动号码216。然而,使用经典算法,我得到4 2 2

还有一种在C here中实现的高效算法,它也提供4 2 2作为答案,但它没有文档,我无法访问它基于的文章。

上一个链接中的算法似乎是正确的,但任何人都可以解释它是如何工作的吗?

我也感兴趣的一些相关问题:

  1. 维基百科算法真的错了,还是我错过了什么?他们如何计算n
  2. 我只想知道在某个移动中每个挂钩上有多少个磁盘,而不是每个磁盘上挂的是什么挂钩,这是文献似乎更关注的内容。有没有更简单的方法来解决我的问题?

3 个答案:

答案 0 :(得分:1)

1)如果你的算法说维基百科坏了,我猜你是对的......

2)至于计算每个挂钩中的磁盘数量,为它做一个递归算法是非常直接的:

(未经测试,不优雅且可能充满+ -1错误代码如下:)

function hanoi(n, nsteps, begin, middle, end, nb, nm, ne)
    // n = number of disks to mive from begin to end
    // nsteps = number of steps to move
    // begin, middle, end = index of the pegs
    // nb, nm, ne = number of disks currently in each of the pegs

    if(nsteps = 0) return (begin, middle, end, nb, nm, ne)

    //else:

    //hanoi goes like
    // a) h(n-1, begin, end, middle) | 2^(n-1) steps
    // b) move 1 from begin -> end   | 1 step
    // c) h(n-1, middle, begin, end) | 2^(n-1) steps
    // Since we know how the pile will look like after a), b) and c)
    // we can skip those steps if nsteps is large...

    if(nsteps <= 2^(n-1)){
        return hanoi(n-1, nsteps, begin, end, middle, nb, ne, nm):
    }
    nb -= n;
    nm += (n-1);
    ne += 1;
    nsteps -= (2^(n-1) + 1);
    //we are now between b) and c)

    return hanoi((n-1), nsteps, middle, begin, end, nm, nb, ne);

function(h, n, nsteps)
    return hanoi(n, nsteps, 1, 2, 3, n, 0, 0)

如果你想要效率,它应该尝试将其转换为迭代形式(不应该很难 - 你不需要保持堆栈)并找到一种方法来更好地表示程序的状态,而不是不再使用6+变量。

答案 1 :(得分:1)

你可以利用两个权力位置很容易知道的事实。对于T大小的塔,我们有:

Time          Heights
2^T-1      |  {  0,   0,   T }
2^(T-1)    |  {  0, T-1,   1 }
2^(T-1)-1  |  {  1, T-1,   0 }
2^(T-2)    |  {  1,   1, T-2 }
2^(T-2)-1  |  {  2,   0, T-2 }
2^(T-2)    |  {  2, T-3,   1 }
2^(T-2)-1  |  {  3, T-3,   0 }
          ...
0          |  {  T,   0,   0 }

很容易找出你的行动k在哪个等级之间;只需看看log2(k)。

接下来,注意在2 ^(a-1)和2 ^ a-1之间,有T-a磁盘停留在同一个地方(最重的磁盘)。然而,所有其他块都将移​​动,因为在此阶段算法正在移动大小为a的子功率。因此,使用迭代方法。

让书籍保持正确可能有点棘手,但在这里你可以找到任何k的高度,时间复杂度为O(log2(T))。

干杯

答案 2 :(得分:0)

If you look at the first few moves of the puzzle, you'll see an important pattern. Each move (i - j) below means on turn i, move disc j. Discs are 0-indexed, where 0 is the smallest disc.

1 - 0
2 - 1
3 - 0
4 - 2
5 - 0
6 - 1
7 - 0
8 - 3
9 - 0
10 - 1
11 - 0
12 - 2
13 - 0
14 - 1
15 - 0

光盘0每转2圈,从第1个开始移动。光盘1每4圈移动一次,从第2个开始...光盘i每2 ^(i + 1)圈移动,从第2轮开始^岛

因此,在恒定时间内,我们可以确定给定光盘移动了多少次,给定m:

移动=(m + 2 ^ i)/(2 ^(i + 1))[整数除法]

接下来要注意的是每张光盘都以循环模式移动。即,奇数盘每次移动时向左移动(2,3,1,2,3,1 ......),偶数盘向右移动(1,3,2,1, 3,2 ......)

因此,一旦您知道光盘移动了多少次,您就可以通过采用模式3(并进行一些计算)轻松确定它结束的钉子。