Ändern von Zufallsverteilungsfunktionen :: Verringern Sie die Wahrscheinlichkeit, dass mehrere ähnliche Werte in einer Sequenz angezeigt werden

8

Ich möchte eine Folge von Zahlen erzeugen, um prozedural Planeten in einem Galaxiensektor zu erzeugen. Jeder Planet sollte zufällig platziert werden, es sollte jedoch sehr unwahrscheinlich sein, dass zwei Planeten direkt nebeneinander liegen. Wie kann ich das erreichen?

Ich weiß, dass Sie die Chancen durch Anwenden einer Verteilungsfunktion ändern können, aber wie kann ich sie steuern, um bestimmte Werte mehr oder weniger wahrscheinlich zu machen?

Gif zeigt die Idee, eine Wahrscheinlichkeitskurve in Abhängigkeit von den bereits generierten Werten zu ändern.

API-Beast
quelle
2
Durch einfaches Hinzufügen eines Mindestabstands wird sichergestellt, dass sich ein Planet nicht neben einem anderen befindet. Ich schätze, das ist zu einfach. Könnten Sie noch etwas näher darauf eingehen?
Madmenyo
@MennoGouw Ja, das würde es für diesen speziellen Fall lösen, obwohl ich mein Verständnis der Wahrscheinlichkeit verbessern möchte, also suche ich nach einer "weicheren" Lösung ohne harte Grenzen / Verwerfen generierter Zahlen.
API-Beast
Klären Sie die "weichere" Lösung. Es geht darum, Regeln festzulegen. Wenn Sie bestimmte Regeln für die Prozedurgenerierung benötigen, müssen Sie diese Regeln hinzufügen. Wenn Sie Sonderfälle haben, legen Sie mehr oder andere Regeln für diese fest.
Madmenyo
Ich bin mir nicht sicher, warum Sie nicht einfach einen Generator verwenden, dessen Vertrieb einen guten Ruf hat. (Ich denke, der Mersenne-Twister ist nicht schlecht.)
Vaillancourt
2
Genau. Die zufällige Erzeugung selbst ist nicht das Problem. Wenn Sie dies tun, kann Ihr Zufallsgenerator sogar beschädigt werden, indem Sie ihn vorhersehbar machen. Regelgenerierung ist der richtige Weg.
Asche999

Antworten:

8

Wenn Sie die gewünschte Verteilung kennen, können Sie die Ablehnungsstichprobe verwenden .

Einfachster Weg: Wählen Sie in der obigen Grafik zufällig Punkte aus, bis Sie feststellen, dass sich einer unterhalb der Kurve befindet. Dann benutze einfach die x-koordinate.

Für die tatsächliche Verteilung gibt es verschiedene plausible Ansätze. Definieren Sie beispielsweise für die Planetennummer iam Standort pund einige Stärkeparameter k(z. B. 0.5) eine Funktion f_i(x)=abs(p-x)^kund verwenden Sie dann die Verteilungsfunktion g(x)=f_1(x)*f_2(x)*...*f_n(x).

In der Praxis berechnen und speichern Sie die Ergebnisse von g(x)to array t( t[x]=g(x)); Denken Sie auch an den höchsten gesehenen Wert h. Wählen Sie eine zufällige Position xin t, wählen Sie einen zufälligen Wert yzwischen 0und h, wiederholen Sie if y>t[x]; Andernfalls lautet der Rückgabewert x.

yarr
quelle
Könnten Sie die Verteilungsfunktion etwas genauer definieren? Der Rest sollte ziemlich klar sein.
API-Beast
Wenn sich die aktuellen Planeten beispielsweise an den Positionen 0,1, 0,3 und 0,8 befinden, ist g (x) = (abs (x-0,1) * abs (x-0,3) * abs (x-0,8)) ^ 0,5, wobei "^" bedeutet Potenzierung. (Dies ist etwas anders geschrieben als die vorherige Formel, aber äquivalent.) Diese Verteilungsfunktion ähnelt in etwa dem GIF in Ihrer Frage und basiert nicht auf etwas Besonderem. (Abfragezeichenfolge für WolframAlpha: "Plot von 0 bis 1 (abs (x-0,1) * abs (x-0,3) * abs (x-0,8)) ^ 0,5")
yarr
Wow, diese Funktion ist verdammt cool. Wusste nicht, dass eine solche
API-Beast
1

