Distribuzione casuale ponderata continua, distorta verso un'estremità


28

Attualmente sto contribuendo a un sistema di particelle per il nostro gioco e sviluppando alcune forme di emettitore.

La mia distribuzione casuale uniforme lungo una linea o lungo un'area rettangolare funziona bene - nessun problema.

Ma ora vorrei avere qualcosa come un gradiente 1 dimensionale in questa distribuzione. Ciò significherebbe ad esempio che valori più bassi sono più comuni dei valori più alti.

Non so quali sarebbero i termini matematici appropriati per questo problema, quindi le mie capacità di ricerca sono piuttosto inutili con questo. Ho bisogno di qualcosa che sia computazionalmente semplice, poiché il sistema particellare deve essere efficiente.



Nessuno parlerà del calcolo?
Alec Teal,

Risposte:


42

Dai un'occhiata a questa immagine:

Mappatura delle curve

Mostra il processo di mappatura di un valore (casuale) su una curva. Supponiamo di generare un valore casuale X distribuito uniformemente, compreso tra 0 e 1. Mappando questo valore su una curva - o, in altre parole, usando f (X) invece di X - puoi distorcere la tua distribuzione come preferisci .

In questa immagine, la prima curva rende più probabili valori più alti; il secondo rende più probabili valori più bassi; e il terzo rende i valori raggruppati nel mezzo. La formula esatta della curva non è molto importante e può essere scelta a piacere.

Ad esempio, la prima curva assomiglia un po 'alla radice quadrata e la seconda al quadrato. Il terzo è un po 'come il cubo, tradotto solo. Se consideri la radice quadrata troppo lenta, anche la prima curva appare come f (X) = 1- (1-X) ^ 2 - un'inversione del quadrato. O un'iperbole: f (X) = 2X / (1 + X).

Come mostra una quarta curva, puoi semplicemente usare una tabella di ricerca pre-calcolata. È brutta come una curva, ma probabilmente sarà abbastanza buona per un sistema di particelle.

Questa tecnica generale è molto semplice e potente. Qualunque sia la distribuzione di cui hai bisogno, immagina una mappatura delle curve e inventerai una formula in pochissimo tempo. Oppure, se il tuo motore ha un editor, crea un editor visivo per la curva!


grazie mille per la tua spiegazione molto approfondita e comprensibile. anche tutti gli altri post sono stati molto utili, ma potrei davvero capire il tuo post nel modo più semplice e veloce. è emerso perché ha davvero colpito il punto per il mio modo di capire le cose. e gli aspetti che stai spiegando sono esattamente quello che stavo cercando (o vagando)! mi permetterà di usarlo in molti casi in futuro. quindi grazie ancora !!! a proposito, ho giocato con alcune delle tue curve e funziona come un incantesimo.
didito

5
FYI: Queste sono chiamate funzioni quantili: en.wikipedia.org/wiki/Quantile_function
Neil G

8

Una spiegazione più lunga:

Se hai una distribuzione di probabilità desiderata come il gradiente richiesto da @didito, puoi descriverlo come una funzione. Supponiamo che tu voglia una distribuzione triangolare, dove la probabilità a 0 è 0,0, e vuoi scegliere un numero casuale da 0 a 1. Potremmo scriverlo come y = x.

Il prossimo passo è calcolare l'integrale di questa funzione. In questo caso, è x=1x2 . Valutato da 0 a 1, è ½. Questo ha senso: è un triangolo con base 1 e altezza 1, quindi la sua area è ½.

Quindi scegli un punto casuale uniformemente da 0 all'area (½ nel nostro esempio). Chiamiamo questo z. (Stiamo scegliendo uniformemente dalla distribuzione cumulativa .)

Il prossimo passo è tornare indietro, per trovare quale valore di x (lo chiameremo x̂) corrisponde a un'area di z. Stiamo cercando x=1x2 , valutato da 0 a x̂, pari a z. Quando risolvi per1x̂2=z, si ottienex̂=2z .

In questo esempio, scegli z da 0 a ½ e quindi il numero casuale desiderato è 2z . Semplificato, puoi scriverlo comerand(0,1) - esattamente ciò che eBusiness ha raccomandato.


grazie per il tuo contributo prezioso. mi piace sempre sentire come le persone qualificate risolvono i problemi. ma devo ancora avvolgerci la testa per essere onesto ...
didito

Questo e spettacolare. Ho sempre fatto sqrt(random())tutta la mia vita ma ci sono arrivato empiricamente. Cercando di legare un numero casuale a una curva, e ha funzionato. Ora che sono un po 'più esperto di matematica, sapere perché funziona è molto prezioso!
Gustavo Maciel,

5

Probabilmente otterresti una stretta approssimazione a ciò che desideri utilizzando un sistema esponenziale.

