预计每一次出现3次之前,掷骰子的次数


15

在每侧出现3次之前,必须掷骰子的预期次数是多少?

这个问题是在新西兰的小学提出的,并通过模拟解决了。该问题的解析解决方案是什么?


6
由于掷骰的结果是随机的,因此无法事先知道需要掷多少骰。例如,如果问题是要寻找的,则在每侧出现3次之前的预期滚动数应明确说明。在这种情况下,stats.stackexchange.com/tags/self-study/info适用。
Juho Kokkala's

3
告诉那些新西兰孩子阅读Norman L. Johnson,Samuel Kotz和N. Balakrishnan的“离散多元分布” wiley.com/WileyCDA/WileyTitle/productCd-0471128449.html
马克·L·斯通

Answers:


28

假设所有边都有相等的机会。让我们归纳并找到所需的预期滚动数,直到第面出现次,第面出现次,...,而面出现次。由于双方的身份无关紧要(他们都有平等的机会),因此可以简化此目标的描述:让我们假设双方根本不需要出现,双方只需出现一次,...和的侧面必须出现次。让1 n 1 2 n 2 d n d i 0 i 1 i n n = max n 1n 2n di = i 0i 1i nd=61ñ12ñ2dñd一世0一世1一世ññ=最大值ñ1ñ2ñd

一世=一世0一世1一世ñ
指定这种情况,并为预期的掷骰数
Ë一世
。这个问题要求Ë0006i_3 一世3=6表示所有六个侧面都需要被观察三次。

容易复发。 在下一卷中,出现的一面对应于i_j中的一个一世Ĵ:即,我们不需要看到它,或者我们需要看到一次,...,或者我们需要看到ñ次以上。Ĵ是我们需要看到它的次数。

  • Ĵ=0,我们不需要看到它,并且没有任何变化。这发生的概率为一世0/d

  • 当我们确实需要看到这一面。现在,有少一侧需要看到次,而另一侧需要看到次。因此,变为而变为。让对的组件的此操作指定为,这样Ĵ Ĵ - 1 Ĵ Ĵ - 1 Ĵ - 1Ĵ + 1 ĴĴ>0ĴĴ-1一世Ĵ一世Ĵ-1一世Ĵ-1一世Ĵ+1一世一世Ĵ

    一世Ĵ=一世0一世Ĵ-2一世Ĵ-1+1一世Ĵ-1一世Ĵ+1一世ñ

    这以概率。一世Ĵ/d

我们只需要计算该掷骰数并使用递归告诉我们预计还会有多少掷骰子。 根据期望和总概率定律,

Ë一世=1+一世0dË一世+Ĵ=1ñ一世ĴdË一世Ĵ

(让我们知道,每当,总和中的对应项为零。)一世Ĵ=0

如果,则完成,并且。否则,我们可以求解,给出所需的递归公式e i= 0 e i一世0=dË一世=0Ë一世

(1)Ë一世=d+一世1Ë一世1++一世ñË一世ñd-一世0

注意是我们希望看到的事件总数。如果,则对于任何,操作将其数量减少1 ,这种情况总是如此。因此,此递归在精确于的深度处终止。(等于问题中的)。此外,(不难检查)此问题中每个递归深度的可能性很小(从不超过)。因此,这是一种有效的方法,至少在组合可能性不是太多且我们记住中间结果的情况下(因此值Ĵ Ĵ > 0 Ĵ > 0 | | 3 6 = 18 8 e

|一世|=0一世0+1一世1++ñ一世ñ
ĴĴ>0一世Ĵ>0|一世|36=18岁8Ë 被多次计算)。

我计算出

Ë0006=22868786045088836998400000000032.677.

对我来说,这似乎太小了,所以我进行了一个模拟(使用R)。经过三百万次掷骰子后,此游戏已完成超过100,000次,平均长度为。该估计值的标准误差为:该平均值与理论值之间的差异不明显,从而确认了理论值的准确性。0.02732.6690.027

长度的分布可能是令人感兴趣的。(显然,它必须从开始,这是收集所有六面每3次所需的最小卷数。)18

数字

# Specify the problem
d <- 6   # Number of faces
k <- 3   # Number of times to see each
N <- 3.26772e6 # Number of rolls

# Simulate many rolls
set.seed(17)
x <- sample(1:d, N, replace=TRUE)

# Use these rolls to play the game repeatedly.
totals <- sapply(1:d, function(i) cumsum(x==i))
n <- 0
base <- rep(0, d)
i.last <- 0
n.list <- list()
for (i in 1:N) {
  if (min(totals[i, ] - base) >= k) {
    base <- totals[i, ]
    n <- n+1
    n.list[[n]] <- i - i.last
    i.last <- i
  }
}

# Summarize the results
sim <- unlist(n.list)
mean(sim)
sd(sim) / sqrt(length(sim))
length(sim)
hist(sim, main="Simulation results", xlab="Number of rolls", freq=FALSE, breaks=0:max(sim))

实作

尽管的递归计算很简单,但在某些计算环境中仍存在一些挑战。其中最主要的是在计算的值时将其存储。这是必不可少的,因为否则将(冗余)计算每个值很多次。但是,用索引的数组潜在需要的存储空间可能很大。理想情况下,仅应存储在计算过程中实际遇到的值。这需要一种关联数组。È ee(i)ii

