n个m面骰子的点数问题

关于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%')

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值