2009-07-31 7 views
7

Me gustaría tener un número aleatorio como esto: (en C#)especial de números aleatorios

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

pero es importante que el número aleatorio estar más cerca de 8, (o sea generalmente grandes), Me refiero a si usamos a para:

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

el resultado será así;

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

Así que usted está pidiendo números aleatorios que se equivocan, no son al azar? – blowdart

+12

No, no lo hace, solo quiere una distribución diferente (más de Gauss, alrededor de 8, no distribuida uniformemente entre todos los números) – schnaader

+1

Me gustaría generar números aleatorios ponderados. –

Respuesta

2

se necesita una función de distribución que tiene un número entre 0 y 1 y lo convierte en un número en el intervalo que desea, con una mayor peso en un número específico. Podrías crear una función con funciones trigonométricas (sin, cos, ...), exponencial o tal vez un polinomio.

ACTUALIZACIÓN: Tener un vistazo a this page para obtener más información sobre la distribución de probabilidad

+1

WTF es un polinomio? Me parece que fuiste por el botón "Publicar tu respuesta" demasiado rápido – paxdiablo

+3

un polinom es una función algebraica escrita en la forma: f (x) = an * x^n + (an-1) * x^(x-1) + ... a2 * x^2 + a1 * x + a0; –

+0

No, eso sería un * polinomio *. Veo que nadie aprecia mi humor (a menudo extraño, según ella, que debe ser obedecido). Creo que me iré a la cama. – paxdiablo

29

Necesita ponderar los resultados. Usted puede hacer eso con algo como esto:

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)]; 
} 

Si sabía que mi rango era pequeña y consistente, que haría uso de la mesa - es trivial para que sea su propia clase.

Para completar, también agregaré esta clase. Esta clase toma prestado del procesamiento de imágenes y utiliza la función de corrección gamma: un valor entre 0 y 1 elevado a gamma, que devuelve un valor entre 0 y 1 pero distribuye más al extremo bajo si gamma < 1.0 y más al extremo superior si 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; 
    } 
} 

(a partir de los comentarios, se trasladó fuera de rango r GetWeightedRandom(). También se ha añadido la comprobación a continuación())

OK, vamos a ir a la ciudad realmente aquí. Estoy canalizando John skeet para esto - es una clase abstracta con una propiedad de plantilla que devuelve una función de transformación que mapea el rango [0..1) a [0..1] y escala el número aleatorio a ese rango. También reimplementé gamma en términos de esto e implementé el pecado y el cos también.

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, me ganaste con esta respuesta. +1. –

+3

Esto está bien para números relativamente pequeños, pero si quiere una distribución centrada en 1000000, necesita una matriz realmente grande ... Creo que sería mejor usar una función de distribución. –

+0

8 fue un ejemplo, de hecho, tenemos: int profundo en lugar de 10, y nada en lugar de 8 (me refiero a estar cerca de 8, 7 o 9) –

0

Me parece que usted desea que sus números aleatorios sean ponderados hacia el extremo superior. ¿Sería esta una evaluación justa?

Algo así como this puede ayudar a usted (es Java, pero se aplican los principios)

+0

No, no es justo, pero es importante. –

2

En lugar de utilizar la variante de serie, también se puede echar un vistazo a este SO answer que tiene un enlace a Math.NET Iridium que implementa generadores aleatorios no uniformes .

Las ventajas de la variante de matriz son que obtiene un enfoque más dinámico sin tener que volver a escribir la matriz todo el tiempo. También podría hacer algunas cosas que serían prácticamente imposibles con la variante de matriz (grandes números aleatorios no uniformes).

1

Con algún tipo de ponderación adicional que debería ser posible. Depende de cómo especifique "cerca de ocho". Una forma muy sencilla de hacerlo es la siguiente:

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

La cuadratura tendrá un peso de los números hacia el extremo inferior, es decir,en este caso, el 33% del tiempo obtendrá un 0, mientras que obtendrá un 9 solo alrededor del 5% del tiempo.

Este método, por supuesto, se adapta al caso particular.

1

No es exactamente lo que está buscando, pero una manera muy simple de aproximar una distribución normal de números es mediante la suma de múltiples generaciones juntas.

Un ejemplo clásico de esta técnica es en el juego Dungeons and Dragons donde la fuerza de los personajes se puede determinar tirando tres dados de seis caras y sumando los resultados. Esto da un rango de 3 a 18 con números alrededor de 10, lo más probable. Las variantes incluyen:

  • Rodando 4 dados y descartando el más bajo. Esto sesga la distribución hacia números más altos.
  • Promediando los puntajes en vez de agregarlos. Esto hace que el rango de salida sea más fácil de entender.

Alternativamente, this es bastante cerca ...