主要是因为我不相信经典游戏的结果,做了一个交易(见https://math.stackexchange.com/questions/608957/monty-hall-problem-extended)我做了这个小程序.事实上,如果有3扇门,而问答大师打开一扇门,然后你从你最初选择的门上切换,你的机会从33%上升到67%!此程序是一个通用版本,您可以选择门的数目和测验大师将打开多少扇门,它计算出最初选择的价格(基本上是比门的数目多1)的机会,以及在打开门后更改所选门的机会。
我被使用Python集的优雅解决方案所吸引,但我不知道这是否是最有效的方法。与其他方法相比,随着更多的门和门的打开,它看起来变得更高效了。
谢谢你的意见..。
#!/usr/bin/env python
''' application of Make a deal statistics
application is using sets {}
for reference:
https://math.stackexchange.com/questions/608957/monty-hall-problem-extended
'''
import random
def Make_a_Deal(doors, doors_to_open):
''' Generalised function of Make_a_Deal. Logic should be self explanatory
Returns win_1 for the option when no change is made in the choice of
door and win_2 when the option to change is taken.'''
win_1, win_2 = False, False
doors_set = set(range(1, doors+1))
price_set = set(random.sample(doors_set, 1))
choice1_set = set(random.sample(doors_set, 1))
open_set = set(random.sample(doors_set.difference(price_set).
difference(choice1_set), doors_to_open))
choice2_set = set(random.sample(doors_set.difference(open_set).
difference(choice1_set), 1))
win_1 = choice1_set.issubset(price_set)
win_2 = choice2_set.issubset(price_set)
return win_1, win_2
def main():
''' input:
- throws: number of times to Make_a_Deal (must be > 0)
- doors: number of doors to choose from (must be > 2)
- doors_to_open: number of doors to be opened before giving the option
to change the initial choice (must be > 0 and <= doors-2)'''
try:
throws = int(input('how many throws: '))
doors = int(input('how many doors: '))
doors_to_open = int(input('how many doors to open: '))
if (throws < 1) or (doors < 3) or \
(doors_to_open > doors-2) or (doors_to_open < 1):
print('invalid input')
return
except Exception as e:
print('invalid input: ', e)
return
number_of_wins_1, number_of_wins_2, counter = 0, 0, 0
while counter < throws:
win_1, win_2 = Make_a_Deal(doors, doors_to_open)
if win_1:
number_of_wins_1 += 1
if win_2:
number_of_wins_2 += 1
counter += 1
print('completion is {:.2f}%'.
format(100*counter/throws), end='\r')
print('number of wins option 1 is {:.2f}%: '.
format(100*number_of_wins_1/counter))
print('number of wins option 2 is {:.2f}%: '.
format(100*number_of_wins_2/counter))
if __name__ == '__main__':
main()发布于 2018-07-12 10:22:41
我第二次使用@Graipher的答案函数作为sets采样的辅助函数,但是我们可以通过使用以下定义来改进它的解决方案,该定义自动将random.sample返回的值转换为set:
def random_choice(population, k=1):
return {*random.sample(population, k)}对于代码的读者来说,您的文档字符串没有多大意义。相反,为用户编写docstring,记录函数/模块正在做什么,而不是如何。
您不需要在其名称中明确变量的类型,选择反映存储内容的名称,而不是如何存储。
我会将输入收集与main分开。它的验证是好的,尽管有点过了:如果抛出不是正的,那么就不会有任何抛出,而统计数据将是0,这是一致的。让您的main与数据的来源无关,以简化可重用性和测试。如果您确实想报告进度,则应该接受回调,以使行为是可选的,并且易于互变。
如果将输入收集与main分离,就可以很容易地将input切换到argparse或其他任何GUI。
不要抓住通用的Exceptions在这里,你只关心ValueErrors,它将避免打印Invalid input的例外,例如KeyboardInterrupt。
您还应该将错误消息打印到sys.stderr,并且可以选择使用sys.exit()使用非零返回代码退出程序。
喜欢使用for循环比whiles显式计数器。
拟议改进:
#!/usr/bin/env python3
"""Application of Make a deal statistics
Application is using sets {}
For reference:
https://math.stackexchange.com/questions/608957/monty-hall-problem-extended
"""
import sys
import random
def random_choice(population, k=1):
return {*random.sample(population, k)}
def make_a_deal(doors, doors_to_open):
"""Generalised function of Make a Deal.
Returns a pair of boolean indicating:
- wether the original choice was a win for the first element
- wether the changed choice was a win for the second element
"""
doors = set(range(doors))
prize = random_choice(doors)
original_choice = random_choice(doors)
opened = random_choice(doors - prize - original_choice, doors_to_open)
changed_choice = random_choice(doors - opened - original_choice)
return original_choice <= prize, changed_choice <= prize
def main(throws, doors, doors_to_open, progression_callback=None):
"""Gather statistics about repeated simulations of Make a Deal game.
Parameters:
- throws: number of simulations to run
- doors: number of doors to choose from (must be > 2)
- doors_to_open: number of doors to be opened before giving the
option to change the initial choice (must be > 0 and <= doors - 2)
"""
if doors < 3:
raise ValueError('doors must be at least 3')
if not (0 < doors_to_open <= doors - 2):
raise ValueError('doors to open mismatch the number of doors')
original_wins = changed_wins = 0
for throw in range(throws):
original_win, changed_win = make_a_deal(doors, doors_to_open)
if original_win:
original_wins += 1
if changed_win:
changed_wins += 1
if progression_callback is not None:
progression_callback(throw / throws)
return original_wins / throws, changed_wins / throws
def show_completion(ratio):
print('Completion is {:.2%}'.format(ratio), end='\r')
if __name__ == '__main__':
try:
throws = int(input('How many throws: '))
doors = int(input('How many doors: '))
doors_to_open = int(input('How many doors to open: '))
wins1, wins2 = main(throws, doors, doors_to_open, show_completion)
except ValueError as e:
print('Invalid input:', file=sys.stderr, end=' ')
sys.exit(e)
print('Number of wins without changing is {:.2%}'.format(wins1))
print('Number of wins after changing is {:.2%}'.format(wins2))发布于 2018-07-12 09:53:49
Python有一个正式的风格指南,PEP8。它建议对变量和函数名使用lower_case。docstrings还有一个样式指南,PEP257,它建议使用三重双引号("""So, like this""")。
您的模块docstring可能应该包含一个句子摘要,比如“扩展Monty问题的n扇门,其中k是由游戏大师打开的。”
遗憾的是,random.choice不适用于sets,因为它们是不可索引的。但是,您经常使用它的替代random.sample(x, 1),因此您可能希望将其放入一个函数中:
def random_choice(x):
return random.sample(x, 1)[0]这也意味着您需要使用{random_choice(x)},而不是set(random_choice(x)),因为现在这是一个单独的元素,不再是一个包含一个元素的列表。
doors_set.difference(price_set).difference(choice1_set)和doors_set - price_set - choice1_set是一样的,它更短,至少也一样可读性。
类似地,choice1_set.issubset(price_set)等同于choice1_set <= price_set (尽管这里更有表现力的版本可能更容易理解)。
您不需要在Python中声明变量,特别是如果您以任何方式覆盖它的值(win_1和win_2)。
我还会将option_1和option_2重命名为更容易理解的no_change和change。
由于您再也不使用doors,所以我将从名称中删除所有尾随的_set。
如果您从0或1开始枚举您的门,则没有什么不同,因此为了简单起见,我将使用set(range(doors))。
这样,您的make_a_deal函数将变成:
def make_a_deal(doors, doors_to_open):
''' Generalised function of Make_a_Deal. Logic should be self explanatory
Returns win_1 for the option when no change is made in the choice of
door and win_2 when the option to change is taken.'''
doors = set(range(doors))
price = {random_choice(doors)}
choice1 = {random_choice(doors)}
open_doors = set(random.sample(doors - price - choice1, doors_to_open))
choice2 = {random_choice(doors - open_doors - choice1)}
no_change = choice1 <= price
change = choice2 <= price
return no_change, changehttps://codereview.stackexchange.com/questions/198344
复制相似问题