首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Python制作一个交易程序集。

用Python制作一个交易程序集。
EN

Code Review用户
提问于 2018-07-12 08:52:47
回答 2查看 1.2K关注 0票数 10

主要是因为我不相信经典游戏的结果,做了一个交易(见https://math.stackexchange.com/questions/608957/monty-hall-problem-extended)我做了这个小程序.事实上,如果有3扇门,而问答大师打开一扇门,然后你从你最初选择的门上切换,你的机会从33%上升到67%!此程序是一个通用版本,您可以选择门的数目和测验大师将打开多少扇门,它计算出最初选择的价格(基本上是比门的数目多1)的机会,以及在打开门后更改所选门的机会。

我被使用Python集的优雅解决方案所吸引,但我不知道这是否是最有效的方法。与其他方法相比,随着更多的门和门的打开,它看起来变得更高效了。

谢谢你的意见..。

代码语言:javascript
复制
#!/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()
EN

回答 2

Code Review用户

发布于 2018-07-12 10:22:41

我第二次使用@Graipher的答案函数作为sets采样的辅助函数,但是我们可以通过使用以下定义来改进它的解决方案,该定义自动将random.sample返回的值转换为set

代码语言:javascript
复制
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显式计数器。

拟议改进:

代码语言:javascript
复制
#!/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))
票数 4
EN

Code Review用户

发布于 2018-07-12 09:53:49

Python有一个正式的风格指南,PEP8。它建议对变量和函数名使用lower_casedocstrings还有一个样式指南,PEP257,它建议使用三重双引号("""So, like this""")。

您的模块docstring可能应该包含一个句子摘要,比如“扩展Monty问题的n扇门,其中k是由游戏大师打开的。”

遗憾的是,random.choice不适用于sets,因为它们是不可索引的。但是,您经常使用它的替代random.sample(x, 1),因此您可能希望将其放入一个函数中:

代码语言:javascript
复制
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_1win_2)。

我还会将option_1option_2重命名为更容易理解的no_changechange

由于您再也不使用doors,所以我将从名称中删除所有尾随的_set

如果您从0或1开始枚举您的门,则没有什么不同,因此为了简单起见,我将使用set(range(doors))

这样,您的make_a_deal函数将变成:

代码语言:javascript
复制
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, change
票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/198344

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档