我正在尝试编写一个程序来显示掷 N 个骰子的示例 space

I'm trying to do a program that shows the sample space of throwing N dices

我正在尝试编写一个程序来显示 space 掷 N 个骰子的示例并写下每个骰子的结果,然后将每个骰子的结果相加对样本的每个元素进行骰子 space,最后用总和制作直方图。

我知道如何处理少量骰子,手动放置 for 圈。

3 个骰子的示例:

import numpy as np
import matplotlib.pyplot as plt

diceNum=3       # number of dices
ss = 6**diceNum # number of elements of the sample space: throw n dices and write points of each dice. (n**N)

diceSum = []
diceThrow = []

nn=0 # to count the number of elements

# 3 for loops for each dice
for k in range(6):
   
   for i in range(6):
   
       for j in range(6):
           
           dices = [k+1,i+1,j+1]   # Result of throw the 3 dices
           diceThrow.append(dices) # Sample space 
           diceSum.append(sum(diceThrow[nn])) #Sum each element of the sample space
       
           nn=nn+1


print(diceThrow)
print(" ")
print(diceSum)

# Make a histogram with the sum of each element of the sample space
plt.hist(diceSum, bins = sm)
plt.grid()
plt.show()

我的问题是如何对 N 个骰子执行此操作,例如 N=100,而无需手动放置 N for 循环?对这个算法有什么想法吗?

以下函数生成示例 space 列表。下面的打印语句还显示了如何检索总和列表。在我的示例中,我没有将函数调用分配给要绘制的变量,但您可以自己实现。 (我不是万无一失的,所以如果这对你不起作用或者你不明白,请告诉我)

def foo(n, rolls=[], roll=[]):
if n > 0:
    for i in range(1,7):
        foo(n-1, rolls, roll+[i])
else:
   rolls.append(roll)
return rolls

print(rolls:=foo(3), "\n\n", [sum(roll) for roll in rolls])

要像 Bill 提到的那样制作更直观的代码:

diceNum=10     # number of dices
ss = 6**diceNum # number of elements of the sample space: throw n dices and write points of each dice. (n**N)

diceSum = []
diceThrow = []


def roll_dice(diceThrow_n1)->list: # diceThrow_n1 is the list for n dices thrown
    #check if it's the first dice
    if diceThrow_n1 == []:
        diceThrow_n2=np.array([[i] for i in range(1,7)])
    else:
        diceThrow_n2 = [] # list for n+1 thrown dices
        for d in diceThrow_n1:
            for t in range(1,7): # throw the n+1 dice
                diceThrow_n2.append([*d,t])
    return diceThrow_n2

for d in range(diceNum): # Throw diceNum dices
    diceThrow = roll_dice(diceThrow)

diceSum = [sum(elm) for elm in diceThrow] # Sum each element of the sample space

roll_dice 获取您的 diceThrown 列表除了获取所有 List 条目并添加另一个骰子掷出的结果之外什么都不做。 (所以每次执行该函数时,diceThrow 中的条目都会乘以 6)。

让我们检查 diceNum = 2: 在第一次执行 d = 0(第一次掷骰子)时,我们提供 roll_dice 一个空列表 (diceThrow = [])。因此 roll_dice 用 1 ...6 填充 diceThrow (diceThrow = [[1],[2],[3],[4],[5],[6]])。 所以现在 d = 1: roll_dice 以 diceThrow = [[1],[2],[3],[4],[5],[6]] 开头: 所以我们转到函数的其他部分。现在我们遍历列表中的所有条目。从 d = [1] 开始。 (* 在列表前面给出所有元素 (*[a,b,c]=a,b,c)) 它获取每个掷出的骰子并将下一次掷出的结果相加。所以 [1] 变为 [[1,1],[1,2],[1,3],...,[1,6]]。但是 1 只是第一次抛出的一个可能结果,所以我们对所有其他可能的结果都这样做,并以:

diceThrow = [[1 1]
 [1 2]
 [1 3]
 [1 4]
 [1 5]
 [1 6]
 [2 1]
 [2 2]
 [2 3]
 [2 4]
 [2 5]
 [2 6]
 [3 1]
 [3 2]
 [3 3]
 [3 4]
 [3 5]
 [3 6]
 [4 1]
 [4 2]
 [4 3]
 [4 4]
 [4 5]
 [4 6]
 [5 1]
 [5 2]
 [5 3]
 [5 4]
 [5 5]
 [5 6]
 [6 1]
 [6 2]
 [6 3]
 [6 4]
 [6 5]
 [6 6]]

现在可以对每个额外的骰子重复此操作。 最后,我们像您之前所做的那样对每个条目求和,但是每次我们在 diceThrow 中创建一个新条目时都这样做,我们在最后这样做是为了防止我们多次这样做。 但问题就在这里开始了。这是非常低效的,因为 python 中的列表不是最快的。我们一次又一次地这样做。创建一个列表,创建一个更大的列表,....

一种更好但不太直观的方法是使用 numpy。

diceNum=2
diceThrow = np.array(np.meshgrid(*([np.array([1,2,3,4,5,6])]*diceNum))).T.reshape(-1,diceNum)
diceSum = [sum(elm) for elm in diceThrow]

原则上,您将 meshgrid 函数 diceNum np.arrays 赋予 1,2,...,6],然后以获得 diceNum 骰子的方式对其进行整形。 关于这里发生的事情的一个很好的解释(对我 xD 也有一点启发),你可以在这里找到 Numpy: efficient way to generate combinations from given ranges and Using numpy to build an array of all combinations of two arrays 但即使这样,我也会在 diceNum > 10 的情况下进入长时间运行。也许其他人有一个好主意,即保持实用方法而不使用任何分析理论。