Crea la x in base a qualcosa come 1- (valore rnd ^) (Supponendo che rnd sia compreso tra 0 e 1) e otterrai alcuni comportamenti diversi di inclinazione da sinistra a destra in base a ciò che usi. Un valore più alto ti darà una distribuzione più distorta

Puoi utilizzare uno strumento di rappresentazione grafica online per ottenere alcune idee approssimative sui comportamenti che ti daranno le diverse equazioni prima di inserirle, oppure puoi semplicemente giocherellare con le equazioni direttamente nel tuo sistema di particelle, a seconda dello stile più adatto ai tuoi gusti.

MODIFICARE

Per qualcosa come un sistema di particelle in cui il tempo di CPU per particella è molto importante, l'uso diretto di Math.Pow (o equivalente del linguaggio) può portare a una riduzione delle prestazioni. Se si desiderano maggiori prestazioni e il valore non viene modificato in fase di runtime, considerare di passare a una funzione equivalente come x * x anziché x ^ 2.

(Gli esponenti frazionari potrebbero essere più un problema, ma qualcuno con un background matematico più forte di me probabilmente potrebbe trovare un buon modo per creare una funzione di approssimazione)


1
Invece di utilizzare un programma di rappresentazione grafica, puoi semplicemente tracciare la distribuzione Beta poiché questo è un caso speciale. Per un dato value, questo è Beta (valore, 1).
Neil G,

grazie. ho provato a tracciare alcuni grafici e penso che potrebbe portarmi dove voglio.
didito

@Neil G grazie per il suggerimento con "distribuzione beta" - sembra interessante e utile ... Farò qualche ricerca su questo argomento
didito

3

Il termine che stai cercando è Weighted Random Numbers , la maggior parte degli algoritmi che ho visto usano funzioni trig, ma penso di aver capito un modo che sarà efficiente:

Crea una tabella / matrice / Elenco (qualunque cosa) che contenga un valore moltiplicatore per la funzione casuale. Compilalo a mano o programmaticamente ...

randMulti= {.1,.1,.1,.1,.1,.1,.2,.2,.3,.3,.9,1,1,1,} 

... quindi Moltiplica randomper un valore scelto casualmente randMultie infine per il valore massimo della distribuzione ...

weightedRandom = math.random()*randMulti[Math.random(randMulti.length)]*maxValue

Credo che questo sarà molto più veloce dell'uso sqrt, o di altre funzioni più complesse dal punto di vista computazionale, e consentirà modelli di raggruppamento più personalizzati.


2
Se puoi sacrificare la memoria, una tabella di 100 valori pre-calcolati sarebbe più veloce (e leggermente più accurata). Dubito che l'utente sarebbe in grado di distinguere tra la versione completa e quella pre-calcolata.
Daniel Blezek,

@Daniel sarebbe più veloce, ma con 100 valori casuali, è abbastanza facile vedere schemi ripetitivi.
Attaccando Hobo

Solo perché sembra esserci uno schema ripetuto non significa che non sia casuale. L'essenza della casualità è la sua imprevedibilità, il che significa letteralmente che per quanto non si possa prevedere che non ci sarà uno schema, non si può nemmeno prevedere che potrebbe essercene uno (almeno per un breve periodo). Dovrai fare alcuni test, ma se trovi modelli con più test usando semi differenti, potrebbe essere necessario rivedere il tuo algoritmo per generare numeri pseudo-casuali.
Randolf Richardson,

@AttackingHobo grazie per quel trucco. mi piace l'uso di LUT. e la formula è abbastanza facile da capire. non ci avevo pensato prima. non vedendo il legno per gli alberi ... :) anche io penso che si dovrebbero evitare schemi ripetitivi, ma probabilmente in questo caso non verrebbero riconosciuti. tuttavia, pre-calcolare tutti i valori danneggerebbe l'esperienza visiva. comunque grazie per avermi ricordato che questo è un fattore da considerare sul tema della casualità ...
didito

grazie anche per aver menzionato il termine "Numeri casuali" ponderati!
didito,

2

Penso che ciò che chiedi sia la distribuzione ottenuta usando una funzione di radice quadrata.

[position] = sqrt(rand(0, 1))

Ciò fornirà una distribuzione nel campo della singola dimensione [0, 1] cui la probabilità di una posizione è equivalente a quella posizione, ovvero una "distribuzione triangolare".

Generazione alternativa senza squareroot:

[position] = 1-abs(rand(0, 1)-rand(0, 1))

Una radice quadrata nell'implementazione ottimale è solo una serie di comandi di moltiplicazione e somma senza rami. (Vedi: http://en.wikipedia.org/wiki/Fast_inverse_square_root ). Quale di queste due funzioni è più veloce può variare a seconda della piattaforma e del generatore casuale. Ad esempio, su una piattaforma x86 occorrerebbero solo pochi rami imprevedibili nel generatore casuale per rallentare il secondo metodo.