Ich bin nicht sicher, ob das Problem durch die Frage vollständig spezifiziert ist, aber ich kann einige einfache Ideen liefern. Die zweite davon liefert Zahlen, die in etwa dem entsprechen, was Ihr Bild anzeigt.

Wie Sie vielleicht feststellen, ändert sich die Verteilungsfunktion nach jeder generierten Zahl und verfügt über einen Speicher (dh sie ist nicht markovisch ). Jede dieser Methoden kann sich als unpraktisch erweisen, wenn der 'Speicher' (Anzahl der zuvor gezeichneten Zahlen) abgerufen wird sehr groß.

  1. Einfach:
    Generieren Sie Zufallszahlen aus einer flachen Verteilung, vergleichen Sie sie mit zuvor gezeichneten Zahlen und wiederholen Sie den Vorgang, wenn 'zu nah'.

  2. Diese Antwort ähnelt eher Ihrer Zahl (vorausgesetzt, wir möchten aus 0..1 ziehen):

    • Erstellen Sie eine neue geordnete Liste, fügen Sie 0 und 1 ein
    • Zufallszahl aus einer flachen Verteilungsfunktion erzeugen: N_0
      • Fügen Sie diese Nummer der Liste hinzu
    • Zeichnen Sie beim nächsten Anruf eine andere Nummer N_1.
    • wenn N_1> N_0
      • Zeichnen Sie eine neue Gaußsche Zufallszahl mit Mittelwert = 1 und einer Standardabweichung o von allem, was Sie wollen. Eine kleinere Zahl (im Vergleich zu 1-N_1) hält die Zufallszahlen weiter voneinander entfernt. Dies garantiert keinen Mindestabstand zwischen den Zügen, aber Ihre Figur scheint es auch nicht zu sein.
    • Der entgegengesetzte Fall von N_1 <N_0 wird ähnlich behandelt
    • Bei nachfolgenden Ziehungen wird weiterhin eine Zufallszahl (N_i) aus einer flachen Verteilung generiert
    • Durchlaufen Sie Ihre Liste, um zu sehen, zwischen welchen zwei zuvor gezeichneten Zahlen die neue Zahl liegt (N_-, N_ +).
    • Erstellen Sie eine neue Gaußsche Zufallszahl mit dem Mittelwert (N_- + N _ +) / 2
    • Fügen Sie die flache Verteilungsnummer (N_i) zu Ihrer Liste (geordnete Liste) hinzu

Endpunktfächer sind ein Sonderfall, aber es sollte einfach genug sein, damit Sie sehen können, wie Sie damit umgehen.

Adam V. Steele
quelle
0

Denken Sie an den Unterschied zwischen 1 Würfel und 3 Würfel . 1 Würfel gibt Ihnen eine gleichmäßige Wahrscheinlichkeit für alle Werte, während 3 Würfel tendenziell eine höhere Wahrscheinlichkeit für die Werte in Richtung Mitte haben.

Je mehr "Würfel" in Ihrer Gleichung enthalten sind, desto größer ist Ihre Chance, etwas in Richtung Zentrum zu bringen. Definieren wir also eine Funktion, die jede Zahl gleichmäßig verarbeiten kann :

// Takes a random number between floor and ceil
// pow defines how strongly these results should gravitate towards the middle
// We also define a function TrueRand(floor, ceil) elsewhere where you should substitute your own random function
int CenterRandom(int floor, int ceil, int pow = 3)
{
    if(ceil == floor)
        return ceil; // don't care to compare

    int total = 0;
    for(int x = 0; x < pow; x++)
    {
       total += TrueRand(floor, ceil);
    }
    return total / pow;
}

Jetzt können wir eine Beispielfunktion definieren, um dies zu verwenden:

