Modifica funzioni di distribuzione casuali :: Riduci le probabilità di ottenere più valori simili in una sequenza


8

Voglio generare una sequenza di numeri per la generazione procedurale di pianeti in un settore galattico. Ogni pianeta dovrebbe essere posizionato in modo casuale, tuttavia dovrebbe essere molto improbabile che due pianeti siano direttamente uno accanto all'altro. Come posso raggiungerlo?

So che puoi modificare le possibilità applicando una funzione di distribuzione, ma come posso controllarle per rendere più / meno probabili valori specifici?

Gif che mostra l'idea di modificare una curva di probabilità in base ai valori già generati.


2
La semplice aggiunta di una distanza minima assicurerebbe che un pianeta non sia vicino a un altro. Stimo che sia troppo semplice, quindi potresti approfondire un po 'di più?
Madmenyo

@MennoGouw Sì, questo lo risolverebbe per questo caso specifico, anche se voglio migliorare la mia comprensione della probabilità, quindi sto cercando una soluzione "più morbida" senza limiti rigidi / scartando i numeri generati.
API-Beast

Chiarire la soluzione "più morbida". Si tratta di stabilire regole. Quando sono necessarie determinate regole per la generazione procedurale, è necessario aggiungere queste regole. Se hai casi speciali hai impostato più o diverse regole per questi.
Madmenyo

Non sono sicuro del motivo per cui non usi solo un generatore che ha un'ottima reputazione sulla sua distribuzione? (Penso che il twister di Mersenne non sia male.)
Vaillancourt

2
Sono d'accordo. La generazione casuale in sé non è il problema. In questo modo puoi persino interrompere il tuo generatore casuale rendendolo prevedibile. La generazione di regole è la strada da percorrere.
ashes999,

Risposte:


8

Se conosci la distribuzione che desideri, puoi utilizzare il campionamento del rifiuto .

Il modo più semplice: nel grafico sopra, seleziona i punti a caso finché non ne trovi uno sotto la curva. Quindi basta usare il xcoordinato.

Per la distribuzione effettiva, ci sono vari approcci plausibili. Ad esempio, per il numero del pianeta inella posizione pe alcuni parametri di intensità k(ad es. 0.5), Definire una funzione f_i(x)=abs(p-x)^k, quindi utilizzare la funzione di distribuzione g(x)=f_1(x)*f_2(x)*...*f_n(x).

In pratica, calcola e archivia i risultati di g(x)to array t( t[x]=g(x)); ricordare anche il valore più alto visto h. Scegli una posizione casuale xin t, scegli un valore casuale ytra 0e h, ripeti if y>t[x]; altrimenti il ​​valore di ritorno è x.


Potresti approfondire un po 'la definizione della funzione di distribuzione? Il resto dovrebbe essere abbastanza chiaro.
API-Beast

Ad esempio, se i pianeti attuali si trovano nelle posizioni 0.1, 0.3 e 0.8, g (x) = (abs (x-0.1) * abs (x-0.3) * abs (x-0.8)) ^ 0.5, dove "^" significa esponenziazione. (Questo è scritto in modo leggermente diverso dalla formula precedente, ma equivalente.) Questa funzione di distribuzione assomiglia approssimativamente alla gif nella tua domanda e non si basa su qualcosa in particolare. (Stringa di query per WolframAlpha: "grafico da 0 a 1 (abs (x-0.1) * abs (x-0.3) * abs (x-0.8)) ^ 0.5")
yarr

Caspita, quella funzione è dannatamente bella. Non sapevo che una funzione del genere fosse in realtà così semplice :) Link per il pigro: bit.ly/1pWOZMJ
API-Beast

1

Non sono sicuro che il problema sia completamente specificato dalla domanda, ma posso fornire alcune idee semplici, il secondo di questi fornirà numeri approssimativamente in base a ciò che la tua foto indica che vuoi.

