2009-07-31 15 views
7

Mi piacerebbe avere un numero casuale come questo: (in C#)speciale di numeri casuali

Random r = new Random(); 
r.next (0,10) 

ma è importante per il numero casuale essere più vicino a 8, (o che si tratti di solito grande), Voglio dire se usiamo un per:

for (int i =0; i<...;i++) 
{ 
    write: r.next (0,10) 
} 

il risultato essere così;

8 7 6 9 1 0 5 3 2 
2 3 8 9 7 7 6 2 3 
8 8 9 7 2 8 2 8 4 
3 
+5

Quindi stai chiedendo per i numeri casuali che, ehm, non sono casuali? – blowdart

+12

No, non vuole, vuole solo una distribuzione diversa (più Gauss-like intorno all'8, non uniformemente distribuita tra tutti i numeri) – schnaader

+1

Mi piacerebbe generare numeri casuali ponderati. –

risposta

2

Hai bisogno di un funzione di distribuzione che prende un numero compreso tra 0 e 1 e lo converte in un numero compreso che si desidera, con un peso più elevato su un numero specifico. È possibile creare tale funzione con funzioni trigonometriche (sin, cos, ...), esponenziali o forse un polinomio.

UPDATE: Date un'occhiata al this page per ulteriori informazioni sulla distribuzione di probabilità

+1

WTF è un polinomio? Methinks hai scelto il pulsante "Pubblica la tua risposta" troppo veloce – paxdiablo

+3

un polinom è una funzione algebrica scritta nel formato: f (x) = an * x^n + (an-1) * x^(x-1) + ... a2 * x^2 + a1 * x + a0; –

+0

No, sarebbe un * polinomio *. Vedo che nessuno qui apprezza il mio (spesso strano, secondo lei che deve essere obbedito) umorismo. Penso che me ne vado a letto. – paxdiablo

29

È necessario pesare i risultati. Si può fare con qualcosa di simile:

private int[] _distribution = new int[] { 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9 }; 
Random _r = new Random(); 

public int GetWeightedRandom() 
{ 
    return _distribution[_r.Next(0, _distribution.Length)]; 
} 

Se sapevo che la mia gamma era piccolo e coerente, userei il tavolo - è banale per rendere la propria classe.

Per completezza, aggiungerò anche questa classe in. Questa classe prende in prestito dall'elaborazione dell'immagine e utilizza la funzione di correzione della gamma: un valore compreso tra 0 e 1 elevato a gamma, che restituisce un valore compreso tra 0 e 1 ma distribuito di più alla fascia bassa se gamma < 1.0 e altro alla fascia alta se gamma> 1.0.

public class GammaRandom { 
    double _gamma; 
    Random _r; 

    public GammaRandom(double gamma) { 
     if (gamma <= 0) throw new ArgumentOutOfRangeException("gamma"); 
     _gamma = gamma; 
     _r = new Random(); 
    } 
    public int Next(int low, int high) { 
     if (high <= low) throw new ArgumentOutOfRangeException("high"); 
     double rand = _r.NextDouble(); 
     rand = math.Pow(rand, _gamma); 
     return (int)((high - low) * rand) + low; 
    } 
} 

(dai commenti, spostato r fuori portata GetWeightedRandom(). Anche aggiunto il controllo a Next())

OK, facciamo davvero andare in città qui. Sto canalizzando John skeet per questo: è una classe astratta con una proprietà template che restituisce una funzione di trasformazione che associa l'intervallo [0..1) a [0..1) e ridimensiona il numero casuale in tale intervallo. Ho anche reimplementato gamma in termini di esso e implementato anche sin e cos.

public abstract class DelegatedRandom 
{ 
    private Random _r = new Random(); 
    public int Next(int low, int high) 
    { 
     if (high >= low) 
      throw new ArgumentOutOfRangeException("high"); 
     double rand = _r.NextDouble(); 
     rand = Transform(rand); 
     if (rand >= 1.0 || rand < 0) throw new Exception("internal error - expected transform to be between 0 and 1"); 
     return (int)((high - low) * rand) + low; 
    } 
    protected abstract Func<double, double> Transform { get; } 
} 

public class SinRandom : DelegatedRandom 
{ 
    private static double pihalf = Math.PI/2; 
    protected override Func<double, double> Transform 
    { 
     get { return r => Math.Sin(r * pihalf); } 
    } 
} 
public class CosRandom : DelegatedRandom 
{ 
    private static double pihalf = Math.PI/2; 
    protected override Func<double, double> Transform 
    { 
     get { return r => Math.Cos(r * pihalf); } 
    } 
} 
public class GammaRandom : DelegatedRandom 
{ 
    private double _gamma; 
    public GammaRandom(double gamma) 
    { 
     if (gamma <= 0) throw new ArgumentOutOfRangeException("gamma"); 
     _gamma = gamma; 
    } 
    protected override Func<double, double> Transform 
    { 
     get { return r => Math.Pow(r, _gamma); } 
    } 
} 
+0

Ah, mi hai battuto con questa risposta. +1. –

+3

Questo va bene per numeri relativamente piccoli, ma se si desidera una distribuzione centrata su 1000000, è necessario un array veramente grande ... Penso che sarebbe meglio usare una funzione di distribuzione. –

+0

8 è stato un esempio, infatti abbiamo: int deep anziché 10 e nient'altro che 8 (intendo vicino a 8 o 7 o 9) –

0

Sembra a me come si desidera che i numeri casuali per la ponderazione verso la fascia alta - questo sarebbe una valutazione corretta?

Qualcosa di simile this può aiutare (è Java, ma applicare i principi)

+0

No, non è giusto, ma è importante. –

2

Invece di utilizzare la variante di matrice, si potrebbe anche avere uno sguardo a questo SO answer che ha un collegamento a Math.NET Iridium che implementa generatori casuali non uniformi .

I vantaggi della variante di array sono che si ottiene un approccio più dinamico senza dover riscrivere l'array tutto il tempo. Potresti anche fare alcune cose che sarebbero praticamente impossibili con la variante dell'array (grandi numeri casuali non uniformi).

1

Con una sorta di ponderazione aggiuntiva che dovrebbe essere possibile. Dipende da come si specifica "vicino a otto". Un modo molto semplice per farlo è questo:

for (int i =0; i<...;i++) 
{ 
    n = r.next (0,100); 
    write: (n*n)/1000 
} 

La quadratura peserà i numeri verso la fascia bassa, vale a direin questo caso, il 33% delle volte riceverai uno 0, mentre riceverai uno 9 solo il 5% circa del tempo.

Questo metodo, naturalmente, è adattato per adattarsi al caso particolare.

1

Non esattamente quello che stai cercando, ma un modo molto semplice per approssimare una normale distribuzione di numeri è l'aggiunta di più generazioni.

Un classico esempio di questa tecnica è nel gioco Dungeons and Dragons in cui la forza di un personaggio può essere determinata facendo rotolare tre dadi a sei facce e aggiungendo i risultati. Questo dà un intervallo da 3 a 18 con i numeri intorno a 10 il più probabile. Le varianti includono:

  • Rotolare 4 dadi e scartare il più basso. Questo distorce la distribuzione verso numeri più alti.
  • Calcolare la media dei punteggi anziché aggiungerli. Questo rende l'intervallo di output più facile da capire.

In alternativa, this è abbastanza vicino ...