解析:
'''
永远不要试图跟踪一个大型递归的过程!
要写出递归,关键就是找出递归的递归方程式:
也就是说,要完成的最后一步是什么,那么最后一步的前一步要做什么
又或者说,要完成这一步那么下一步该做什么
'''
# 汉诺塔的模拟递归过程:
# 有n个盘子,需要由a经过b移动到c:(n,a,b,c)
# 第一步、先把n-1个盘子由a经过c移动到b:(n-1,a,c,b)
# 第二步、把第n个盘子由a经过’b‘移动到c:(1,a,b,c),---只有一个盘子了当然不用在经过b移动到c了,这么写只是为了使结构一致方便写递归函数
# 第三步、把n-1个盘子由b经过a移动到c:(n-1,b,a,c)
# 最后递归就行了
代码:
def move(n,a,b,c):
# 递归出口,如果只有一个盘子,直接把它移到c就行了,当然a和c只是参数,他们既可以是a也可以是b和c,但是:永远不要试图跟踪一个大型递归的过程!
# 只需要认为他就是从a直接移动到c就行了
if n==1:
print(a,'-->',c)
else:
move(n-1,a,c,b) # 第一步、先把n-1个盘子由a经过c移动到b:(n-1,a,c,b)
move(1,a,b,c) # 第二步、把第n个盘子由a经过’b‘移动到c:(1,a,b,c),---只有一个盘子了当然不用在经过b移动到c了,这么写只是为了使结构一致方便写递归函数
move(n-1,b,a,c) # 第三步、把n-1个盘子由b经过a移动到c:(n-1,b,a,c)
move(7,'A','B','C')
移动次数
方法一
已知盘子的数量为n,计算把n个盘子从a经过b移动到c移动需要移动多少次。
如果按照上面的代码计算盘子的移动次数,时间会非常慢,因为上面的代码是模拟盘子的移动过程,盘子移动多少次,实际就递归多少次。
通过上面的代码可以发现一个规律:
- 当n=1时,只移动一次
- 当n>1时,
- 先走个将n-1个盘子从a经由b移动到c的次数
- 再走个将一个盘子直接从a移动到c的次数,也就是移动1次
- 最后再走个将b上的n-1个盘子经由a移动到c的次数
从上面的规律可以看出,如果n=1,只移动1次,否则移动 2*move(n-1)+1次。于是就得到了求求移动次数的递归代码;
递归求盘子的移动次数
"""
有n个盘子,他需要从a借助于b移动到c,移动的次数是move(n):
当n=1时,只需要移动1次
当n=2时,
先走个将n-1个盘子从a借助于c移动到b的次数,即:move(n-1)
再将a上的最底下的那个盘子直接移动到c,即1次
再走个将b上的n-1个盘子借助a移动到c的次数,即:move(n-1)
也就是说当n>1时,走的次数是2*move(n-1)+1
"""
# 移动n个盘子需要的次数
n = 64
def move (n):
if n == 1:
return 1
return 2*move(n-1)+1
print(move(n))
方法二
另外一种求n个盘子移动次数的方法是结论法:
盘子数目 | 移动次数 |
---|---|
1 | 2 1 − 1 = 2 0 = 1 2^1-1=2^0=1 21−1=20=1 |
2 | 2 2 − 1 = 2 0 + 2 1 = 3 2^2-1=2^0+2^1=3 22−1=20+21=3 |
3 | 2 3 − 1 = 2 0 + 2 1 + 2 2 = 7 2^3-1=2^0+2^1+2^2=7 23−1=20+21+22=7 |
… | … |
n | 2 n − 1 = 2 0 + 2 1 + 2 2 + . . . + 2 n − 1 2^n-1=2^0+2^1+2^2+...+2^{n-1} 2n−1=20+21+22+...+2n−1 |
所以,移动n的盘子需要的次数为: |
python
import math
n = 64
print(int(math.pow(2,n)-1))
print(pow(2,n)-1)
java
package demo.test1;
import java.math.BigInteger;
public class Main {
public static void main(String[] args) {
BigInteger bit = new BigInteger("2");//定义一个大整数类型的2
bit = bit.pow(64);//2的64次方,并重新赋值给bit
bit = bit.subtract(new BigInteger("1"));//大整数的减法,并重新赋值给bit
System.out.println(bit);
}
}