In entrambi i casi, come è possibile rendersi conto che la funzione di distribuzione sta cambiando dopo ogni numero generato, e ha una memoria (cioè: non è markoviana ) e uno di questi metodi può rivelarsi impraticabile quando la 'memoria' (numero di numeri estratti in precedenza) diventa molto largo.

  1. Semplice:
    genera un numero casuale da una distribuzione piatta, confronta con numeri precedentemente disegnati, ripeti se 'troppo vicino'

  2. Questa risposta è più simile alla tua figura (supponendo che vogliamo trarre da 0..1):

    • crea un nuovo elenco ordinato, inserisci 0 e 1
    • genera un numero casuale da una funzione di distribuzione piatta: N_0
      • aggiungi questo numero all'elenco
    • alla prossima chiamata, disegna un altro numero N_1,
    • se N_1> N_0
      • disegna un nuovo numero casuale gaussiano con media = 1 e una deviazione standard o di qualsiasi cosa tu voglia, un numero più piccolo (rispetto a 1-N_1) manterrà i numeri casuali ulteriormente distanziati. Questo non garantirà una distanza minima tra i sorteggi, ma anche in questo caso la tua figura non sembra neanche.
    • caso opposto di N_1 <N_0 gestito in modo simile
    • nelle estrazioni successive continua a generare un numero casuale (N_i) da una distribuzione piatta
    • attraversa la tua lista per vedere tra quali due numeri estratti in precedenza si trova il nuovo numero (N_-, N_ +)
    • crea un nuovo numero casuale gaussiano con media (N_- + N _ +) / 2
    • aggiungi il numero di distribuzione flat (N_i) al tuo elenco (elenco ordinato)

i contenitori degli endpoint sono un caso speciale, ma dovrebbe essere abbastanza semplice da consentirti di capire come gestirli.


0

Pensa alla differenza tra 1 dado e 3 dadi . 1 dado ti dà una probabilità uniforme per tutti i valori, mentre 3 dadi tenderanno ad avere una probabilità più alta per i valori verso il centro.

Più "dadi" nella tua equazione, maggiore è la tua possibilità di ottenere qualcosa verso il centro. Quindi definiamo una funzione che può gestire qualsiasi numero in modo uniforme :

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

Ora possiamo definire una funzione di esempio per usare questo:

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

Ora il primo da notare è che questo codice in realtà non controlla se il selettore corrisponde già a uno dei punti. In tal caso, non genererà un punto, forse qualcosa che ti piacerebbe.

Per spiegare cosa sta succedendo qui è che CenterRandom genera una sorta di curva a campana. Questa funzione suddivide il piano in più curve a campana, una per coppia di punti esistenti. Il selettore ci dice da quale curva a campana generare. Poiché scegliamo linearmente, possiamo garantire che le coppie con spazi più ampi tra loro vengano scelte più spesso, ma lo lasciamo ancora completamente casuale.

Spero che questo ti indichi nella giusta direzione.


0

So che stai chiedendo una sequenza di posizioni casuali, ma se non sei limitato a generare il set in sequenza, c'è un altro approccio: generare un set di punti con la spaziatura desiderata.

Quello che penso tu voglia è un insieme di pianeti che sono ragionevolmente distanziati con una certa casualità. Invece di generare posizioni planetarie con un generatore di numeri casuali, genera la spaziatura dei pianeti con un generatore di numeri casuali. Ciò ti consentirà di controllare direttamente la distribuzione della spaziatura, utilizzando un generatore di numeri casuali che preleva da tale distribuzione. Questo è semplice in 1 dimensione.

In 2 dimensioni, ho visto alcuni approcci che generano "rumore blu" ma non conosco un modo per generare spazio con una distribuzione arbitraria. Questo articolo tratta l'approccio standard "prova a posizionarlo e rifiuta se è troppo vicino", ma puoi generarli tutti in una volta, con una soluzione "più morbida" posizionando tutti i tuoi punti, quindi usando Lloyd Relaxation per spostare tutti i pianeti su più posizioni desiderabili. Sposterà ulteriormente i pianeti troppo vicini. Le piastrelle ricorsive Wang sono un altro approccio che potrebbe essere utile. Questo articoloestende il problema alla generazione di pianeti con una densità e qualche altro oggetto come asteroidi con un'altra densità. Potresti anche essere in grado di generare rumore blu usando la serie di Fourier; Non ne sono sicuro. L'approccio della serie di Fourier ti consentirebbe anche di utilizzare distribuzioni arbitrarie invece del solo rumore blu.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.