确定掷骰子中数字出现的频率

发布于 2024-07-12 13:44:45 字数 377 浏览 4 评论 0原文

对于游戏,我试图确定在给定的骰子数量下特定的 # 出现的频率。 我知道……这个问题看起来很奇怪。 让我尝试用实际数字来解释它。

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

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

有什么想法吗?


@duffymo - 如果有某种算法来提出它,那就太好了。 看来上述方法需要大量的手工挑选和放置数字。 如果我的骰子数量是动态的,例如 10,那么我认为手动执行此操作将是低效且麻烦的。 :)

For a game I'm trying to determine the frequency that a certain # will show up at a given # of dice being rolled. I know... that question seems odd. Let me try to explain it with real numbers.

So, for 1 die, the frequency for each number will be identical. 1-6 will show up equal number of times.

Now for 2 dice, things get different. I imagine 5,6,7 are going to be the most frequently rolled, while numbers at both ends of the spectrum will show up less or not at all (in the case of 1). I'd like to know how to calculate this list and show them in the proper order, from most frequent to less frequent.

Any thoughts?


@duffymo - It would be really nice though to have some sort of an algorithm to come up with it. It seems that the above way is going to require a lot of hand picking and placing of numbers. If my die count is dynamic up to say 10, doing that by hand will be ineffecient and troublesome I think. :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

扮仙女 2024-07-19 13:44:46

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

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

There's lots of stuff online about dice probability. Here's one link that helped me out with a Project Euler question:

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

风向决定发型 2024-07-19 13:44:46

纯粹的事实...

您知道帕斯卡三角形是 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.

Neat factoid...

Did you know that Pascal's triangle is the probability distribution of the sums of N 2-sided dice?

   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.
宣告ˉ结束 2024-07-19 13:44:46

使用动态函数创建的 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)。 以后不要再犯这样的错误了...

JavaScript implementation using dynamic function creation:

<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>

EDIT: Rather disappointed nobody pointed this out; had to replace 6 * dice with Math.pow(6, dice). No more mistakes like that...

残月升风 2024-07-19 13:44:46

围绕到底“为什么”这似乎存在一些谜团,尽管达菲莫已经解释了其中的一部分,但我正在看另一篇文章,上面写着:

没有理由掷出 5、6 和 7 的次数多于 2,因为第一次掷骰子与第二次掷骰子是独立的事件,并且两者的概率相同,均为 1- 6 被滚动。

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

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

因此,7 非常(最)有可能被滚动...因为无论我在第一个骰子上滚动什么骰子,我仍然可以通过在第二个骰子上滚动正确的数字来达到正确的总数。 6 和 8 的可能性同样稍小,5 和 9 的可能性更大,依此类推,直到我们达到 2 和 12,同样可能性为 36 分之一。

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

There seems to be some mystery surrounding exactly "why" this is, and although duffymo has explained part of it, I'm looking at another post that says:

There should be no reason why 5, 6 and 7 should be rolled more [than 2] since the first roll of the die is a independent event from the second roll of the die and both of them have equal probablity of 1-6 of being rolled.

There's a certain appeal to this. But it's incorrect...because the first roll affects the chances. The reasoning can probably most easily be done through an example.

Say I'm trying to figure out if the probability of rolling 2 or 7 is more likely on two dice. If I roll the first die and get a 3, what are my chances now of rolling a total of 7? Obviously, 1 in 6. What are my chances of rolling a total of 2? 0 in 6...because there's nothing I can roll on the second die to have my total be 2.

For this reason, 7 is very (the most) likely to be rolled...because no matter what I roll on the first die, I can still reach the correct total by rolling the right number on the second die. 6 and 8 are equally slightly less likely, 5 and 9 more so, and so on, until we reach 2 and 12, equally unlikely at 1 in 36 chance apiece.

If you plot this (sum vs likelyhood) you'll get a nice bell curve (or, more precisely, a blocky aproximation of one because of the discrete nature of your experiment).

是你 2024-07-19 13:44:46

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

由于 select 在数学中的工作方式,我意识到如果我有一个具有下限的重载 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;
  }

After a lot of searching on the Internet and stackoverflow, I found Dr. Math explains it well in a working function (a link in another answer has an incorrect formula). I converted Dr. Math's formula to C# and my nUnit tests (which had been failing before with other attempts at the code) all passed.

First I had to write a few helper functions:

  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();
  }

Because of the way choose works in mathematics, I realized I could cut down on the calculations if I had an overloaded Factorial function with a lower bound. This function can bail out when the lower bound is reached.

  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()));
  }

When those were in place, I was able to write

  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;
  }

两个骰子有 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。

剩下的你可以做到十二点。

任何双陆棋玩家都清楚这些。

There are 6*6 = 36 combinations for two dice.

2 = 1+1 can only appear once, so its frequency is 1/36.
3 = 1+2 or 2+1, so its frequency is 2/36 = 1/18.
4 = 1+3, 2+2, or 3+1, so its frequency is 3/36 = 1/12.

You can do the rest out to twelve.

Any backgammon player knows these well.

冰雪梦之恋 2024-07-19 13:44:45

递归方法的粗略草稿:

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;
    }
}

除非我弄错了,否则这应该会吐出像[键,频率]这样组织的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

Rough draft of a recursive way to do it:

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;
    }
}

Unless I'm mistaken, this should spit out KeyValuePairs organized like [key, frequency].

EDIT: FYI, after running this, it shows the frequencies for GetFrequenciesByOutcome(2, 6) to be:

2: 1

3: 2

4: 3

5: 4

6: 5

7: 6

8: 5

9: 4

10: 3

11: 2

12: 1

撧情箌佬 2024-07-19 13:44:45

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

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

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

There is no real "algorithm" or simulation necessary - it's a simple calculation based on a formula derived by De Moivre:

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

And it's not a "bell curve" or normal distribution.

孤独陪着我 2024-07-19 13:44:45

通过移动其位置,将之前滚动的频率数组“边数”倍相加,然后您将得到每个数字显示的频率数组。

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]

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

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

Add up the array of frequency of previous rolls, 'side number' times by shifting it's position, then you will get the array of frequencies each numbers show up.

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

This is much faster than brute force simulation, since simple equation is the best.
Here is my python3 implementation.

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)]

for example,

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]

Note that, you should use 'target number - roll count' as index of the list to get frequency of each number. If you want to get probabilities, use 'side number'^'roll count' as a denominator.

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))

This code 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文