La probabilità di una posizione non sarà uguale alla posizione (ciò è matematicamente impossibile - banalmente, il dominio e l'intervallo della funzione include sia 0,50 e 0,51), né è una distribuzione triangolare. ( en.wikipedia.org/wiki/Triangular_distribution )

1
Mentre sqrt fornisce alcuni schemi interessanti, i sistemi di particelle in genere devono essere molto chiari CPU per particella, quindi consiglierei di evitare le radici quadrate (che sono lente dal punto di vista computazionale) ove possibile. A volte puoi cavartela semplicemente pre-calcolandoli, ma può far sì che le tue particelle presentino picchi evidenti nel tempo.
Lunin,

1
@Joe Wreschnig, hai letto tu stesso l'articolo di Wikipedia, inserisci a = 0, b = 1, c = 1 nella formula di generazione e ottieni la formula nel mio post.
aaaaaaaaaaaa,

3
@Lunin, perché ti lamenti della radice quadrata quando hai un esponente nella tua risposta?
aaaaaaaaaaaa,

1
@Lunin: la teoria delle prestazioni è un campo piuttosto trascurato, molto di ciò che la gente pensa di sapere circa 30 anni fa circa quando gli ALU erano molto costosi e lenti. Anche la funzione esponente che hai appena scoperto essere una funzione aritmetica piuttosto lenta è raramente un peccatore di prestazioni altamente significative. Branching (usando un'istruzione if) e cache misses (lettura di un dato che non risiede attualmente nella cache) sono generalmente quelli che costano di più.
aaaaaaaaaaaa

1

Usa una distribuzione Beta:

  • Beta (1,1) è piatto
  • Beta (1,2) è un gradiente lineare
  • Beta (1,3) è quadratico

eccetera.

I due parametri di forma non devono necessariamente essere numeri interi.


grazie per il vostro aiuto. come detto sopra, la distribuzione beta sembra interessante. ma non riesco ancora a capire il contenuto della pagina di Wikipedia. o una formula / codice. bene, anche in questo momento non ho tempo per indagare ulteriormente: si vede che boost ha un codice per le distribuzioni beta, ma questo sarebbe eccessivo. beh, suppongo di dover prima esaminarlo e poi scrivere la mia versione semplificata.
didito

1
@didito: non è così difficile. Basta sostituire la uniform_generator()chiamata con gsl_ran_beta(rng, a, b). Vedi qui: gnu.org/software/gsl/manual/html_node/…
Neil G

grazie per il suggerimento. non uso GSL (in realtà non ne ho mai sentito parlare prima), ma buona chiamata. controllerò la fonte!
didito,

@didito: In tal caso, andrei con la soluzione di Lunin. In bocca al lupo.
Neil G,

0

Ancora più semplice, a seconda della velocità del tuo generatore casuale, puoi semplicemente generare due valori e mediarli.

O, ancora più semplice, dove X è il risultato dell'rng, prima di tutto double y = double(1/x);,x = y*[maximum return value of rng]; . Questo peserà i numeri in modo esponenziale rispetto ai numeri più bassi.

Generare e mediare più valori per aumentare la probabilità di avvicinare i valori al centro.

Ovviamente questo funziona solo per le distribuzioni standard delle curve a campana o le versioni "piegate" *, ma con un generatore veloce, potrebbe essere più veloce e più semplice rispetto all'utilizzo di varie funzioni matematiche come sqrt.

Puoi trovare tutti i tipi di ricerche su questo per le curve a campana dei dadi. In effetti, Anydice.com è un buon sito che genera grafici per vari metodi di lancio dei dadi. Sebbene tu stia usando un RNG, la premessa è la stessa, così come i risultati. Quindi è un buon posto per vedere la distribuzione prima ancora di codificarla.

* Inoltre, è possibile "piegare" la distribuzione del risultato lungo un asse prendendo l'asse e sottraendo il risultato medio quindi aggiungendo l'asse. Ad esempio, vuoi che i valori più bassi siano più comuni e diciamo che vuoi che 15 sia il tuo valore minimo e 35 che sia il tuo valore massimo, un intervallo di 20. Quindi generi e media insieme due valori con un intervallo di 20 ( il doppio dell'intervallo desiderato), che fornirà una campana centrata su 20 (sottraggiamo cinque alla fine per spostare l'intervallo da 20 a 40, da 15 a 35). Prendi i numeri generati X e Y.

Numero finale,

z =(x+y)/2;// average them
If (z<20){z = (20-z)+20;}// fold if below axis
return z-5;// return value adjusted to desired range

Se zero è il tuo minimo, ancora meglio, fallo invece,

z= (x+y)/2;
If (z<20){z = 20-z;}
else {z = z - 20;}
return z;
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.