Определите частоту чисел, появляющихся в бросках костей - PullRequest
5 голосов
/ 29 января 2009

В игре я пытаюсь определить частоту, с которой определенное число # будет отображаться при данном количестве # бросаемых костей. Я знаю ... этот вопрос кажется странным. Позвольте мне попытаться объяснить это с помощью реальных чисел.

Таким образом, для 1 кубика частота каждого числа будет одинаковой. 1-6 будет отображаться равное количество раз.

Теперь за 2 кубика все становится иначе. Я предполагаю, что 5,6,7 будут наиболее часто бросаемыми, в то время как числа на обоих концах спектра будут отображаться меньше или не будут отображаться вообще (в случае 1). Я хотел бы знать, как рассчитать этот список и показать их в правильном порядке, от наиболее частых до менее частых.

Есть мысли?


@ duffymo - Было бы неплохо иметь какой-то алгоритм для его создания. Кажется, что вышеупомянутый способ потребует много ручного выбора и размещения чисел. Если мое число кубиков будет динамическим, скажем, до 10, я думаю, что делать это вручную будет неэффективно и хлопотно. :)

Ответы [ 9 ]

11 голосов
/ 29 января 2009

Есть 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.

Остальное можно сделать до двенадцати.

Любой игрок в нарды знает это хорошо.

5 голосов
/ 29 января 2009

Реального «алгоритма» или моделирования не требуется - это простой расчет на основе формулы, полученной Де Мойвром:

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

И это не «кривая колокола» или нормальное распределение.

3 голосов
/ 28 марта 2015

Сложите массив частот предыдущих бросков, «число сторон», сдвинув его позицию, и вы получите массив частот, которые будут отображать каждое число.

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]

Обратите внимание, что вы должны использовать 'target 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
3 голосов
/ 29 января 2009

Черновик рекурсивного способа сделать это:

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

2 голосов
/ 29 января 2009

В интернете много информации о вероятности игры в кости. Вот одна ссылка, которая помогла мне с вопросом Project Euler:

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

1 голос
/ 30 января 2009

Аккуратный фактоид ...

Знаете ли вы, что треугольник Паскаля является распределением вероятностей сумм 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.
1 голос
/ 30 января 2009

Реализация 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). Больше таких ошибок нет ...

0 голосов
/ 27 июня 2014

После долгих поисков в Интернете и переполнения стека, я нашел Dr. Математика хорошо объясняет это в рабочей функции (ссылка в другом ответе имеет неправильную формулу). Я преобразовал формулу доктора Матха в 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;
  }
0 голосов
/ 29 января 2009

Кажется, есть какая-то загадка, связанная именно с "почему", и хотя Даффимо объяснил часть этого, я смотрю на другой пост, который говорит:

Не должно быть никаких причин, по которым 5, 6 и 7 следует бросать больше [чем 2], так как первый бросок матрицы является независимым событием от второго броска фильеры, и оба имеют равную вероятность 1- 6 из проката.

Есть определенное обращение к этому. Но это неверно ... потому что первый бросок влияет на шансы. Рассуждение, вероятно, легче всего сделать на примере.

Скажем, я пытаюсь выяснить, является ли вероятность броска 2 или 7 более вероятной на двух кубиках. Если я брошу первый кубик и получу 3, каковы мои шансы бросить в общей сложности 7? Очевидно, 1 из 6. Каковы мои шансы бросить в общей сложности 2? 0 в 6 ... потому что я ничего не могу бросить на второй кубик, чтобы моя сумма была 2.

По этой причине 7 (с наибольшей вероятностью) выпадет ... потому что, независимо от того, что я бросаю на первом кубике, я все равно могу достичь правильной суммы, выполнив правильное число на втором кристалле. 6 и 8 одинаково немного менее вероятны, 5 и 9 более вероятны, и так далее, пока мы не достигнем 2 и 12, одинаково маловероятно при 1 из 36 шансов за штуку.

Если вы построите график (сумма против вероятности), вы получите хорошую кривую колокольчика (или, точнее, блочную аппроксимацию одного из-за дискретного характера вашего эксперимента).

...