1. Monty Hall Problem
一个著名的博弈论游戏,起源大概是一个美国电视游戏节目叫做Let's make a deal,这个问题名字来源于节目主持人Monty Hall 。
游戏简述:
“参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车或者是奖品,选中后面有车的那扇门就可以赢得该汽车或奖品,而另外两扇门后面则各藏有一只山羊或者是后面没有任何东西。当参赛者选定了一扇门,但未去开启它的时候,知道门后情形的节目主持人会开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门会否增加参赛者赢得汽车的机会率?如果严格按照上述的条件的话,答案是会。—换门的话,赢得汽车的概率是2/3。”
用python仿写这个游戏
大多程序都可以拆解为输入、处理和输出。本题就是按照这个思路写出相应的程序
1.1 输入1--模拟次数
While True:
#注意boolean的书写是大写的T,True
try:
n = int(input('How nany times do you want to run the simulation? '))
#如果出现异常,不会继续运行try后面的程序行,而是跳入exceept程序段执行程序
break
#跳出当前循环,注意,如果有多个循环的时候,一次break不能跳出所有循环。
except ValueError:
print('Incorrect input, try again')
#这样实现了用户多次输入的功能,允许用户多次输入来调整想要的输入值。
这段程序使用的是之前lab1用过的try ... except ...被动异常捕获方法,但是只能保证输入值是int,而我们的输入值不仅是int,还需要>0,这里新引入一种主动异常处理方式--raise
While True:
try:
n = int(input('How nany times do you want to run the simulation? '))
if n < = 0:
raise ValueError
#rasie命令使得程序进入except ValueError程序块,不执行后续的try程序块的命令。
break
except ValueError:
print('Incorrect input, try again')
1.2 输入2--更改答案
和上文同理
while True:
try:
n = int(input('How nany times do you want to run the simulation? '))
if n <= 0:
raise ValueError
break
except ValueError:
print('Incorrect input, try again')
print(n)
while True:
switch = input('Do you want to switch? ')
if switch in {'yes', 'Yes', 'y', 'Y'}:
switch = True
#由于switch只有两种情况,赋予boolean值效率最高,不要赋予其他int值。
break
if switch in {'no', 'No', 'n', 'N'}:
switch = False
break
else:
print('Incorrect input, try again')
1.3 游戏初始化
在三扇门后面要随机选择一个放入大奖,需要用随机函数来写,之前讲过import random,接下来的问题是查询使用哪个函数更方便。查询random的函数方法是dir(random),但是其中会有很多initilize的函数,这些函数命名两端是_,把他们删除的显示方法如下
import random
[x for x in dir(random) if not x.startswith('_')]
上文程序写法是简化的list生成方法,它等于下文程序
import random
result = []
for x in dir(random):
if not x.startswith('_'):
result.append(x)
result
从中选取了choice函数,因为它的属性是Choose a random element from a non-empty sequence.
from random import *
doors = ['A', 'B', 'C']
#用list记录三扇门
nb_of_wins = 0
#记录获奖次数,初始化为0
winning_door = choice(doors)
#随机从doors的list中选择一个放置大奖
first_choice = doors.pop(randrange(3))
#在index[0,2]中随机抽选一个int作为用户的初始选择index,并把doors中对应index的门assign给first_choice,再从doors中删除
1.4 游戏过程
游戏过程是主持人删除了一个错误答案,玩家决定是否更换选项。
if not switch:
#前文把switch赋予了boolean值,所以可以直接用not switch判断True or False
second_choice = first_choice
if first_choice = winning_door:
#如果first_choice是winning_door,打开的门可以是doors剩下两扇门中的任何一个
opened_door = doors.pop(randrange(2))
#从剩下两扇门中随意找到一扇门打开,接下来是要看玩家选项是否switch
if switch:
second_choice = doors[0]
#因为此时doors的list中只有一个元素,所以switch的话,就把doors中剩下的这个元素(index一定是0)assign给second_choice
else:
nb_of_wins += 1
#因为最开始已经定义not switch时second_choice的赋值问题,所以可以直接确定中奖。
else:
doors.remove(winning_door)
#因为first_choice不是大奖,所以要从doors的list中删除大奖的门,大奖的门不能被主持人打开
opened_door = doors[0]
#此时doors的list中只剩下一个非大奖选项
if switch:
second_choice = winning_door
nb_of_wins += 1
1.5 模块组合和结果输出
输入模块、游戏初始化和游戏过程都已经写好,接下来整合模块,并且按照要求输出结果
from random import choice, randrange
while True:
try:
n = int(input(input('How nany times do you want to run the simulation? '))
if n <= 0:
raise ValueError
break
except ValueError:
print('Incorrect input, try again')
while True:
switch = input('Do you want to switch? ')
if switch in {'y', 'Y', 'yes', "Yes'}:
switch = True
break
if switch in {'n', 'N', 'no', ‘No'}:
switch = False
break
print('Incorrect input, try again')
for _ in range(n):
doors = ['A', 'B', 'C']
winning_door = choice(doors)
nb_of_wins = 0
first_choice = doors.pop(randrange(3))
if not switch:
second_choice = first_choice
if first_choice == winning_door:
opened_door = doors.pop(randrange(2))
if switch:
second_choice = doors[0]
else:
nb_of_wins += 1
else:
doors.remove(winning_door)
opened_door = doors[0]
if switch:
nb_of_wins += 1
second_choice = winning_door
print('Winning door:', winning_door)
print('First choice:', first_choice)
print('Opened door:', opened_door)
print('Seconc choice:', second_choice)
print()
print(nb_of_wins)
运行结果会发现switch的获奖概率是2/3,not switch的获奖概率是1/3。
2. PDF automated form filling
这个部分的内容没有详细讲解,主要是展示有这样的可能性。运行相关程序的时候需要install两个module,一个是PyPDF2,另一个是pyautogui,方法如下:
pip3 install PyPDF2
pip3 install pyobjc-core
pip3 install pybojc
pip3 install pyautogui
实际上打开这个程序的python源文档,并不难理解,大部分是字符串操作。
3. Elementary cellular automata (元胞自动机)
简单说:以1个符号作为种子,根据某种规则向下衍生。
3.1 规则制定
本问题的规则是根据一个输入的数转化为2进制后制定的,先要实现二进制显示8个数字的功能。以输入90为例:
bin(90)
>>> '0b1011010'
转化后数字前面有二进制符号0b,需要把它们去掉
bin(90)[2:]
>>> '1011010'
然后发现不同的数字二进制转化后并不能确定是8位数,所以要在前面补全数字0
'0' * (8 - len(bin(90)[2:]) + bin(90)[2:]
>>> '01011010'
这样的程序虽然达到了目的,但是太过复杂,想到之前提到过的格式化输出,可以利用string来帮助简化程序
f'{90:08b}'
>>> '01011010'
接下来完成输入值90的规则制定
n = f'{90:08b}'
rules = {}
for i in range(8):
rules[(i // 4, i // 2 % 2, i % 2)] = n[7 - i]
使用python的简化写法
n = f'{90:08b}'
rules = {(i // 4, i // 2 % 2, i % 2): n[7 - i] for i in range(8)}
3.2 程序实现
def print_line(line):
#根据list调整输出格式
for e in line:
if e:
#如果某个位置是非0的
print('*', end = '')
else:
print(' ', end = '')
print()
#换行输出
n = f'{90:08b}'
rules = {(i // 4, i // 2 % 2, i % 2): int(n[7 - i]) for i in range(8)}
line = [0] * 20 + [1] + [0] * 20
#初始化,左右两边各20个0,中间有个1
print_line(line)
#输出初始化的第一行,后续行根据规则产生,边界是不能超出41个数字(20个0,1个1,20个0是初始化的位置)
for i in range(20):
#每次根据规则产生新的一行时,1的左右两侧各使用了1个0,所以只能产生20行,否则超出临界值。
new_line = [0] * 41
for j in range(20 - i, 20 + i + 1):
#第i行需要根据规则产生数字的区域是[20-i, 20+i]
new_line[j] = rules[line[j - 1], line[j], line[j + 1]]
line = new_line
print_line(line)
>>>
*
* *
* *
* * * *
* *
* * * *
* * * *
* * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
* * * *
* * * * * * * *
* * * * * * * *
* * * * * * * * * * * * * * * *
* *
* * * *
* * * *
* * * * * * * *
4. Rational Number
有理数用小数的形式表现的时候小数点后分为两个部分,有限的无规律数字组+无限的有规律的循环数字组。(Eric给的课上代码是有错误的,正确的如下)
sigma = input('Input sigma: ')
tau = input('Input tau:')
p = int(sigma) * ((10 ** (len(tau))) - 1) + int(tau)
q = ((10 ** len(tau)) -1) * 10 ** len(sigma)
print(p/q)
#[sigma * (10^{|tau|} - 1) + tau] /
# [(10^{|tau|} - 1) * 10^{|sigma|}]
#该公式是从0.(sigma)(tau)(tau)(tau)...
= sigma * 10^{-|sigma|} + tau(10^{-|sigma|-|tau|} +
10^{-|sigma|-2|tau|} +
...)推导出来的
>>>
Input sigma: 27
Input tau:343
0.27343343343343346
在这个问题的python源文档中,有一种需要注意的函数调用方法
def f():
return (2, 6)
def g(m, n):
return m * n
print(g(*f()))
#注意f()前边的*,因为函数f()返回的是一个tuple(2,6),这是一个元素,而g()需要两个数的输入,所以要用*f()把tuple(2, 6)这样一个元素转化为两个元素。
再举一个例子
def f(a, b):
return a + b
f(*(2, 6))
>>> 8
网友评论