确定骰子卷中显示的数字的频率

时间:2009-01-29 20:23:24

标签: algorithm probability dice

对于游戏,我正在尝试确定某个#将在给定的掷骰子#上显示的频率。我知道......这个问题似乎很奇怪。让我试着用实数来解释它。

因此,对于1个芯片,每个数字的频率将是相同的。 1-6将显示相同的次数。

现在有2个骰子,情况会有所不同。我想5,6,7将是最频繁滚动的,而频谱两端的数字将显示较少或根本不显示(在1的情况下)。我想知道如何计算这个列表并以正确的顺序显示它们,从最常见到较不频繁。

有什么想法吗?


@duffymo - 虽然有一些算法来提出它会非常好。似乎上述方式需要大量的手工采摘和数字放置。如果我的死亡数量是动态的,可以说是10,那么我认为手工操作将是无效的和麻烦的。 :)

9 个答案:

答案 0 :(得分:11)

两个骰子有6 * 6 = 36种组合。

2 = 1 + 1只能出现一次,因此其频率为1/36。 3 = 1 + 2或2 + 1,因此其频率为2/36 = 1/18。 4 = 1 + 3,2 + 2或3 + 1,因此其频率为3/36 = 1/12。

你可以休息十二点。

任何步步高玩家都清楚这些。

答案 1 :(得分:5)

没有必要使用真正的“算法”或模拟 - 这是基于De Moivre推导出的公式的简单计算:

http://www.mathpages.com/home/kmath093.htm

这不是“钟形曲线”或正态分布。

答案 2 :(得分:3)

粗略草案的递归方式:

public static IEnumerable<KeyValuePair<int, int>> GetFrequenciesByOutcome(int nDice, int nSides)
{
    int maxOutcome = (nDice * nSides);
    Dictionary<int, int> outcomeCounts = new Dictionary<int, int>();
    for(int i = 0; i <= maxOutcome; i++)
        outcomeCounts[i] = 0;

    foreach(int possibleOutcome in GetAllOutcomes(0, nDice, nSides))
        outcomeCounts[possibleOutcome] = outcomeCounts[possibleOutcome] + 1;

    return outcomeCounts.Where(kvp => kvp.Value > 0);
}

private static IEnumerable<int> GetAllOutcomes(int currentTotal, int nDice, int nSides)
{
    if (nDice == 0) yield return currentTotal;
    else
    {
        for (int i = 1; i <= nSides; i++)
            foreach(int outcome in GetAllOutcomes(currentTotal + i, nDice - 1, nSides))
                yield return outcome;
    }
}

除非我弄错了,否则这应该像[key,frequency]那样吐出KeyValuePairs。

编辑:仅供参考,运行此功能后,它会显示GetFrequenciesByOutcome(2,6)的频率为:

2:1

3:2

4:3

5:4

6:5

7:6

8:5

9:4

10:3

11:2

12:1

答案 3 :(得分:3)

通过移动它的位置来添加先前滚动的频率数组,“边数”,然后您将获得每个数字显示的频率数组。

1, 1, 1, 1, 1, 1  # 6 sides, 1 roll

1, 1, 1, 1, 1, 1
   1, 1, 1, 1, 1, 1
      1, 1, 1, 1, 1, 1
         1, 1, 1, 1, 1, 1
            1, 1, 1, 1, 1, 1
+              1, 1, 1, 1, 1, 1
_______________________________
1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1  # 6 sides, 2 rolls

1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
   1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
      1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
         1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
            1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
+              1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1
______________________________________________
1, 3, 6,10,15,21,25,27,27,25,21,15,10, 6, 3, 1  # 6 sides, 3 rolls

这比蛮力模拟要快得多,因为简单的方程是最好的。 这是我的python3实现。

def dice_frequency(sides:int, rolls:int) -> list:
    if rolls == 1:
        return [1]*sides
    prev = dice_frequency(sides, rolls-1)
    return [sum(prev[i-j] for j in range(sides) if 0 <= i-j < len(prev))
            for i in range(rolls*(sides-1)+1)]

例如,

dice_frequency(6,1) == [1, 1, 1, 1, 1, 1]
dice_frequency(6,2) == [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]
dice_frequency(6,3) == [1, 3, 6, 10, 15, 21, 25, 27, 27, 25, 21, 15, 10, 6, 3, 1]

请注意,您应该使用“目标号码 - 滚动计数”作为列表的索引来获取每个号码的频率。如果您想获得概率,请使用“side number”^'roll count'作为分母。

sides = 6
rolls = 3
freq = dice_frequency(sides,rolls)
freq_sum = sides**rolls
for target in range(rolls,rolls*sides+1):
    index = target-rolls
    if 0 <= index < len(freq):
        print("%2d : %2d, %f" % (target, freq[index], freq[index]/freq_sum))
    else:
        print("%2d : %2d, %f" % (target, 0, 0.0))

此代码yeilds

 3 :  1, 0.004630
 4 :  3, 0.013889
 5 :  6, 0.027778
 6 : 10, 0.046296
 7 : 15, 0.069444
 8 : 21, 0.097222
 9 : 25, 0.115741
