骰子卷的分布直方图

时间:2015-01-23 22:06:59

标签: c++ algorithm

我看到了关于jobcup的问题,但我没有得到我想要的答案。我自己写了一个答案,希望您对我对时间复杂度的分析发表评论,并对算法和代码进行评论。或者你可以在时间方面提供更好的算法。感谢。

你被给予d> 0个公平骰子,n> 0" sides",写一个函数,返回骰子结果频率的直方图。

例如,对于2个骰子,每个骰子有3个边,结果是:

(1, 1) -> 2
(1, 2) -> 3
(1, 3) -> 4
(2, 1) -> 3
(2, 2) -> 4
(2, 3) -> 5
(3, 1) -> 4
(3, 2) -> 5
(3, 3) -> 6

该函数应返回:

2: 1
3: 2
4: 3
5: 2
6: 1

(我的溶胶)。如果使用强力深度优先搜索,则时间复杂度为O(n ^ d)。但是,您可以使用DP思想来解决此问题。例如,d = 3且n = 3。计算d == 2时可以使用d == 1的结果:

d==1

num  #
1    1
2    1
3    1

d==2

first roll     second roll is 1
num  #         num  # 
1    1         2    1                  
2    1      -> 3    1
3    1         4    1

first roll     second roll is 2
num  #         num  # 
1    1         3    1                  
2    1      -> 4    1
3    1         5    1

first roll     second roll is 3
num  #         num  # 
1    1         4    1                  
2    1      -> 5    1
3    1         6    1

因此,

second roll
num  #
2    1
3    2
4    3
5    2
6    1

此DP算法的时间复杂度为

SUM_i(1:d) {n*[n(d-1)-(d-1)+1]} ~ O(n^2*d^2)
               ~~~~~~~~~~~~~~~ <--eg. d=2, n=3, range from 2~6 

代码用C ++编写如下

vector<pair<int,long long>> diceHisto(int numSide, int numDice) {
int n = numSide*numDice;
vector<long long> cur(n+1,0), nxt(n+1,0);
for(int i=1; i<=numSide; i++) cur[i]=1;

for(int i=2; i<=numDice; i++) { 
    int start = i-1, end = (i-1)*numSide; // range of previous sum of rolls
    //cout<<"start="<<start<<"   end="<<end<<endl;
    for(int j=1; j<=numSide; j++) {
        for(int k=start; k<=end; k++) 
            nxt[k+j] += cur[k];
    }

    swap(cur,nxt);
    for(int j=start; j<=end; j++) nxt[j]=0; 
}
vector<pair<int,long long>> result;
for(int i=numDice; i<=numSide*numDice; i++)
    result.push_back({i,cur[i]});
return result;

}

2 个答案:

答案 0 :(得分:1)

你可以在O(n * d ^ 2)中完成。首先,请注意,n边骰子的生成函数是p(n)= x + x ^ 2 + x ^ 3 + ... + x ^ n,并且d抛出的分布具有生成函数p(n )^ d。将多项式表示为数组,需要O(nd)系数,并且通过保持滚动总和,可以在O(nd)时间内单次通过p(n)。

这是一些实现此功能的python代码。它有一个非显而易见的优化:它从每个p(n)中抛出一个因子x(或者等效地,它将骰子视为具有面0,1,2,...,n-1而不是1,2, 3,...,n)这就是为什么在显示分布时加入d的原因。

def dice(n, d):
    r = [1] + [0] * (n-1) * d
    nr = [0] * len(r)
    for k in xrange(d):
        t = 0
        for i in xrange(len(r)):
            t += r[i]
            if i >= n:
                t -= r[i-n]
            nr[i] = t
        r, nr = nr, r
    return r

def show_dist(n, d):
    for i, k in enumerate(dice(n, d)):
        if k: print i + d, k

show_dist(6, 3)

时间和空间的复杂性很容易看出:有d和(n-1)* d迭代的嵌套循环因此时间复杂度为O(nd ^ 2),并且有两个大小为O(nd)且没有其他分配的数组,因此空间复杂度为O(nd)。

答案 1 :(得分:0)

以防万一,这里是Python中使用the OpenTurns platform的简单示例。

import openturns as ot
d = 2 # number of dice
n = 6 # number of sides per die

# possible values
dice_distribution = ot.UserDefined([[i] for i in range(1, n + 1)])

# create the sum distribution d times the sum
sum_distribution = sum([dice_distribution] * d)

就是这样!

print(sum_distribution)

将向您显示所有可能的值及其对应的概率:

>>> UserDefined(
     {x = [2], p = 0.0277778},
     {x = [3], p = 0.0555556},
     {x = [4], p = 0.0833333},
     {x = [5], p = 0.111111},
     {x = [6], p = 0.138889},
     {x = [7], p = 0.166667},
     {x = [8], p = 0.138889},
     {x = [9], p = 0.111111},
     {x = [10], p = 0.0833333},
     {x = [11], p = 0.0555556},
     {x = [12], p = 0.0277778}
   )

您还可以绘制概率分布函数:

sum_distribution.drawPDF()

Rendered image of the probability distribution function