- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
当我发现 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代码。
然而,对于我的一生,我似乎无法找到一个很好的解释来解释为什么会这样。
谁能帮我理解一下?
最佳答案
Hanoi 塔的递归解决方案是这样的,如果你想将 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 << k) - 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 列遵循递归逻辑:如果您采用列的中间条目,则上面和下面的部分是彼此的副本,但数字排列。这是算法本身的一个明显结果,它不对 Hook 数字执行任何算术,而只是排列它们。在 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
至于为什么这些序列实际上产生了正确的 Hook 编号,让我们考虑 FROM 列。递归解从 hanoi(0, 2, 1, N) 开始,所以在中间行 (2 ** (N-1)) 你必须有 movedisk(0, 2)。现在根据递归规则,在 (2 ** (N-2)) 你需要有 movedisk(0, 1) 和在 (2 ** (N-1)) + 2 ** (N-2) movedisk ( 1, 2).这为从钉子创建了“0,0,1”模式,在上表中以不同的排列可见(检查第 2、4 和 6 行的 0,0,1 和第 1、2、3 行的 0,0 ,2,以及 1,1,0 的第 5、6、7 行,都是同一模式的所有排列版本)。
现在,在所有具有此属性的函数中,它们围绕 2 的幂创建自身的副本但带有偏移量,作者选择了那些产生模 3 正确排列的函数。这不是一项非常困难的任务,因为三个整数 0..2 只有 6 种可能的排列,并且排列在算法中按逻辑顺序进行。 (X|(X-1))+1 不一定与河内问题密切相关,或者不需要;它具有复制属性并且恰好以正确的顺序产生正确的排列就足够了。
关于algorithm - 这是如何运作的?河内奇怪的塔解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2209860/
这是我正在做的作业。我创建了 2 个类来玩汉诺塔。第一个基本上是运行实际游戏类的运行者。 import java.util.Scanner; class TowersRunner { publ
这是我为汉诺塔问题编写的 Python 代码,其中塔必须从左桩转移到中间桩,使用右桩作为备用: def hanoi(n, origin = "1", destination = "2", spare
我正在使用 jQuery 标签!库创建用户“技能”输入表单。我认为这将是一个非常快速和简单的设置,就像大多数 jQuery 库一样,但是我在这个方面遇到了很多麻烦。我尝试按照下面示例中的源代码进行操作
我如何(如果可能)使用 C++11 可变参数编程来定义一系列 vector是在一个函数体内,(或者换句话说,一系列 N 维数组,递减 N 直到 0),就像下面的变量一样? vector>> v; ve
我编写了一个 Ansible playbook,它需要运行它的作业编号作为其参数之一,以便我将对该作业的引用添加到我维护的数据库中。如何获取 Tower 模板以将其传递给剧本? 这是我当前的解决方案,
默认情况下,android 会存储最后 200 个 wifi 连接和 50 个单元位置详细信息。 我使用 WifiManager 中的 getConfiguredNetworks() 获得了 wifi
如何(如果可能)使用 c++11 可变参数编程来定义一系列 vector 's 在函数体中,(或者换句话说,N 维数组的序列,N 's 递减直到 0),就像下面的变量? vector>> v; vec
我们正在 Tensorflow 上运行多 GPU 作业,并评估从基于队列的模型(使用 string_input_producer 接口(interface))到新的 Tensorflow 数据集 AP
我有一个由另一个进程启动的 Ansible 作业。现在我需要检查 Ansible Tower 中当前正在运行的作业的状态。 我可以使用 REST API 使用 /jobs/{id} 跟踪状态是否正在运
我正在使用一个非常棒的插件,名为 jQuery Tagit在我当前项目的开发中。 一切都进行得很顺利(令人怀疑),直到我尝试添加自己的一小部分 jQuery。我的目标是让最终用户可以选择使用按钮添加标
我是一名优秀的程序员,十分优秀!