Número aleatório entre 2 números duplos

156

É possível gerar um número aleatório entre 2 duplos?

Exemplo:

public double GetRandomeNumber(double minimum, double maximum)
{
    return Random.NextDouble(minimum, maximum) 
}

Então eu chamo com o seguinte:

double result = GetRandomNumber(1.23, 5.34);

Qualquer pensamento seria apreciado.

CodeLikeBeaker
fonte

Respostas:

329

Sim.

Random.NextDouble retorna um duplo entre 0 e 1. Você multiplica o valor pelo intervalo que você precisa (diferença entre o máximo e o mínimo) e depois o adiciona à base (mínimo).

public double GetRandomNumber(double minimum, double maximum)
{ 
    Random random = new Random();
    return random.NextDouble() * (maximum - minimum) + minimum;
}

O código real deve ter aleatoriamente um membro estático. Isso economizará o custo de criação do gerador de números aleatórios e permitirá que você chame GetRandomNumber com muita frequência. Como estamos inicializando um novo RNG a cada chamada, se você ligar rápido o suficiente para que o horário do sistema não mude entre as chamadas, o RNG será propagado com exatamente o mesmo registro de data e hora e gerará o mesmo fluxo de números aleatórios.

Michael
fonte
33
Apenas tome cuidado se você chamar GetRandomNumber () em um loop, pois ele gerará o mesmo valor repetidamente
John Rasch
13
Poderia ser um bom método extensão,public double GetRandomNumber(this Random random, double minimum, double maximum) {...}
Johnny5
5
Lembre-se de que, como Random.NextDouble nunca retorna 1.0, essa função também nunca será igual ao número máximo.
Matthew Matthew
6
Sugiro colocar como nova semente Random ((int) DateTime.Now.Ticks), isso deve torná-lo "mais aleatório", mesmo em loops rápidos.
Daniel Skowroński
5
Isso não funciona se o valor mínimo for double.MinValue e o valor máximo for double.MaxValue. Alguma sugestão sobre como lidar com este caso de uso?
Gene S
40

Johnny5 sugeriu a criação de um método de extensão. Aqui está um exemplo de código mais completo, mostrando como você pode fazer isso:

public static class RandomExtensions
{
    public static double NextDouble(
        this Random random,
        double minValue,
        double maxValue)
    {
        return random.NextDouble() * (maxValue - minValue) + minValue;
    }
}

Agora você pode chamá-lo como se fosse um método na Randomclasse:

Random random = new Random();
double value = random.NextDouble(1.23, 5.34);

Observe que você não deve criar muitos novos Randomobjetos em um loop porque isso tornará provável que você obtenha o mesmo valor várias vezes seguidas. Se você precisar de muitos números aleatórios, crie uma instância Randome reutilize-a.

Mark Byers
fonte
9

Cuidado: se você estiver gerando randomum loop interno como, por exemplo for(int i = 0; i < 10; i++), não coloque a new Random()declaração dentro do loop.

Do MSDN :

A geração do número aleatório começa a partir de um valor inicial. Se a mesma semente for usada repetidamente, a mesma série de números será gerada. Uma maneira de produzir sequências diferentes é tornar o valor da semente dependente do tempo, produzindo assim uma série diferente a cada nova instância do Random. Por padrão, o construtor sem parâmetros da classe Random usa o relógio do sistema para gerar seu valor inicial ...

Portanto, com base nesse fato, faça algo como:

var random = new Random();

for(int d = 0; d < 7; d++)
{
    // Actual BOE
    boes.Add(new LogBOEViewModel()
    {
        LogDate = criteriaDate,
        BOEActual = GetRandomDouble(random, 100, 1000),
        BOEForecast = GetRandomDouble(random, 100, 1000)
    });
}

double GetRandomDouble(Random random, double min, double max)
{
     return min + (random.NextDouble() * (max - min));
}

Desta forma, você tem a garantia de obter valores duplos diferentes.

Leniel Maccaferri
fonte
8

