问题描述
我正在尝试做一个程序,显示掷 N 个骰子的样本空间并写入每个骰子的结果,然后将样本空间每个元素的每个骰子的结果相加,然后最后用总和制作直方图。
我知道如何处理少量骰子,手动放置 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
个循环?对这个算法有什么想法吗?
解决方法
像比尔提到的那样制作更直观的代码:
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],[6]] 开头: 所以我们转到函数的 else 部分。现在我们遍历列表中的所有条目。从 d = [1] 开始。 (* 在列表前面给出了所有元素 (*[a,b,c]=a,c)) 它取每一个掷骰子并将下一次掷骰的结果相加。所以 [1] 到 [[1,1],[1,2],3],...,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,6],然后以你获得diceNum骰子的方式重塑它。 对这里发生的事情的一个很好的解释(对我 xD 有一点启发),你可以在这里找到 Numpy: efficient way to generate combinations from given ranges 和 Using numpy to build an array of all combinations of two arrays 但即便如此,我还是进入了 DiceNum > 10 的长时间运行。也许其他人有一个好主意,保留实用方法而不使用任何分析理论。
,以下函数生成样本空间列表。下面的打印语句还显示了如何检索总和列表。在我的示例中,我没有将函数调用分配给要绘制的变量,但您可以自己实现。 (我不是万无一失的,所以如果这对您不起作用或您不理解,请告诉我)
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])