6

对于一个游戏,我试图确定某个 # 将出现在给定的骰子 # 中出现的频率。我知道……这个问题似乎很奇怪。让我试着用实数来解释它。

因此,对于 1 个骰子,每个数字的频率将相同。1-6 将出现相同的次数。

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

有什么想法吗?


@duffymo - 虽然有某种算法来提出它会非常好。似乎上述方式将需要大量的手工挑选和放置数字。如果我的骰子数是动态的,最多可以说 10,那么我认为手工操作将是低效且麻烦的。:)

4

9 回答 9

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。

您可以在十二点之前完成其余的工作。

任何步步高玩家都知道这些。

于 2009-01-29T20:28:05.597 回答
5

不需要真正的“算法”或模拟 - 它是基于 De Moivre 推导出的公式的简单计算:

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

而且它不是“钟形曲线”或正态分布。

于 2009-01-29T20:42:51.900 回答
4

递归方式的草稿:

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

于 2009-01-29T20:33:37.050 回答
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))

此代码产生

 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
于 2015-03-28T14:44:56.520 回答
2

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

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

于 2009-01-29T20:28:57.090 回答
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 * diceMath.pow(6, dice). 不要再犯这样的错误了……

于 2009-01-29T21:10:25.277 回答
1

整洁的事实...

你知道帕斯卡三角形是 N 个 2 面骰子之和的概率分布吗?

   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.
于 2009-01-29T21:32:10.567 回答
0

似乎有一些关于“为什么”的谜团,虽然 duffymo 已经解释了其中的一部分,但我正在看另一篇文章,上面写着:

应该没有理由为什么 5、6 和 7 应该掷出更多 [超过 2],因为第一次掷骰子是独立于第二次掷骰子的事件,并且它们都有 1-6 的相同概率滚动。

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

假设我试图弄清楚掷出 2 或 7 的概率是否更有可能在两个骰子上。如果我掷出第一个骰子并得到 3,那么我现在总共掷出 7 的机会有多大?显然,六分之一。我总共滚动 2 的机会是多少?0 比 6...因为我无法在第二个骰子上滚动以使我的总数为 2。

出于这个原因,7 很有可能被掷出……因为无论我在第一个骰子上掷出什么,我仍然可以通过在第二个骰子上掷出正确的数字来达到正确的总数。6 和 8 的可能性同样略小,5 和 9 的可能性更大,依此类推,直到我们达到 2 和 12,同样不可能,各有 36 分之一的机会。

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

于 2009-01-29T20:51:51.443 回答
0

在互联网和stackoverflow上进行了大量搜索后,我发现Math博士在一个工作函数中很好地解释了它(另一个答案中的链接有一个不正确的公式)。我将 Dr. 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;
  }
于 2014-06-27T15:40:27.227 回答