首页 > 解决方案 > 有没有循环的 Monty Hall 问题的 Pandas/Numpy 实现?

问题描述

这更像是一种好奇心练习......

如果您还没有听说过蒙蒂霍尔问题,可以在这个很棒的youtube 视频中解释它。

我使用 numpy 在 python 中模拟了它:

import numpy as np

num_games = 100000
options = np.arange(1, 4, 1)

stick_result = 0
switch_result = 0

for i in range(1, num_games + 1):
    winning_door = np.random.randint(1, 4)
    first_choice = np.random.randint(1, 4)

    if winning_door == first_choice:
        stick_success += 1

    # remove a door that isn't the winning_door or the first_choice 
    door_to_remove = np.random.choice(options[~np.isin(options, [winning_door, first_choice])])
    options_with_one_door_removed = options[~np.isin(options, door_to_remove)]

    # switch door to remaining option that isn't the first choice
    second_choice_after_switch = options_with_one_door_removed[~np.isin(options_with_one_door_removed, first_choice)]

    if winning_door == second_choice_after_switch:
        switch_result += 1

这是否可以在没有 for 循环的情况下完成?这是我到目前为止所拥有的,但我不确定如何进行门切换。

import numpy as np

num_games = 100000
options = np.arange(1, 4, 1)

winning_door = np.random.randint(1, 4, num_games)
first_choice = np.random.randint(1, 4, num_games)

stick_successes = (winning_door == first_choice).sum()

# remove a door that isn't the winning_door or the first_choice
door_to_remove = ???
options_with_one_door_removed = ???

# switch door to remaining option that isn't the first choice
second_choice_after_switch = ???

switch_successes = (winning_door == second_choice_after_switch).sum()

您必须确定游戏节目主持人从游戏的每个实例中移除哪扇门(winning_door&first_choice数组的每一行),然后将其切换first_choice到剩余的另一扇门。)

有任何想法吗?

标签: pythonpandasnumpy

解决方案


您最大的问题是choice使用蒙版进行矢量化。这可能看起来像:

def take_masked_along_axis(arr, where, index, axis):
    """ Take the index'th non-masked element along each 1d slice along axis """
    assert where.dtype == bool
    assert index.shape[axis] == 1
    # np.searchsorted would be faster, but does not vectorize
    unmasked_index = (where.cumsum(axis=axis) > index).argmax(axis=axis)
    unmasked_index = np.expand_dims(unmasked_index, axis=axis)  # workaround for argmax having no keepdims
    return np.take_along_axis(arr, unmasked_index, axis=axis)


def random_choice_masked_along_axis(arr, where, axis):
    """ Like the above, but choose the indices via a uniform random number """
    assert where.dtype == bool
    index = np.random.sample(arr.shape[:axis] + (1,) + arr.shape[axis+1:]) * where.sum(axis=axis, keepdims=True)
    return take_masked_along_axis(arr, where, index, axis=axis)

使代码的第一部分类似于

options_broadcast = np.broadcast_to(options, (3, num_games))
removable = (options != options_broadcast) & (options != options_broadcast)
door_to_remove = random_choice_masked_along_axis(options_broadcast, where=removable, axis=0)

推荐阅读