A abordagem mais simples simplesmente geraria um número aleatório entre 0 e a diferença dos dois números. Em seguida, adicione o menor dos dois números ao resultado.

Greg D
fonte
3

Você pode usar código como este:

public double getRandomNumber(double minimum, double maximum) {
    return minimum + randomizer.nextDouble() * (maximum - minimum);
}
Malcolm
fonte
2

Você pode fazer isso:

public class RandomNumbers : Random
{
    public RandomNumbers(int seed) : base(seed) { }

    public double NextDouble(double minimum, double maximum)
    {
        return base.NextDouble() * (maximum - minimum) + minimum;
    }
}
user490775
fonte
2

Estou um pouco atrasado para a festa, mas eu precisava implementar uma solução geral e verificou-se que nenhuma das soluções pode satisfazer minhas necessidades.

A solução aceita é boa para pequenos intervalos; no entanto, maximum - minimumpode ser infinito para grandes intervalos. Portanto, uma versão corrigida pode ser esta versão:

public static double NextDoubleLinear(this Random random, double minValue, double maxValue)
{
    // TODO: some validation here...
    double sample = random.NextDouble();
    return (maxValue * sample) + (minValue * (1d - sample));
}

Isso gera números aleatórios bem entre double.MinValuee double.MaxValue. Mas isso introduz outro "problema", que é bem apresentado neste post : se usarmos intervalos tão grandes, os valores podem parecer "pouco naturais". Por exemplo, após gerar 10.000 dobras aleatórias entre 0 edouble.MaxValue todos os valores estavam entre 2,9579E + 304 e 1,7976E + 308.

Então, eu criei também outra versão, que gera números em uma escala logarítmica:

public static double NextDoubleLogarithmic(this Random random, double minValue, double maxValue)
{
    // TODO: some validation here...
    bool posAndNeg = minValue < 0d && maxValue > 0d;
    double minAbs = Math.Min(Math.Abs(minValue), Math.Abs(maxValue));
    double maxAbs = Math.Max(Math.Abs(minValue), Math.Abs(maxValue));

    int sign;
    if (!posAndNeg)
        sign = minValue < 0d ? -1 : 1;
    else
    {
        // if both negative and positive results are expected we select the sign based on the size of the ranges
        double sample = random.NextDouble();
        var rate = minAbs / maxAbs;
        var absMinValue = Math.Abs(minValue);
        bool isNeg = absMinValue <= maxValue ? rate / 2d > sample : rate / 2d < sample;
        sign = isNeg ? -1 : 1;

        // now adjusting the limits for 0..[selected range]
        minAbs = 0d;
        maxAbs = isNeg ? absMinValue : Math.Abs(maxValue);
    }

    // Possible double exponents are -1022..1023 but we don't generate too small exponents for big ranges because
    // that would cause too many almost zero results, which are much smaller than the original NextDouble values.
    double minExponent = minAbs == 0d ? -16d : Math.Log(minAbs, 2d);
    double maxExponent = Math.Log(maxAbs, 2d);
    if (minExponent == maxExponent)
        return minValue;

    // We decrease exponents only if the given range is already small. Even lower than -1022 is no problem, the result may be 0
    if (maxExponent < minExponent)
        minExponent = maxExponent - 4;

    double result = sign * Math.Pow(2d, NextDoubleLinear(random, minExponent, maxExponent));

    // protecting ourselves against inaccurate calculations; however, in practice result is always in range.
    return result < minValue ? minValue : (result > maxValue ? maxValue : result);
}

Alguns testes:

Aqui estão os resultados classificados da geração de 10.000 números duplos aleatórios entre 0 e Double.MaxValuecom as duas estratégias. Os resultados são exibidos com o uso da escala logarítmica:

0..Double.MaxValue

Embora os valores aleatórios lineares pareçam estar errados à primeira vista, as estatísticas mostram que nenhum deles é "melhor" que o outro: mesmo a estratégia linear tem uma distribuição uniforme e a diferença média entre os valores é praticamente a mesma em ambas as estratégias .

