滚动骰子后得到特定总和的概率.红宝石

Pav*_*v31 4 ruby math probability standard-deviation

找到n个骰子滚动概率概率的最佳解决方案是什么?我正在通过寻找解决它

  1. 意思.
  2. 标准差.
  3. z_score下面的数字 x
  4. z_score代表上面的数字 x
  5. 将两者都转换为概率
  6. 从另一个中减去一个

这就是我到目前为止所做的.

# sides - number of sides on one die
def get_mean(sides)
  (1..sides).inject(:+) / sides.to_f
end

def get_variance(sides)
  mean_of_squares = ((1..sides).inject {|sum, side| sum + side ** 2}) / sides.to_f
  square_mean = get_mean(sides) ** 2

  mean_of_squares - square_mean
end

def get_sigma(variance)
  variance ** 0.5
end

# x - the number of points in question
def get_z_score(x, mean, sigma)
  (x - mean) / sigma.to_f
end

# Converts z_score to probability
def z_to_probability(z)
  return 0 if z < -6.5
  return 1 if z > 6.5

  fact_k = 1
  sum = 0
  term = 1
  k = 0

  loop_stop = Math.exp(-23)
  while term.abs > loop_stop do
    term = 0.3989422804 * ((-1)**k) * (z**k) / (2*k+1) / (2**k) * (z**(k+1)) / fact_k
    sum += term
    k += 1
    fact_k *= k
  end

  sum += 0.5
  1 - sum
end

# Calculate probability of getting '?' total points by rolling 'n' dice with 'sides' number of sides.
def probability_of_sum(x, n, sides=6)

  mean = n * get_mean(sides)
  variance = get_variance(sides)
  sigma = get_sigma(n * variance)

  # Rolling below the sum
  z1 = get_z_score(x, mean, sigma)
  prob_1 = z_to_probability(z1)

  # Rolling above the sum
  z2 = get_z_score(x+1, mean, sigma)
  prob_2 = z_to_probability(z2)

  prob_1 - prob_2
end

# Run probability for 100 dice
puts probability_of_sum(400, 100)
Run Code Online (Sandbox Code Playgroud)

我担心的是,当我选择时x = 200,概率为0.这是正确的解决方案吗?

Nei*_*ter 6

添加两个独立概率分布的结果与对两个分布进行卷积相同.如果分布是离散的,则它是离散卷积.

因此,如果单个骰子表示为:

probs_1d6 = Array.new(6) { Rational(1,6) }
Run Code Online (Sandbox Code Playgroud)

那么2d6可以像这样计算:

probs_2d6 = []
probs_1d6.each_with_index do |prob_a,i_a|  
  probs_1d6.each_with_index do |prob_b,i_b| 
    probs_2d6[i_a + i_b] = ( probs_2d6[i_a + i_b] || 0 ) + prob_a * prob_b
  end
end

probs_2d6
# =>  [(1/36), (1/18), (1/12), (1/9), (5/36), (1/6), 
#      (5/36), (1/9), (1/12), (1/18), (1/36)]
Run Code Online (Sandbox Code Playgroud)

尽管这对于骰子的两侧是n平方的,并且完全逻辑组合可以减少这种情况,但是对于更复杂的设置而言这样做通常不太灵活.这种方法的好处是你可以继续添加更多的骰子,并做其他更奇特的组合.例如,要获得4d6,您可以将2d6的两个结果卷积.使用有理数可以让您解决浮点精度方面的问题.

我跳过了一个细节,你需要存储初始偏移(正常六面模具的+1)并将它们加在一起,以便知道概率匹配的内容.

我在gem games_dice中创建了一个更复杂的逻辑版本,浮点而不是Rational,可以应对其他一些骰子组合.

这是使用上述方法以简单的方式对您的方法进行基本重写(简单地一次组合一个骰子的效果):

def probability_of_sum(x, n, sides=6)
  return 0 if x < n
  single_die_probs = Array.new(sides) { Rational(1,sides) }

  combined_probs = [1] # Represents chance of getting 0 when rolling 0 dice :-)

  # This is not the most efficient way to do this, but easier to understand
  n.times do
    start_probs = combined_probs
    combined_probs = []
    start_probs.each_with_index do |prob_a,i_a|  
        single_die_probs.each_with_index do |prob_b,i_b| 
          combined_probs[i_a + i_b] = ( combined_probs[i_a + i_b] || 0 ) + prob_a * prob_b
        end
    end
  end

  combined_probs[ x - n ] || 0
end

puts probability_of_sum(400, 100).to_f
# => 0.0003172139126369326
Run Code Online (Sandbox Code Playgroud)

注意,该方法实际上计算了100到600的完整概率分布,因此您只需要调用一次并存储数组(加上偏移量+100)一次,然后您可以执行其他有用的操作,例如大于a的概率一定数量.由于Rational在Ruby中使用数字,所有都具有完美的精度.

因为在你的情况下,你只有一种类型的骰子,我们可以避免使用Rational直到最后,仅使用整数(基本上是组合值的计数),并除以组合的总数(边数与力的数量) ).这速度要快得多,并在一秒钟内返回100秒骰子的值:

def probability_of_sum(x, n, sides=6)
  return 0 if x < n
  combined_probs = [1] # Represents chance of getting 0 when rolling 0 dice :-)

  n.times do
    start_probs = combined_probs
    combined_probs = []
    start_probs.each_with_index do |prob_a,i_a|  
        sides.times do |i_b| 
          combined_probs[i_a + i_b] = ( combined_probs[i_a + i_b] || 0 ) + prob_a
        end
    end
  end

  Rational( combined_probs[ x - n ] || 0, sides ** n )
end
Run Code Online (Sandbox Code Playgroud)


Dou*_*are 6

存在涉及二项式系数的交替和的精确解.我已经在几个地方(在QuoraMSE上)写了它,你可以在其他地方找到它,虽然有一些有缺陷的版本.请注意,如果实现了这一点,则可能需要取消比最终结果大得多的二项式系数,如果使用浮点运算,则可能会失去太多精度.

Neil Slater建议使用动态编程来计算卷积是一个很好的建议.它比二项式系数的总和慢,但相当稳健.您可以通过几种方式加快速度,例如通过平方使用取幂,以及使用快速傅里叶变换,但很多人会发现这些过度.

要修正你的方法,你应该对正常近似使用(简单)连续性校正,并限制你有足够骰子的上下文,并且你正在评估你预期正常近似将是好的最大值和最小值,无论是绝对意义还是相对意义.连续性校正是将n的计数替换为从n-1/2到n + 1/2的间隔.

滚动总数为200的方式的确切数量是7745954278770349845682110174816333221135826585518841002760,因此概率是除以6 ^ 100,即约1.18563 x 10 ^ -20.

简单连续性校正的正态近似是Phi((200.5-350)/ sqrt(3500/12)) - Phi((199.5-350)/ sqrt(3500/12))= 4.2×10 ^ -19.这在绝对意义上是准确的,因为它非常接近0,但它相差35倍,因此相对而言并不是很好.正态近似给出了更接近中心的更好的相对近似.