◎知识点
谁在说谎
猴子吃桃
汉诺塔
◎脚本练习
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@FileName: python_practice3.py
@Function: python practice
@Author: Zhihe An
@Site: https://chegva.com
@Time: 2021/7/6
"""
"""一、谁在说谎"""
"""
【问题描述】
张三说李四在说谎,李四说王五在说谎,王五说张三和李四都在说谎
这三人中到底谁说的是真话,谁说的是假话?
【设计思路】
张三说李四在说谎,说明:要么张三说的是真话李四说的是假话,要么张三说的是假话李四说的是真话
李四说王五在说谎,说明:要么李四说的是真话王五说的是假话,要么李四说的是假话王五说的是真话
王五说张三和李四都在说谎,说明:要么王五说的是真话张三和李四说的都是假话,
要么王五说的是假话张三和李四至少有一个说的是真话
设True表示某人说的是真话,设False表示某人说的是假话
设isZhang、isLi和isWang分别表示张三、李四和王五是否在说谎,则其取值范围都为[True, False]
通过三重循环穷举isZhang、isLi和isWang,在穷举的过程中,只要满足已知条件:
isZhang == (not isLi) and isLi == (not isWang) and isWang == ((not isZhang) and (not isLi))
即找到谁在说谎
"""
# 设True表示某人说的是真话,设False表示某人说的是假话
t = [True, False]
# 设isZhang、isLi和isWang分别表示张三、李四和王五是否在说谎,则其取值范围都为[True, False]
for isZhang in t:
for isLi in t:
for isWang in t:
# 在穷举的过程中,只要满足已知条件
if isZhang == (not isLi) \
and isLi == (not isWang) \
and isWang == ((not isZhang) and (not isLi)):
# 即找到谁在说谎
print('张三:{zhang},李四:{li},王五:{wang}'
.format(zhang = '真话' if isZhang == True else '假话',
li = '真话' if isLi == True else '假话',
wang = '真话' if isWang == True else '假话'))
"""二、猴子吃桃"""
"""
【问题描述】
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个
第二天早上又将第一天剩下的桃子吃掉一半,又多吃了一个
以后每天早上都吃了前一天剩下的一半后又多吃一个
到第10天早上想再吃时,发现只剩下一个桃子了
求猴子第一天共摘了多少个桃子
【设计思路】
设计思路一:递推
第1天的桃子数是第2天的桃子数加1后的2倍,第2天的桃子数是第3天的桃子数加1后的2倍,
...,一般地,第n天的桃子数是第n+1天的桃子数加1后的2倍
设第n天的桃子数是L(n),则有递推关系L(n)=(L(n+1)+1)*2,且初始条件为:L(10)=1
根据递推关系和初始条件,L(10) -→ L(9) -→ L(8)... -→ L(1)
"""
def monkey_peach1():
"""求猴子第一天共摘了多少个桃子"""
L = [None] * 11
# 初始条件为:L(10) = 1
L[10] = 1
# 设第n天的桃子数是L(n),则有递推关系L(n)=(L(n+1)+1)*2,且初始条件为:L(10)=1
# 根据递推关系和初始条件,L(10) -→ L(9) -→ L(8)... -→ L(1)
for n in range(9, 0, -1):
L[n] = (L[n + 1] + 1) * 2
return L[1]
print('猴子第一天共摘了%d个桃子' % monkey_peach1()) # 猴子第一天共摘了1534个桃子
"""
设计思路二:递归
设计思路一的递推关系可看做在一个函数体的内部又调用了该函数
设计思路一的初始条件可看做递归结束条件(递归出口)
"""
def monkey_peach2(day):
"""求猴子第一天共摘了多少个桃子"""
# 递归结束条件(递归出口): L(10) = 1
if day == 10:
return 1
else:
# 递推关系L(n)=(L(n+1)+1)*2可看做在一个函数体的内部又调用了该函数
return (monkey_peach2(day + 1) + 1) * 2
print('猴子第一天共摘了%d个桃子' % monkey_peach2(1)) # 猴子第一天共摘了1534个桃子
"""三、汉诺塔"""
"""
【问题描述】
有一个梵塔叫汉诺塔,汉诺塔上有三根柱子A、B和C,
柱子A上有若干圆盘,所有圆盘大小不等,且小的在上大的在下。
将柱子A上的所有圆盘借助柱子B移动到柱子C上,移动的过程中每次只能移动一个圆盘,
移动后仍然是小的在上大的在下,如下图所示:
-----------------------------------
X | |
XXX | |
XXXXX | |
---A------B------C-----------------
-----------------------------------
| | X
| | XXX
| | XXXXX
---A------B------C-----------------
求汉诺塔的三根柱子上所有圆盘的的移动过程
【设计思路】
当柱子A有1个圆盘时,只需要将圆盘从柱子A移动到柱子C
当柱子A有2个圆盘时,先将柱子A上面的圆盘从柱子A移动到柱子B,
再将柱子A下面的圆盘从柱子A移动到柱子C,最后将柱子B的圆盘从柱子B移动到柱子C
当柱子A有3个圆盘时,先将柱子A上面的两个圆盘从柱子A借助柱子C移动到柱子B,
然后将柱子A最下面的圆盘从柱子A移动到柱子C,最后将柱子B的两个圆盘从柱子B借助柱子A移动到柱子C
一般地,当柱子A有n个圆盘时,从上到下依次编号为1,2,3,...,n,
先将柱子A上面编号为1至n-1的圆盘从柱子A借助柱子C移动到柱子B,
然后将柱子A最下面编号为n的圆盘从柱子A移动到柱子C,
最后将柱子B的n-1个圆盘从柱子B借助柱子A移动到柱子C
所求的问题是:将柱子A的n个圆盘借助柱子B移动到柱子C
结合上面的一般性步骤,很容易想到使用递归。
假设递归函数hanoi(n, A, B, C)用于将n个圆盘从柱子A借助柱子B移动到柱子C,
函数move(m, A, C)用于将编号为m的圆盘从柱子A移动到柱子C,
则上面的一般步骤可以表示为:
(1) 当n = 1时,调用move(1, A, C),将圆盘从柱子A移动到柱子C
(2) 当n > 1时,
1) 调用hanoi(n - 1, A, C, B),
将柱子A上面编号为1至n - 1的圆盘从柱子A借助柱子C移动到柱子B
2) 调用move(n, A, C),将柱子A最下面编号为n的圆盘从柱子A移动到柱子C
3) 调用hanoi(n - 1, B, A, C),将柱子B的n - 1个圆盘从柱子B借助柱子A移动到柱子C
参考:如何理解汉诺塔的递归?(https://www.zhihu.com/question/24385418)
"""
def move(m, A, C):
"""将编号为m的圆盘从柱子A移动到柱子C"""
print('移动%d号圆盘,%s --→ %s' % (m, A, C))
def hanoi(n, A, B, C):
"""将n个圆盘从柱子A借助柱子B移动到柱子C"""
if n == 1:
# 将圆盘从柱子A移动到柱子C
move(1, A, C)
else:
# 将柱子A上面编号为1至n - 1的圆盘从柱子A借助柱子C移动到柱子B
hanoi(n - 1, A, C, B)
# 将柱子A最下面编号为n的圆盘从柱子A移动到柱子C
move(n, A, C)
# 将柱子B的n - 1个圆盘从柱子B借助柱子A移动到柱子C
hanoi(n - 1, B, A, C)
hanoi(5, 'A', 'B', 'C')