python中的简单蒙特卡罗模拟

时间:2019-03-09 10:15:24

标签: python python-3.x

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

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

  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)

3 个答案:

答案 0 :(得分:2)

无论功能返回什么,您都可能不会打印-这就是为什么它什么也不显示。

使用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

答案 1 :(得分:0)

您的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)

或者,甚至更有效地,只需检查Patrick len(sides_rolled) < 6,如Patrick Artner在回答中所指出。

答案 2 :(得分:0)

通过使用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