// Distribues a number of points between floor and ceil
// We assume a function PlotPoint(int) exists to aid in creating the planet, etc...
void DistributePoints(int floor, int ceil, int numPoints)
{
    // Could easily output this in the function parameters, but language wasn't specified
    int[numPoints] breaks;
    int numBreaks = 0;

    // Special case for first pair
    breaks[0] = CenterRandom(floor, ceil);
    numBreaks++;

    for(int x = 0; x < numPoints - 1; x++)
    {
        // Generate a random number linearly, this will be used for picking
        // This way we have a greater chance of choosing a random value between larger pairs
        int picker = TrueRandom(floor, ceil);

        // Now we first find the pair of points that our picker exists on
        // For simplicity, we handle the first and last pair separately

        if(picker >= floor && picker < breaks[0])
        {
            breaks[x] = CenterRandom(floor, breaks[0] - 1);
        }
        for(int i = 0; i < numBreaks; i++)
        {
            if(picker > breaks[i] && picker < breaks[i+1])
            {
                breaks[x] = CenterRandom(breaks[i] + 1, breaks[i+1] - 1);
            }
        }
        if(picker > breaks[numBreaks] && picker <= ceil)
        {
            breaks[x] = CenterRandom(breaks[numBreaks] + 1, ceil);
        }

        PlotPoint(breaks[x]); // Plot the point
    }
}

Als erstes ist zu beachten, dass dieser Code wirklich nicht prüft, ob die Auswahl bereits mit einem der Punkte übereinstimmt. Wenn dies der Fall ist, wird einfach kein Punkt generiert, möglicherweise etwas, das Sie möchten.

Um zu erklären, was hier vor sich geht, generiert CenterRandom eine Art Glockenkurve. Diese Funktion unterteilt die Ebene in mehrere Glockenkurven, eine pro vorhandenem Punktepaar. Der Picker teilt uns mit, aus welcher Glockenkurve generiert werden soll. Da wir linear auswählen, können wir sicherstellen, dass Paare mit größeren Lücken häufiger ausgewählt werden, aber wir lassen es trotzdem völlig zufällig.

Hoffe das weist dich in die richtige Richtung.


quelle
0

Ich weiß, dass Sie nach einer Folge von zufälligen Positionen fragen , aber wenn Sie nicht darauf beschränkt sind, die Menge nacheinander zu generieren, gibt es einen anderen Ansatz: Generieren Sie eine Menge von Punkten mit dem gewünschten Abstand.

Was ich denke, dass Sie wollen, ist eine Reihe von Planeten, die einigermaßen zufällig verteilt sind. Anstelle der Erzeugung Planeten - Positionen mit einem Zufallszahlengenerator, erzeugen Planeten Abstand mit einem Zufallszahlengenerator. Auf diese Weise können Sie die Verteilung des Abstands direkt steuern, indem Sie einen Zufallszahlengenerator verwenden, der aus dieser Verteilung auswählt. Dies ist in 1 Dimension unkompliziert.

In zwei Dimensionen habe ich einige Ansätze gesehen, die „blaues Rauschen“ erzeugen, aber ich kenne keinen Weg, um Abstände mit einer beliebigen Verteilung zu erzeugen. In diesem Artikel wird der Standardansatz "Versuchen Sie es zu platzieren und abzulehnen, wenn es zu nahe ist" behandelt. Sie können jedoch alle auf einmal mit einer "weicheren" Lösung generieren, indem Sie alle Ihre Punkte platzieren und dann mit Lloyd Relaxation alle Planeten auf mehr verschieben wünschenswerte Positionen. Es wird die zu nahen Planeten weiter auseinander bewegen. Rekursive Wang-Kacheln sind ein weiterer Ansatz, der nützlich sein könnte. Dieses Papiererweitert das Problem auf die Erzeugung von Planeten mit einer Dichte und einem anderen Objekt wie Asteroiden mit einer anderen Dichte. Möglicherweise können Sie auch mithilfe der Fourier-Reihe blaues Rauschen erzeugen. Ich bin mir nicht sicher. Mit dem Fourier-Reihen-Ansatz können Sie auch beliebige Verteilungen anstelle von nur blauem Rauschen verwenden.

amitp
quelle