Pergunta

Para um jogo que eu estou tentando determinar a frequência com que um certo # vai aparecer em um determinado # dos dados que estão sendo rolados. Eu sei ... essa pergunta parece estranha. Deixe-me tentar explicar isso com números reais.

Assim, para 1 dado, a frequência para cada número será idêntico. 1-6 aparecerá igual número de vezes.

Agora, para 2 dados, as coisas ficam diferentes. Imagino 5,6,7 vão ser mais frequentes rolou, enquanto os números em ambas as extremidades do espectro vai aparecer menos ou nada (no caso de 1). Eu gostaria de saber como calcular esta lista e mostrar-lhes na ordem correta, de mais frequente para menos frequente.

Qualquer pensamento?


@duffymo - Seria muito bom apesar de ter algum tipo de um algoritmo para chegar a ele. Parece que o caminho acima vai exigir uma grande quantidade de picking mão e colocando de números. Se minha contagem dado é até dinâmica dizer 10, fazendo que a mão será ineffecient e problemático eu acho. :)

Foi útil?

Solução

rascunho de uma forma recursiva para fazê-lo:

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

Se não me engano, isso deve cuspir KeyValuePairs organizados como [chave, frequência].

EDIT : FYI, após a execução desta, que mostra as frequências para GetFrequenciesByOutcome (2, 6) ser:

2: 1

3: 2

4: 3

5: 4

6: 5

7: 6

8: 5

9: 4

10: 3

11: 2

12: 1

Outras dicas

Existem 6 * 6 = 36 combinações de dois dados.

2 = 1 + 1 só pode aparecer uma vez, para a sua frequência é 1/36. 3 = 1 + 2 + 1 ou 2, pelo que a sua frequência é 2/36 = 18/01. 4 = 1 + 3, 2 + 2, ou 3 + 1, de modo que a sua frequência é 3/36 = 1/12.

Você pode fazer o resto para doze.

Qualquer jogador de gamão sabe estas bem.

Não existe um "algoritmo" real ou simulação necessário - é um simples cálculo baseado em uma fórmula derivada por De Moivre:

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

E não é uma "curva de sino" ou distribuição normal.

Adicione-se a matriz de frequência de rolos anteriores, 'número lateral' vezes por mudando a sua posição, então você vai ter a matriz de freqüências cada números mostram-se.

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

Este é muito mais rápido do que a simulação de força bruta, desde simples equação é o melhor. Aqui está minha implementação 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)]

Por exemplo,

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 que, você deve usar 'número de destino - Contagem roll' como índice da lista para obter frequência de cada número. Se você deseja obter probabilidades, use 'número lateral' ^ 'contagem roll' como denominador.

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

Esta yeilds código

 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

Há um monte de coisas online sobre dados de probabilidade. Aqui está um link que me ajudou a sair com uma pergunta Projeto Euler:

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

factóide Neat ...

Você sabia que o triângulo de Pascal é a distribuição de probabilidade das somas de N DICE 2 lados?

   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.

implementação JavaScript usando a criação de função dinâmica:

<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: Um pouco decepcionado ninguém apontou isso; teve que substituir 6 * dice com Math.pow(6, dice). Não há mais erros como esse ...

Parece haver algum mistério em torno exatamente "porque" este é, e embora duffymo explicou parte dela, eu estou olhando para outro post que diz:

Não deve haver nenhuma razão para que 5, 6 e 7 devem ser enroladas mais [de 2] desde o primeiro rolo do dado é um evento independente a partir do segundo rolo do dado e ambos têm igual probablity de 1- 6 de ser enrolado.

Há um certo apelo a esta. Mas é incorreta ... porque o primeiro rolo afeta as chances. O raciocínio pode provavelmente mais facilmente ser feito através de um exemplo.

dizer que estou tentando descobrir se a probabilidade de rolar 2 ou 7 é mais provável em dois dados. Se eu rolar o primeiro dado e obter a 3, quais são as minhas chances agora de rolar um total de 7? Obviamente, 1 em 6. Quais são minhas chances de rolar um total de 2? 0 em 6 ... porque não há nada que eu possa rolar no segundo dado para ter o meu total de ser 2.

Por esta razão, 7 é muito (mais) susceptível de ser enrolada ... porque não importa o que eu rolo no primeiro morrer, eu ainda pode chegar ao total correta rolando o número certo no segundo die. 6 e 8 são igualmente ligeiramente menos provável, 5 e 9, de modo mais, e assim por diante, até que atinjam 2 e 12, igualmente improvável a 1 em 36 possibilidade cada.

Se você traçar este (soma vs likelyhood) você terá uma curva agradável sino (ou, mais precisamente, uma aproximação blocos de alguém por causa da natureza discreta da sua experiência).

Depois de muita pesquisa na Internet e stackoverflow, eu achei Dr. Math explica muito bem em uma função de trabalho (um link em outra resposta tem uma fórmula incorreta). Eu converti fórmula do Dr. Math para meus testes NUnit C # e (que tinha sido falhando antes com outras tentativas de código) todos passaram.

Em primeiro lugar eu tinha que escrever algumas funções auxiliares:

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

Por causa da maneira escolher trabalhos em matemática, eu percebi que poderia reduzir os cálculos se eu tivesse uma função fatorial sobrecarregado com um limite inferior. Esta função pode socorrer quando o limite inferior é atingido.

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

Quando aqueles estavam no lugar, eu era capaz de escrever

  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;
  }
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top