6

ゲームの場合、特定のサイコロを振ったときに特定のサイコロが出る頻度を決定しようとしています。私は知っています...その質問は奇妙に思えます。実数で説明してみましょう。

したがって、1つのサイコロの場合、各番号の頻度は同じになります。1〜6は同じ回数表示されます。

2つのサイコロの場合、状況は異なります。5,6,7が最も頻繁にロールされると思いますが、スペクトルの両端の数字は表示されないか、まったく表示されません(1の場合)。このリストを計算して、頻度の高いものから低いものへと適切な順序で表示する方法を知りたいです。

何かご意見は?


@duffymo-それを思い付くためのある種のアルゴリズムがあるとはいえ、本当に素晴らしいでしょう。上記の方法では、多くの手作業による数字の選択と配置が必要になるようです。私のダイカウントが10まで動的である場合、それを手作業で行うのは非効率的で面倒だと思います。:)

4

9 に答える 9

11

2つのサイコロには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です。

残りは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;
    }
}

私が間違っていない限り、これは [キー、頻度] のように編成された KeyValuePairs を吐き出すはずです。

編集: 参考までに、これを実行した後、GetFrequencyByOutcome(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]

各番号の頻度を取得するには、リストのインデックスとして「ターゲット番号 - ロール カウント」を使用する必要があることに注意してください。確率を求める場合は、分母に「辺の数」^「ロール数」を使用します。

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

サイコロの確率については、オンラインでたくさんのことがあります。プロジェクトオイラーの質問で私を助けた1つのリンクは次のとおりです。

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

于 2009-01-29T20:28:57.090 に答える
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 に答える
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>

EDIT:誰もこれを指摘しなかったのはかなりがっかりしました。に置き換える必要がありまし6 * diceMath.pow(6, dice)。そんな間違いはもうない…

于 2009-01-29T21:10:25.277 に答える
0

インターネットと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();
  }

choose が数学で機能する方法のため、下限のあるオーバーロードされた階乗関数があれば、計算を削減できることに気付きました。この関数は、下限に達したときに救済できます。

  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 に答える
0

これが正確に「なぜ」なのかについては、いくつかの謎があるようです。ダッフィーモはその一部を説明していますが、別の投稿を見ています

サイコロの最初のロールはサイコロの 2 番目のロールから独立したイベントであり、それらの両方が 1 ~ 6 になる確率が等しいため、5、6、および 7 が [2 よりも] 多くロールされる必要がある理由はありません。巻いた。

これには一定の魅力があります。しかし、それは正しくありません...最初のロールがチャンスに影響するからです。推論は、おそらく例を通して最も簡単に行うことができます。

サイコロを 2 つ振って 2 または 7 が出る確率が高いかどうかを調べようとしているとします。最初のサイコロを振って 3 が出た場合、合計 7 になる可能性はどれくらいですか? 明らかに、6 分の 1 です。合計 2 が出る可能性はどのくらいですか? 0/6...2 番目のサイコロを振って合計が 2 になるものは何もないからです。

このため、7 が出る可能性が非常に (最も高く) あります...なぜなら、最初のサイコロで何を振っても、2 番目のサイコロで正しい数字を振れば、正しい合計に到達できるからです。6 と 8 は同様にわずかに可能性が低く、5 と 9 はそれ以上というように続き、2 と 12 に達するまで、それぞれ 36 分の 1 の確率で同様に可能性が低くなります。

これをプロットすると(合計対可能性)、素敵なベルカーブが得られます(または、より正確には、実験の離散的な性質のために、1つのブロック状の近似が得られます)。

于 2009-01-29T20:51:51.443 に答える