2009-01-29 5 views
5

Für ein Spiel versuche ich die Häufigkeit zu bestimmen, mit der eine bestimmte Anzahl # bei einer bestimmten Anzahl von gewürfelten Würfeln angezeigt wird. Ich weiß ... diese Frage scheint merkwürdig. Lassen Sie mich versuchen, es mit reellen Zahlen zu erklären.Ermitteln der Häufigkeit von Zahlen, die in Würfelwürfen angezeigt werden

Also, für 1 sterben, die Frequenz für jede Nummer wird identisch sein. 1-6 wird gleich oft angezeigt.

Jetzt für 2 Würfel, die Dinge werden anders. Ich stelle mir vor, 5,6,7 werden am häufigsten gewürfelt, während Zahlen an beiden Enden des Spektrums weniger oder gar nicht erscheinen werden (im Fall von 1). Ich würde gerne wissen, wie man diese Liste berechnet und sie in der richtigen Reihenfolge zeigt, von den häufigsten zu den weniger häufigen.

Irgendwelche Gedanken?


@duffymo - Es wäre aber wirklich schön, eine Art Algorithmus zu haben, der dazu passt. Es scheint, dass der obige Weg viel Handarbeit und Zahlen erfordert. Wenn meine Zählung dynamisch ist, sagen wir bis 10, ist das unhandlich und mühsam, denke ich. :)

+0

Sind Sie die Zahlen nach oben hinzufügen? Warum bist du zu dem Schluss gekommen, dass 5,6 und 7 mehr auftauchen würden? – JoshFinnie

+1

@JoshFinnie - es könnte mir sein, dass nur angenommen wird, dass 5,6,7 wäre häufiger - aber ich basiere diese Annahme auf die Tatsache, dass eine Rolle 5 zu bekommen 2 + 3 & 4 + 1, während eine 3 kann nur mit einer 2 + 1 angezeigt werden; eine Sechs ist 3 + 3, 4 + 2, 5 + 1; usw. – bugfixr

Antwort

3

Rohentwurf einer rekursiven Art und Weise, es zu tun:

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

Wenn ich nicht irre, sollte diese KeyValuePairs ausspucken organisiert wie [Schlüssel, Häufigkeit].

EDIT: FYI, nachdem diese ausgeführt wird, zeigt es die Frequenzen für GetFrequenciesByOutcome (2, 6) sein:

2: 1

3: 2

4: 3

5: 4

6: 5

7: 6

8: 5

9: 4

10: 3

11: 2

12: 1

+0

Ich frage mich, warum dies abgelehnt wurde, da niemand sonst einen Algorithmus zur Verfügung gestellt hat. – mquander

+0

Nicht wahr - siehe Antwort # 1. – duffymo

+0

# 1 ist eine Berechnung, kein Algorithmus. Es wurde vorgeschlagen, dass die Berechnung manuell durchgeführt wird und dann die Tabelle hart-codiert (autsch) - ich hätte lieber einen echten Algorithmus wie diesen, als die Zahlen manuell zu berechnen und sie hart zu codieren. – bugfixr

11

Es gibt 6 * 6 = 36 Kombinationen für zwei Würfel.

2 = 1 + 1 kann nur einmal vorkommen, daher ist die Frequenz 1/36. 3 = 1 + 2 oder 2 + 1, so ist seine Frequenz 2/36 = 1/18. 4 = 1 + 3, 2 + 2 oder 3 + 1, so ist seine Häufigkeit 3/36 = 1/12.

Sie können den Rest bis zwölf erledigen.

Jeder Backgammonspieler kennt diese gut.

5

es kein wirklicher „Algorithmus ist "oder Simulation erforderlich - es ist eine einfache Berechnung basierend auf einer Formel von De Moivre abgeleitet:

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

Und es ist keine "Glockenkurve" oder Normalverteilung.

+1

Keine Glockenkurve? Bist du dir da sicher? Ich meine, es ist definitiv wie eine Glocke geformt, obwohl ich zugeben muss, dass meine Statistiken schwach genug sind, dass ich davon überzeugt werden könnte, dass es keine "normale" Verteilung ist und vielleicht nicht die klassische "Glockenkurve". – Beska

+0

(BTW, tolle Verbindung ... Ich werde es abstimmen, wenn Sie mich davon überzeugen können, dass es nicht wirklich eine Glockenkurve ist.) – Beska

+1

Es ist eine diskrete Verteilung, also könnte es keine echte Normalverteilung sein. Wie die meisten Verteilungen ist es jedoch asymptotisch normal - für eine große Anzahl von Würfeln ist die Normalverteilung eine bessere Annäherung (und rechnerisch einfacher). – user57368

0

