def hanoi_move ( start , via , target ,n ,k ):
""" finds the k-th move in an Hanoi Towers instance
with n discs """
if n <=0:
return "zero or fewer disks"
elif k <=0 or k >=2** n or type(k )!= int :
return "number of moves is illegal"
elif k ==2**( n -1):
return str . format ("disk {} from {} to {}",n , start , target )
elif k <2**( n -1):
return hanoi_move ( start , target , via ,n -1 , k)
else:
return hanoi_move ( via , start , target ,n -1 ,k -2**( n -1))
为什么要看2 ^(n-1)动作?你能解释一下代码吗?
答案 0 :(得分:3)
TL; DR版本 - 需要2 ^(n-1)-1次移动才能将一堆n-1个磁盘从一个位置移动到另一个位置,因此这段代码会查看您是否正在移动第n个磁盘磁盘,或者在第n个磁盘上打开或关闭n-1个磁盘的堆栈。
完整答案:
理解为什么这段代码看第(n-1)次移动以确定第k次移动需要了解移动整个河内塔背后的基本思想。
让我们看一个例子。我们有A,B和C点,以及从A位开始的10个盘的塔,我们想要移动到位置C.我们会说盘9是最大的,下一个是8,等等。 ,它看起来像这样:
A 9876543210
B
C
那么我们如何将一切都移到C?好吧,我们需要将基础磁盘(9)移动到C,所以我们首先将其他所有内容移出,如下所示:
A 9
B 876543210
C
然后我们可以移动磁盘9
A
B 876543210
C 9
然后将九个磁盘的堆栈重新移回磁盘9,最终结果如下:
A
B
C 9876543210
当然,我正在跳过如何移动九个磁盘堆栈的整个部分,但这给出了基本的想法 - 移动堆栈需要移动除底部磁盘之外的所有磁盘,然后移动底部磁盘,然后将其余磁盘移回其上。
所以这段代码问“我们在这个过程中的哪个位置?”如果k
等于2 ^(n-1),那么我们当前正在移动我们当前正在尝试移动的堆栈的底部磁盘。如果k
小于该值,我们仍处于将磁盘堆栈从底部磁盘移出的过程中。如果k
大于此值,我们会尝试将磁盘堆栈移回底部磁盘。使用上面的简单示例,如果k
= 2 ^(10-1)= 2 ^ 9 = 512,那么我们将磁盘9从A移动到C.如果k
小于512,则我们仍在将磁盘0到8从磁盘9移到B点。如果k
大于512,那么我们在k
- 512移动到移动磁盘0到8从现场B回到现场C的磁盘9上。
我们怎么知道2 ^(n-1)是正确使用的值?使用mathematical induction我们可以证明移动一堆n个磁盘最多需要(2 ^ n)-1次移动。这是一个快速证明:
感应的基础:要移动一堆n = 1个磁盘,需要(2 ^ n)-1 =(2 ^ 1)-1 = 1移动 - 移动唯一的磁盘。 归纳步骤:假设它适用于n个磁盘(即移动堆栈的最有效方式是(2 ^ n)-1次移动)。然后,为了移动一堆n + 1个磁盘,我们首先使用最有效的方法将前n个磁盘从底部磁盘移开(取(2 ^ n)-1个移动),移动底部磁盘(1个移动) ,然后将其他n个磁盘移回底部磁盘顶部((2 ^ n)-1再次移动)。所以我们采取的移动总数是(2 ^ n)-1 + 1 +(2 ^ n)-1 = 2 *(2 ^ n)-1 =(2 ^(n + 1)) - 1次移动
如果您之前没有看过感应可能看起来很复杂,但基本的想法是这样的 - 因为我们知道它适用于一个磁盘,我们知道它适用于两个磁盘。由于我们现在知道它适用于两个磁盘,因此适用于三个磁盘。因为我们现在知道它适用于三个磁盘......等等。归纳步骤适用于任意n,因此我们可以根据需要多次应用它。因此,对于任何有限n,我们知道移动n个磁盘的最快方法是(2 ^ n)-1次移动。
所以现在的问题是,第n个磁盘移动了哪一步?在(2 ^(n-1)) - 1次移动中,我们可以完成在底部磁盘顶部移动n-1个磁盘的堆栈。然后,在(2 ^(n-1)) - 1 + 1 = 2 ^(n-1)次移动中,我们可以移动底部磁盘。