为了说明,这是工作R代码。这些注释描述了用于存储中间结果的简单“ AA”(关联数组)类的创建。向量被转换为字符串,并且被用于索引包含所有值的列表。的操作被实现为。ĴiEij%.%

这些预备信息使递归函数定义相当简单,且与数学符号类似。特别是线e

x <- (d + sum(sapply(1:n, function(i) j[i+1]*e.(j %.% i))))/(d - j[1])

与上述公式直接可比。注意,所有索引都增加了因为它开始以而不是索引其数组。1 1 0(1)1R10

时间显示计算需要秒; 它的价值是0.01e(c(0,0,0,6))

32.6771634160506

累积的浮点舍入错误已破坏了最后两位(应该是68而不是06)。

e <- function(i) {
  #
  # Create a data structure to "memoize" the values.
  #
  `[[<-.AA` <- function(x, i, value) {
    class(x) <- NULL
    x[[paste(i, collapse=",")]] <- value
    class(x) <- "AA"
    x
  }
  `[[.AA` <- function(x, i) {
    class(x) <- NULL
    x[[paste(i, collapse=",")]]
  }
  E <- list()
  class(E) <- "AA"
  #
  # Define the "." operation.
  #
  `%.%` <- function(i, j) {
    i[j+1] <- i[j+1]-1
    i[j] <- i[j] + 1
    return(i)
  }
  #
  # Define a recursive version of this function.
  #
  e. <- function(j) {
    #
    # Detect initial conditions and return initial values.
    #
    if (min(j) < 0 || sum(j[-1])==0) return(0)
    #
    # Look up the value (if it has already been computed).
    #
    x <- E[[j]]
    if (!is.null(x)) return(x)
    #
    # Compute the value (for the first and only time).
    #
    d <- sum(j)
    n <- length(j) - 1
    x <- (d + sum(sapply(1:n, function(i) j[i+1]*e.(j %.% i))))/(d - j[1])
    #
    # Store the value for later re-use.
    #
    E[[j]] <<- x
    return(x)
  }
  #
  # Do the calculation.
  #
  e.(i)
}
e(c(0,0,0,6))

最后,这是产生正确答案的原始Mathematica实现。记忆是通过惯用语e[i_] := e[i] = ...表达来完成的,几乎消除了所有R预备。但是,在内部,这两个程序以相同的方式执行相同的操作。

shift[j_, x_List] /; Length[x] >= j >= 2 := Module[{i = x},
   i[[j - 1]] = i[[j - 1]] + 1;
   i[[j]] = i[[j]] - 1;
   i];
e[i_] := e[i] = With[{i0 = First@i, d = Plus @@ i},
    (d + Sum[If[i[[k]] > 0, i[[k]]  e[shift[k, i]], 0], {k, 2, Length[i]}])/(d - i0)];
e[{x_, y__}] /; Plus[y] == 0  := e[{x, y}] = 0

e[{0, 0, 0, 6}]

228687860450888369984000000000


5
+1我想对于那些被问到这个问题的学生来说,有些记号将很难遵循(不是我现在有任何具体的选择可以建议)。另一方面,我想知道他们打算如何处理这个问题。
Glen_b-恢复莫妮卡

1
@Glen_b通过实际掷骰子(并计算结果),他们可以学到很多东西。听起来很不错,可以在老师休息时让班级忙半小时:-)。
ub

12

这个问题的原始版本通过询问来开始生活:

每侧出现3次之前需要多少卷

当然,这是一个没有答案的问题,正如@JuhoKokkala上面评论的那样:答案是一个随机变量,需要找到分布。然后将问题修改为询问:“预期的卷数是多少”。以下答案旨在回答最初提出的问题:如何在不使用模拟的情况下找到卷数分布,而仅使用概念上简单的技术,任何拥有计算机的新西兰学生都可以实现为什么不这样做?问题减少到1班轮。

分配所需的卷筒数量...使得每侧出现3次

我们掷骰子次。令表示骰子的第面出现的次数,其中。然后,的联合pmf 是X { 1 ... 6 } X 1X 2... X 6多项式Ñ 1ñX一世一世一世{16}X1X2X6多项式ñ16

PX1=X1X6=X6=ñX1X616ñ 受: 一世=16X一世=ñ

令:那么的cdf 是:ñ={ñX一世3一世}ñPññ=PX一世3|ñ

即要找到cdf,只需为每个值计算:Pñññ={18岁1920}

PX13X63 哪里 X1X6多项式ñ16

例如,这里是Mathematica代码执行此操作,因为从18增加到60。它基本上是单行代码:ñ

 cdf = ParallelTable[ 
   Probability[x1 >= 3 && x2 >= 3 && x3 >= 3 && x4 >= 3 && x5 >= 3 &&  x6 >= 3, 
       {x1, x2, x3, x4, x5, x6} \[Distributed] MultinomialDistribution[n, Table[1/6, 6]]],
    {n, 18, 60}]

...随着增加产生确切的cdf :ñ

18岁14889875110199605761928290762544079842304203111983875176319369216211168408491253173748645888223283142988125507799783342082361483465418375609359740010496

这是cdf作为的函数的图:Pñññ

在此处输入图片说明

要导出pmf,只需首先将cdf相差:Pñ=ñ

在此处输入图片说明

当然,分布没有上限,但是我们可以在此处轻松解决实际需要的所有值。该方法是通用的,并且对于所需的任何所需的侧面组合也应同样有效。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.