Pregunta

Me gustaría tener un número aleatorio como este: (en C #)

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

PERO es importante que el número aleatorio esté más cerca de 8, (o suele ser grande), Quiero decir, si usamos un para:

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

el resultado sea 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
¿Fue útil?

Solución

Necesita una función de distribución que tome un número entre 0 y 1 y lo convierta en un número en el rango que desee, con un mayor peso en un número específico. Podría crear dicha función con funciones trigonométricas (sin, cos, ...), exponenciales, o tal vez un polinomio.

ACTUALIZACIÓN: Consulte esta página para obtener más información sobre la distribución de probabilidad

Otros consejos

Necesitas ponderar tus resultados. Puedes hacerlo 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 supiera que mi rango es pequeño y consistente, usaría la tabla; es trivial hacer 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 se distribuye más a el extremo bajo si gamma < 1.0 y más al extremo superior si gamma & Gt; 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;
    }
}

(de los comentarios, se movió r fuera de GetWeightedRandom (). También se agregó la comprobación de rango a Siguiente ())

OK, vamos a la ciudad 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 asigna el rango [0..1) a [0..1) y escala el número aleatorio a ese rango. También reimplementé gamma en términos de eso e implementé sin y 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); }
    }
}

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

Las ventajas de la variante de matriz son que obtienes un enfoque más dinámico sin tener que reescribir 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).

Con algún tipo de ponderación adicional que debería ser posible. Depende de cómo especifique & Quot; cerca de ocho & Quot ;. Una forma muy simple de hacerlo es esto:

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

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

Este método, por supuesto, debe adaptarse para adaptarse al caso particular.

No es exactamente lo que está buscando, pero una forma muy simple de aproximar una distribución normal de números es agregar varias 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 lados y agregando los resultados. Esto da un rango de 3 a 18 con números alrededor del 10 más probable. Las variantes incluyen:

  • Tira 4 dados y descarta el más bajo. Esto sesga la distribución hacia números más altos.
  • Promediar los puntajes en lugar de sumarlos. Esto hace que el rango de salida sea más fácil de entender.

Alternativamente, esto está bastante cerca ...

Me parece que quiere que sus números aleatorios sean ponderados hacia el extremo superior, ¿sería una evaluación justa?

Algo como esto puede ayudarlo (es Java, pero se aplican los principios)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top