Brincar com intervalos diferentes me mostrou que a estratégia linear fica "sã" com intervalo entre 0 e ushort.MaxValuecom um valor mínimo "razoável" de 10,78294704 (para o ulongintervalo, o valor mínimo era 3,03518E + 15 int;: 353341). Estes são os mesmos resultados de ambas as estratégias exibidas em escalas diferentes:

0..UInt16.MaxValue


Editar:

Recentemente, criei minhas bibliotecas de código aberto, fique à vontade para ver o RandomExtensions.NextDoublemétodo com a validação completa.

György Kőszeg
fonte
1

E se um dos valores for negativo? Não seria uma idéia melhor:

double NextDouble(double min, double max)
{
       if (min >= max)
            throw new ArgumentOutOfRangeException();    
       return random.NextDouble() * (Math.Abs(max-min)) + min;
}
Chris Susie
fonte
5
Eu acho que Math.Abs()é redundante. Como você garantiu isso min >= max, max - mindeve ser um número não negativo de qualquer maneira.
18713 Brian Brian'sch
1

Se você precisar de um número aleatório no intervalo [ double.MinValue; double.MaxValue]

// Because of:
double.MaxValue - double.MinValue == double.PositiveInfinity

// This will be equals to NaN or PositiveInfinity
random.NextDouble() * (double.MaxValue - double.MinValue)

Use em vez disso:

public static class RandomExtensions
{
    public static double NextDoubleInMinMaxRange(this Random random)
    {
        var bytes = new byte[sizeof(double)];
        var value = default(double);
        while (true)
        {
            random.NextBytes(bytes);
            value = BitConverter.ToDouble(bytes, 0);
            if (!double.IsNaN(value) && !double.IsInfinity(value))
                return value;
        }
    }
}
alex
fonte
2
Bom ponto, mas esta proposição resulta em distribuição assimétrica, como no segundo exemplo aqui stackoverflow.com/a/3365388/3922292
Piotr Falkowski
0

Sobre a geração do mesmo número aleatório, se você o chamar em um loop, uma solução bacana é declarar o novo objeto Random () fora do loop como uma variável global.

Observe que você deve declarar sua instância da classe Random fora da função GetRandomInt se você estiver executando isso em um loop.

"Por que é isso?" você pergunta.

Bem, a classe Random na verdade gera números pseudo-aleatórios, com a "semente" para o randomizador sendo a hora do sistema. Se o seu loop for suficientemente rápido, a hora do relógio do sistema não parecerá diferente do randomizador e cada nova instância da classe Random começará com a mesma semente e fornecerá o mesmo número pseudo-aleatório.

A fonte está aqui: http://www.whypad.com/posts/csharp-get-a-random-number-between-x-and-y/412/

Ajibola
fonte
Isso é mais adequado como um comentário, pois nem sequer tenta responder à pergunta real do OP.
precisa saber é o seguinte
0

Use um Random estático ou os números tendem a se repetir em loops apertados / rápidos devido ao relógio do sistema semeando-os.

public static class RandomNumbers
{
    private static Random random = new Random();
    //=-------------------------------------------------------------------
    // double between min and the max number
    public static double RandomDouble(int min, int max) 
    {
        return (random.NextDouble() * (max - min)) + min;
    }
    //=----------------------------------
    // double between 0 and the max number
    public static double RandomDouble(int max) 
    {
        return (random.NextDouble() * max);
    }
    //=-------------------------------------------------------------------
    // int between the min and the max number
    public static int RandomInt(int min, int max) 
    {   
        return random.Next(min, max + 1);
    }
    //=----------------------------------
    // int between 0 and the max number
    public static int RandomInt(int max) 
    {
        return random.Next(max + 1);
    }
    //=-------------------------------------------------------------------
 }

Consulte também: https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8

David C Fuchs
fonte
-1
Random random = new Random();

double NextDouble(double minimum, double maximum)
{  

    return random.NextDouble()*random.Next(minimum,maximum);

}
Mitesh Savani
fonte