繁体   English   中英

python中的简单蒙特卡罗模拟

[英]Simple Monte Carlo Simulation in python

在您看到每个数字之前,您需要掷骰子的次数是多少?

我被要求定义一个运行蒙特卡罗模拟的函数,该函数返回上述问题的估计值。 我对解决方案的理解是,我需要:

  1. 定义一个以试验次数为输入的函数
  2. 生成 1 到 6 之间的随机整数(骰子上的边数)
  3. 如果该整数不在列表中,则将该整数存储在列表中
  4. 计算达到所述条件所需的试验次数,并返回该计数

我对编程和 Python 尤其陌生,所以我正在努力确定为什么我的语法在调用函数时不产生输出,并希望有人能帮助引导我走向正确的方向

这是我的代码:

def roll(n=1000):
    trials = []
    sides = 6
    start = 1

    for i in range (n):
        for x in range (sides):
            collection = [random.randint(1,sides)]
            while any([x not in collection]):
            collection.append(random.randint(1,6))
            trials.append(len(collection))

    return sum(trials)/ len(trials)

您可能没有打印函数返回的任何内容 - 这就是为什么它什么都不显示的原因。

使用print(roll())而不是roll()来打印你得到的结果。

您有太多循环,并且您的解决方案使用了太多内存空间。

考虑一下运气不好,必须滚动 1.000.000.000.000 次才能获得前 6 个 - 您将在列表中保存 1.000.000.000.000 个其他数字......那是很多内存。


您可以使用set来记住看到的数字,并使用计数器来计算找到所有所需的时间:

def roll(sides=6, n=1000):
    """Tests 'n' times to roll all numbers from 1 to 'sides' randomly.
    Returns average tries needed to see all numbers over 'n' tries."""
    trials = []   # collects all sinly tried counters

    for _ in range(n):
        seen = set()   # empty, will only ever store 6 elements at most
        tried = 0      # how long did it take to find all 6?
        while len(seen) < sides:  # use sides here as well
            seen.add(random.randint(1,sides))
            tried += 1
        trials.append(tried)

    return sum(trials)/n


print(roll())  

输出(4 次启动):

14.878

14.694

14.732

14.516

您的while条件并未表达您的期望。 你可能想使用列表理解

while any([x not in collection for x in [1, 2, 3, 4, 5, 6])

还有,你不希望3层循环,只有两个:一个for每个审判和其他while审判是不完整的。 一个工作示例,接近您的原始帖子是

 import random

 def roll(n=1000):
     trials = []
     sides = 6
     start = 1
     possible_sides = [1, 2, 3, 4, 5, 6]

     for i in range (n):
         collection = [random.randint(1,sides)]
         while any([side not in collection for side in possible_sides]):
             collection.append(random.randint(1,6))
         trials.append(len(collection))

     return sum(trials)/ len(trials)

而更有效的解决方案使用set来有效地执行与前一个解决方案相同的事情any([side not in collection for side in possible_sides])

import random

 def roll(n=1000):
     trials = []
     sides = 6
     start = 1
     possible_sides = set([1, 2, 3, 4, 5, 6])

     for i in range (n):
         n_rolls = 0
         sides_rolled = set()
         while not sides_rolled == possible_sides:
             sides_rolled.add(random.randint(1, sides))
             n_rolls += 1

         trials.append(n_rolls)

     return sum(trials)/ len(trials)

或者,更有效地,只需检查len(sides_rolled) < 6 ,正如 Patrick len(sides_rolled) < 6在回答中指出的那样。

您可以通过使用set而不是列表并更改迭代逻辑来大大简化代码:

import random

def roll_till_all_sides_appeared():
    sides_seen = set()
    n = 0

    while len(sides_seen) < 6:
        side = random.randint(1, 6)
        sides_seen.add(side)         # will only be added if it isn't present
        n += 1

    return n

def test(repetitions):
    max_n = float('-inf')
    min_n = float('inf')
    sum_n = 0

    for _ in range(repetitions):
        n = roll_till_all_sides_appeared()
        max_n = max(max_n, n)
        min_n = min(min_n, n)
        sum_n += n

    print('max:', max_n)
    print('min:', min_n)
    print('avg:', sum_n / repetitions)

可以像这样使用此代码:

>>> test(10)
max: 32
min: 8
avg: 14.3
>>> test(100)
max: 45
min: 6
avg: 14.13
>>> test(1000)
max: 56
min: 6
avg: 14.749
>>> test(10000)
max: 62
min: 6
avg: 14.6422

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM