关于n个m面骰子的各点数出现的频率的问题。我觉得可以用n次m项展开式来描述。1个骰子m个面,有n个骰子。那么将会有 个组合情况,变换成 。实际上应该描述为
。
我们用 代表1个骰子的不同面。投n个骰子实际上就是n组 ,从每组中取一个面的组合。可见,我们用 来描述是合理的。因式分解后的每一项可以用 表示,其中 分别代表点数不同的面; 代表对应面出现的数量; 且 ;k是构成这种组合的所有可能情况。 就是投掷n个m面骰子的点数。将所有 相等的项的系数k相加得到的数K,K就是这个点数D的频数。频数除以总组合数 得到频率。
下面用python实现了一个计算n个m面骰子的各点数的频率算法。
算法源于上面的描述,但有所变化。主要的数据结构是两个list和一个字典。两个list分别保存一个骰子中各面的点数(face),标志各个骰子所投出的面的序号。字典用于保存各个点数对应的频数。
其主要思想是模拟一个n位的m进制数;n位代表n个骰子,每个位上的变化值是[0,m-1]代表了骰子不同点数的面。当这个n位m进制的数从0变化到
的时候,就对应了投出n个骰子的所有组合情况。以每一位的数作为序号,在face中查找对应的值,将其相加就得到n个骰子投出的点数。每出现一个点数,在字典times上对应的域加1。循环结束之后的times将保存各个点数以及对应的频次。
源码如下:
#-*- coding:utf-8 -*-
"""
NDM问题
"""
class NDM():
''' n个m面骰子问题 '''
face=[] #骰面点数
count=[] #标志各骰子出现的骰子面标号个数
times={} #记录NDM各个点数的频次
def __init__(self, n, m):
''' n:骰子数
m:骰子面数
start:骰子面起点值,默认为1
end:骰子面终点值,默认为m
step:骰子值步进,默认为1
(end-start+1)/step == m
'''
self.n = n
self.m = m
self.face = []
self.count = []
self.times = {}
self.xh = []
for i in range(1, m+1):
#face[i]表示第i面的点数
self.face.append(i)
for i in range(n):
#count[i]表示第i个骰子出现的骰子面序号
self.count.append(0)
self.count[0] = -1
#区间[n, m*n]各个点数出现的频数
for i in range(n, m*n+1):
self.times[i] = 0 #初始化为0
def showProbability(self, decimal):
s = self.m**self.n
for i in self.times:
print(i, self.times[i], str(round((self.times[i]*100.0/s),decimal))+"%")
def funCompute(self):
''' 计算频次 '''
while self.changeCount(0):
i = self.getDiceValue()
self.times[i] = self.times[i] + 1
return self.times, self.xh
def changeCount(self, c):
''' 每次改变count的值,即一个统计数位器
c为进位位
'''
bAllMax = True #所有骰子都记录到了最大值
for i in range(self.n):
if self.count[i] != self.m-1:
#只要还存在一个骰子还没有记录到最后一面,则表示还没有终止
bAllMax = False
break
if bAllMax:
return False #n个骰子全都记录到了最后一面
bRet = False
if c < self.n:
if (self.count[c] + 1 > self.m - 1): #有进位
self.count[c] = 0
bRet = self.changeCount(c+1)
else:
self.count[c] = self.count[c] + 1
return True
return bRet
def getDiceValue(self):
''' 获取投掷n个骰子的点数 '''
value = 0
#
xh = []
for i in self.count:
xh.append(i)
self.xh.append(xh)
#
for i in self.count:
#取得count里记录的每个骰子的面序号
#将各个骰子面的点数相加
value = value + self.face[i]
return value
4D6的结果如下:
>>> c=NDM(4,6)
>>> a,b=c.funCompute()
各个点数的频次如下
>>> a
{4: 1, 5: 4, 6: 10, 7: 20, 8: 35, 9: 56, 10: 80, 11: 104, 12: 125, 13: 140, 14: 146, 15: 140, 16: 125, 17: 104, 18: 80, 19: 56, 20: 35, 21: 20, 22: 10, 23: 4, 24: 1}
显示点数,频数,频率
>>> c.showProbability(3)
(4, 1, '0.077%')
(5, 4, '0.309%')
(6, 10, '0.772%')
(7, 20, '1.543%')
(8, 35, '2.701%')
(9, 56, '4.321%')
(10, 80, '6.173%')
(11, 104, '8.025%')
(12, 125, '9.645%')
(13, 140, '10.802%')
(14, 146, '11.265%')
(15, 140, '10.802%')
(16, 125, '9.645%')
(17, 104, '8.025%')
(18, 80, '6.173%')
(19, 56, '4.321%')
(20, 35, '2.701%')
(21, 20, '1.543%')
(22, 10, '0.772%')
(23, 4, '0.309%')
(24, 1, '0.077%')