ex06 汉诺塔2 非递归解法

解法示意

  • 需借助二进制
  • 不妨以四层塔为例走一个
  • 我把左、中、右三根柱子依次称为 A, B, C
  • 金片默认都在 A 塔
  • n 片金片从小到大依次编号为 0, 1, 2, ... n-1
  • 设初始值为 0000(2)
  • 8, 4, 2, 1 称呼二进制的各位,对应关系如下图所示

0000

step1

  • 开始累加,每次加一
  • 0000(2) => 0001(2)
  • 因为1位由 01,所以将 0 号金片右移,即将 0 号金片由 A 移至 B
  • 补充:若要将 C 上的金片右移,则移至 A,因为三个塔是循环的

0001

step2

  • 0001(2) => 0010(2)
  • 产生进位,进到哪位,就移动该位对应的金片
  • 因为进位至2位,所以将 1 号金片右移
  • 因为 1 号金片不能放到 B,所以继续向右走,C 正好符合要求

0010

step3

  • 0010(2) => 0011(2)
  • 因为1位由 01,所以将 0 号金片右移,即将 0 号金片由 B 移至 C

0011

step4

  • 0011(2) => 0100(2)
  • 产生进位,因为进至4位,所以将 2 号金片右移

0100

step5

  • 0100(2) => 0101(2)
  • 因为1位由 01,所以将 0 号金片右移,即将 0 号金片由 C 移至 A

0101

  • 按这个方法进行下去,当数字变成 1111 时,A 塔的四片金片就都在 C 塔上了

说明

关于结果

  • 此“二进制”方法可行,但奇数金片与偶数金片在结果上有些许不同
    • 若金片总数为奇数,最终会移至 B 塔
    • 若金片总数为偶数,最终会移至 C 塔
  • 使用高数中“轮换对称性”,在遇到奇数金片时,把原来的 B 塔看成 C 塔,把原来的 C 塔看成 B 塔

两个规律

  • 规律一
    • 因为每走一步,数值加一,所以该二进制数即为步数
    • 该二进制数末尾 0 的个数对应要移动的金片
      • 没有 0,即为 0 个 0,对应 0 号金片;可回顾图 0001, 0011, 0101
      • 1 个 0,对应 1 号金片;可回顾图 0010
      • 2 个 0,对应 2 号金片;可回顾图 0100
      • 依此类推
  • 规律二
    • 编号为 0, 2, 4, ... 的金片,总是进行右移操作
    • 编号为 1, 3, 5, ... 的金片,总是进行左移操作
      • 因为只有三根柱子,所以右移 2 格就是左移 1 格

移动次数

  • 按递归的思路,汉诺塔可分成三大步

    1. 将 A 上 n-1 片金片移至 B
    2. 将 A 剩余的 1 片金片移至 C
    3. 将 B 的 n-1 片金片移至 C
  • f(n)n 片金片完成移动需要的最少次数,则 f(n) = f(n-1) + 1 + f(n-1),即 f(n) = 2f(n-1) + 1

    • 若只有 1 片金片,则 f(1) = 1
    • 若有 2 片金片,则 f(2) = 3
    • 若有 3 片金片,则 f(3) = 7
    • 照此规律,可假设 f(n) = 2^n - 1
  • 可以用“第一类数学归纳法”证明 f(n) = 2^n - 1

    • n = 1 时,f(1) = 2^1 - 1 = 1,成立
    • n = k 时,设 f(k) = 2^k - 1 成立
    • => 当 n = k + 1 时,f(k+1) = 2f(k) + 1 = 2 * (2^k - 1) + 1 = 2^(k+1) - 1,满足假设
    • => 汉诺塔的移动次数为 f(n) = 2^n - 1,证毕

代码

def hanoi(n):
    cols = ['A', 'B', 'C'] if n % 2 == 0 else ['A', 'C', 'B']
    golds = [0] * n  # golds[idx] == tower_idx
    for step in range(1, 2 ** n):
        idx = len(bin(step & -step)) - 3  # lowbit 转二进制,开头是 0b1
        old_tower = cols[golds[idx]]
        golds[idx] = (golds[idx] + 1 + idx % 2) % 3  # 奇数比偶数多走一步
        print(f"step{step}: 将{idx}号金片从{old_tower}移到{cols[golds[idx]]}")
posted @ 2019-02-10 17:49  YorkFish  阅读(2337)  评论(0编辑  收藏  举报