Wie ich oben in meinem Kommentar erwähnt habe, empfehle ich Ihnen, dieses Profil zu erstellen, bevor Sie Ihren Code überkomplizieren. Eine schnelle for
Summierung von Würfeln ist viel einfacher zu verstehen und zu ändern als komplizierte mathematische Formeln und das Erstellen / Suchen von Tabellen. Profilieren Sie immer zuerst, um sicherzustellen, dass Sie die wichtigen Probleme lösen. ;)
Es gibt jedoch zwei Möglichkeiten, ausgefeilte Wahrscheinlichkeitsverteilungen auf einen Schlag abzutasten:
1. Kumulative Wahrscheinlichkeitsverteilungen
Es gibt einen tollen Trick, um aus kontinuierlichen Wahrscheinlichkeitsverteilungen mit nur einer einheitlichen Zufallseingabe eine Stichprobe zu erstellen . Es hat mit der kumulativen Verteilung zu tun , der Funktion, die antwortet: "Wie groß ist die Wahrscheinlichkeit, dass ein Wert nicht größer als x wird?"
Diese Funktion nimmt nicht ab und beginnt bei 0 und steigt über ihre Domäne auf 1 an. Ein Beispiel für die Summe von zwei sechsseitigen Würfeln ist unten gezeigt:
Wenn Ihre kumulative Verteilungsfunktion eine bequem zu berechnende Inverse aufweist (oder Sie können sie mit stückweisen Funktionen wie Bézier-Kurven approximieren), können Sie diese verwenden, um eine Stichprobe aus der ursprünglichen Wahrscheinlichkeitsfunktion zu erstellen.
Die Umkehrfunktion verarbeitet das Parzellieren der Domäne zwischen 0 und 1 in Intervalle, die auf jede Ausgabe des ursprünglichen Zufallsprozesses abgebildet werden, wobei der Einzugsbereich jedes Bereichs mit seiner ursprünglichen Wahrscheinlichkeit übereinstimmt. (Dies gilt infiniteszimal für kontinuierliche Verteilungen. Für diskrete Verteilungen wie Würfelwürfe müssen wir vorsichtig runden.)
Hier ist ein Beispiel für die Emulation von 2d6:
int SimRoll2d6()
{
// Get a random input in the half-open interval [0, 1).
float t = Random.Range(0f, 1f);
float v;
// Piecewise inverse calculated by hand. ;)
if(t <= 0.5f)
{
v = (1f + sqrt(1f + 288f * t)) * 0.5f;
}
else
{
v = (25f - sqrt(289f - 288f * t)) * 0.5f;
}
return floor(v + 1);
}
Vergleichen Sie dies mit:
int NaiveRollNd6(int n)
{
int sum = 0;
for(int i = 0; i < n; i++)
sum += Random.Range(1, 7); // I'm used to Range never returning its max
return sum;
}
Sehen Sie, was ich über den Unterschied in der Code-Klarheit und Flexibilität meine? Der naive Weg mag mit seinen Schleifen naiv sein, aber er ist kurz und einfach, sofort offensichtlich, was er tut, und leicht auf verschiedene Würfelgrößen und -zahlen zu skalieren. Das Vornehmen von Änderungen am kumulativen Verteilungscode erfordert einige nicht triviale Berechnungen, und es wäre leicht, ohne offensichtliche Fehler zu brechen und unerwartete Ergebnisse zu erzielen. (Was ich hoffe, dass ich oben nicht gemacht habe)
Bevor Sie also eine klare Schleife beseitigen, stellen Sie unbedingt sicher, dass es sich wirklich um ein Leistungsproblem handelt, das diese Art von Opfer wert ist.
2. Die Alias-Methode
Die kumulative Verteilungsmethode eignet sich gut, wenn Sie die Umkehrung der kumulativen Verteilungsfunktion als einfachen mathematischen Ausdruck ausdrücken können. Dies ist jedoch nicht immer einfach oder sogar möglich. Eine zuverlässige Alternative für diskrete Verteilungen ist die sogenannte Alias-Methode .
Auf diese Weise können Sie eine beliebige diskrete Wahrscheinlichkeitsverteilung mit nur zwei unabhängigen, gleichmäßig verteilten Zufallseingaben abtasten.
Es funktioniert, indem Sie eine Verteilung wie die unten auf der linken Seite nehmen (keine Sorge, dass die Flächen / Gewichte nicht 1 ergeben, für die Alias-Methode ist das relative Gewicht wichtig) und sie in eine Tabelle wie die folgende umwandeln das richtige wo:
- Für jedes Ergebnis gibt es eine Spalte.
- Jede Spalte ist in höchstens zwei Teile unterteilt, die jeweils einem der ursprünglichen Ergebnisse zugeordnet sind.
- Die relative Fläche / das relative Gewicht jedes Ergebnisses bleibt erhalten.
(Diagramm basierend auf Bildern aus diesem ausgezeichneten Artikel über Stichprobenverfahren )
Im Code stellen wir dies mit zwei Tabellen (oder einer Tabelle von Objekten mit zwei Eigenschaften) dar, die die Wahrscheinlichkeit der Auswahl des alternativen Ergebnisses aus jeder Spalte und die Identität (oder den "Alias") dieses alternativen Ergebnisses darstellen. Dann können wir wie folgt aus der Distribution probieren:
int SampleFromTables(float[] probabiltyTable, int[] aliasTable)
{
int column = Random.Range(0, probabilityTable.Length);
float p = Random.Range(0f, 1f);
if(p < probabilityTable[column])
{
return column;
}
else
{
return aliasTable[column];
}
}
Dies erfordert einige Einstellungen:
Berechnen Sie die relativen Wahrscheinlichkeiten jedes möglichen Ergebnisses (wenn Sie also 1000d6 würfeln, müssen wir die Anzahl der Möglichkeiten berechnen, um jede Summe von 1000 auf 6000 zu bekommen).
Erstellen Sie ein Tabellenpaar mit einem Eintrag für jedes Ergebnis. Die vollständige Methode geht über den Rahmen dieser Antwort hinaus. Ich empfehle daher dringend, auf diese Erklärung des Algorithmus der Alias-Methode zu verweisen .
Speichern Sie diese Tabellen und greifen Sie jedes Mal darauf zurück, wenn Sie einen neuen zufälligen Würfelwurf aus dieser Verteilung benötigen.
Dies ist ein Kompromiss zwischen Raum und Zeit . Der Vorberechnungsschritt ist einigermaßen erschöpfend und wir müssen den Speicher entsprechend der Anzahl der erzielten Ergebnisse freigeben (obwohl wir auch bei 1000d6 von einstelligen Kilobyte sprechen, um den Schlaf nicht zu verlieren), sondern unsere Stichprobe austauschen ist konstante Zeit, egal wie komplex unsere Verteilung sein mag.
Ich hoffe, dass die eine oder andere dieser Methoden von Nutzen sein kann (oder dass ich Sie davon überzeugt habe, dass die Einfachheit der naiven Methode die Zeit wert ist, die für eine Schleife benötigt wird);)