10 : 27, 0.125000
11 : 27, 0.125000
12 : 25, 0.115741
13 : 21, 0.097222
14 : 15, 0.069444
15 : 10, 0.046296
16 :  6, 0.027778
17 :  3, 0.013889
18 :  1, 0.004630

答案 4 :(得分:2)

网上有很多关于骰子概率的东西。这是一个帮助我解决项目欧拉问题的链接:

http://gwydir.demon.co.uk/jo/probability/calcdice.htm

答案 5 :(得分:1)

使用动态函数创建的JavaScript实现:

<script>
var f;
function prob(dice, value)
 {
var f_s = 'f = function(dice, value) {var occur = 0; var a = [];';
for (x = 0; x < dice; x++)
 {
f_s += 'for (a[' + x + '] = 1; a[' + x + '] <= 6; a[' + x + ']++) {';
 }
f_s += 'if (eval(a.join(\'+\')) == value) {occur++;}';
for (x = 0; x < dice; x++)
 {
f_s += '}';
 }
f_s += 'return occur;}';
eval(f_s);
var occ = f(dice, value);
return [occ, occ + '/' + Math.pow(6, dice), occ / Math.pow(6, dice)];
 };

alert(prob(2, 12)); // 2 die, seeking 12
                    // returns array [1, 1/36, 0.027777777777777776]
</script>

编辑:相当失望没有人指出这一点;必须将6 * dice替换为Math.pow(6, dice)。没有更多的错误......

答案 6 :(得分:1)

整洁的事实......

你知道Pascal的三角形是N双面骰子总和的概率分布吗?

   1 1    - 1 die, 1 chance at 1, 1 chance at 2
  1 2 1   - 2 dice, 1 chance at 2, 2 chances at 3, 1 chance at 4
 1 3 3 1  - 3 dice, 1 chance at 3, 3 chances at 4, 3 chances at 5, 1 chance at 6 
1 4 6 4 1 - etc.

答案 7 :(得分:0)

似乎有一些神秘的确切围绕“为什么”这是,虽然duffymo已经解释了其中的一部分,但我正在看另一篇帖子说:

  

应该没有理由为什么5,6和7应该滚动得多[超过2],因为骰子的第一次滚动是来自第二次骰子滚动的独立事件,并且它们都具有相同的概率1- 6被卷起。

对此有一定的吸引力。但这是不正确的......因为第一次滚动会影响机会。推理可能最容易通过一个例子来完成。

说我想弄清楚两个骰子上掷2或7的概率是否更有可能。如果我掷出第一个骰子并获得3,那么我现在有多少机会滚动7个?显然,1比6。我总共2次滚动的几率是多少? 6比0 ...因为没有什么我可以在第二个骰子上滚动以使我的总数为2。

出于这个原因,7非常(最有可能)被卷起......因为无论我在第一个模具上滚动什么,我仍然可以通过在第二个模具上滚动正确的数字来达到正确的总数。 6和8同样略有可能,5和9更不可能,依此类推,直到我们达到2和12,同样不太可能在每36个机会中有1个。

如果你绘制这个(总和与可能性),你会得到一个很好的钟形曲线(或者更确切地说,由于你的实验的离散性,它会产生一个块状的近似)。

答案 8 :(得分:0)

经过大量的互联网搜索和stackoverflow搜索后,我发现Dr. Math在工作函数中解释得很好(另一个答案中的链接的公式不正确)。我把Math博士的公式转换为C#,我的nUnit测试(之前在代码中的其他尝试都失败了)全部通过了。

首先,我必须编写一些辅助函数:

  public static int Factorial(this int x)
  {
     if (x < 0)
     {
        throw new ArgumentOutOfRangeException("Factorial is undefined on negative numbers");
     }
     return x <= 1 ? 1 : x * (x-1).Factorial();
  }

由于选择数学的方式,我意识到如果我有一个具有下限的重载因子函数,我可以减少计算。达到下限时,此功能可以纾困。

  public static int Factorial(this int x, int lower)
  {
     if (x < 0)
     {
        throw new ArgumentOutOfRangeException("Factorial is undefined on negative numbers");
     }
     if ((x <= 1) || (x <= lower))
     {
        return 1;
     }
     else
     {
        return x * (x - 1).Factorial(lower);
     }
  }

  public static int Choose(this int n, int r)
  {
     return (int)((double)(n.Factorial(Math.Max(n - r, r))) / ((Math.Min(n - r, r)).Factorial()));
  }

当这些已经到位时,我能够写出

  public static int WaysToGivenSum(int total, int numDice, int sides)
  {
     int ways = 0;
     int upper = (total - numDice) / sides; //we stop on the largest integer less than or equal to this
     for (int r = 0; r <= upper; r++)
     {
        int posNeg = Convert.ToInt32(Math.Pow(-1.0, r)); //even times through the loop are added while odd times through are subtracted
        int top = total - (r * sides) - 1;
        ways += (posNeg * numDice.Choose(r) * top.Choose(numDice - 1));
     }
     return ways;
  }