Es scheint rund um genau ein Geheimnis zu sein, „warum“ das ist, und obwohl duffymo Teil davon erklärt hat, ich bin in einem anderen Beitrag suchen, der sagt:

Es gibt keinen Grund, warum soll 5 , 6 und 7 sollten mehr [als 2] gewürfelt werden, da der erste Würfelwurf ein unabhängiges Ereignis vom zweiten Wurf des Würfels ist und beide die gleiche Wahrscheinlichkeit von 1-6 haben, gerollt zu werden.

Es gibt eine gewisse Anziehungskraft darauf. Aber es ist falsch ... weil der erste Wurf die Chancen beeinflusst. Die Argumentation kann wahrscheinlich am einfachsten durch ein Beispiel erfolgen.

Angenommen, ich versuche herauszufinden, ob die Wahrscheinlichkeit, 2 oder 7 zu würfeln, bei zwei Würfeln wahrscheinlicher ist. Wenn ich den ersten Würfel würfle und eine 3 bekomme, was sind nun meine Chancen, insgesamt 7 zu würfeln? Offensichtlich 1 zu 6. Was sind meine Chancen, insgesamt 2 zu rollen? 0 in 6 ... weil es nichts gibt, was ich auf dem zweiten Würfel rollen kann, um meine Summe 2 zu sein.

Aus diesem Grund ist 7 sehr (am ehesten) wahrscheinlich gerollt ... weil egal was ich Rolle auf dem ersten Würfel, ich kann immer noch die richtige Summe erreichen, indem ich die richtige Zahl auf dem zweiten Würfel rolle. 6 und 8 sind gleichermaßen etwas weniger wahrscheinlich, 5 und 9 mehr, und so weiter, bis wir 2 und 12 erreichen, ebenso unwahrscheinlich bei 1 in 36 Chance pro Stück.

Wenn Sie dies (Summe vs Wahrscheinlichkeit) plotten, erhalten Sie eine schöne Glockenkurve (oder, genauer gesagt, eine blockige Annäherung von einem wegen der diskreten Natur Ihres Experiments).

1

JavaScript-Implementierung mit dynamischer Funktion Erstellung:

<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: Eher enttäuscht niemand hat darauf hingewiesen; musste 6 * dice durch Math.pow(6, dice) ersetzen. Keine Fehler wie die ...

+0

Ich bin neugierig, warum Sie diesen besonderen Ausruf gewählt haben. :) –

+0

Weil das ist eine viel angenehmer unterhaltsame Art und Weise Code zu schreiben, als was ich tendiere :) – mquander

+0

Ich mag es auch so zu denken. Erst kürzlich entdeckte dynamische Funktionserstellung/Modifikation in JavaScript; Freut mich, einen Nutzen dafür zu finden. –

1

Ordentlich factoid ...

Wussten Sie, dass Pascals Dreieck die Wahrscheinlichkeitsverteilung der Summen von N 2-seitige Würfel ist?

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. 
+0

Das ist ordentlich! Jetzt denke ich darüber nach, wie man diesen iterativen Gedankenprozess (wie man eine Reihe nach der nächsten in Pascals Dreieck erzeugt) zu M-seitigen Würfeln verallgemeinert. – mquander

0

Nach viel im Internet und Stackoverflow des Suchens, fand ich Dr. Math es auch in einer Arbeitsfunktion erklärt (eine Verbindung in einer anderen Antwort hat eine falsche Formel). Ich konvertierte die Formel von Dr. Math nach C# und meine nUnit-Tests (die zuvor bei anderen Code-Versuchen fehlgeschlagen waren) bestanden alle.

Zuerst hatte ich ein paar Hilfsfunktionen zu schreiben:

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

Aufgrund der Art und Weise funktioniert in Mathematik wählen, erkannte ich, ich könnte auf den Berechnungen abgeholzt, wenn ich mit einer unteren Grenze eine überladene Fakultätsfunktion hatte . Diese Funktion kann ausgelöst werden, wenn die Untergrenze erreicht ist.

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

Wenn diese an Ort und Stelle waren, war ich in der Lage zu schreiben

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

Fügen Sie die Anordnung von Häufigkeit der vorherigen Rollen bis ‚Seitennummer‘ mal von seiner Position verschoben wird, dann erhalten Sie die Array von Frequenzen erscheinen jeweils Nummern.

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 

Dies ist viel schneller als Brute-Force-Simulation, da einfache Gleichung die beste ist. Hier ist meine Python3-Implementierung.

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

zum Beispiel

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] 

Beachten Sie, dass, sollten Sie ‚Zielnummer - Rolle count‘ verwenden als Index der Liste Häufigkeit jeder Zahl zu erhalten. Wenn Sie Wahrscheinlichkeiten erhalten möchten, verwenden Sie 'Seitenzahl'^'Rollenanzahl' als Nenner.

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

Dieser 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