Лучший способ генерировать случайное плавание в [закрытом] C#

51
задан mskfisher 9 May 2012 в 05:28
поделиться

3 ответа

Наилучший подход, без сумасшедших значений, распределен относительно представимых интервалов на числовой строке с плавающей запятой (удален «однородный», поскольку относительно непрерывной числовой строки он явно неоднороден) :

static float NextFloat(Random random)
{
    double mantissa = (random.NextDouble() * 2.0) - 1.0;
    // choose -149 instead of -126 to also generate subnormal floats (*)
    double exponent = Math.Pow(2.0, random.Next(-126, 128));
    return (float)(mantissa * exponent);
}

(*) ... проверьте здесь на наличие субнормальных чисел с плавающей запятой

Предупреждение: также генерирует положительную бесконечность! На всякий случай выберите показатель степени 127.

Другой подход, который даст вам некоторые сумасшедшие значения (равномерное распределение битовых шаблонов), потенциально полезный для фаззинга:

static float NextFloat(Random random)
{
    var buffer = new byte[4];
    random.NextBytes(buffer);
    return BitConverter.ToSingle(buffer,0);
}

Улучшение по сравнению с предыдущей версией заключается в том, что он не создает «сумасшедшие» значения (ни бесконечности, ни NaN) и по-прежнему работает быстро (также распределяется относительно представимых интервалов в строке с плавающей запятой):

public static float Generate(Random prng)
{
    var sign = prng.Next(2);
    var exponent = prng.Next((1 << 8) - 1); // do not generate 0xFF (infinities and NaN)
    var mantissa = prng.Next(1 << 23);

    var bits = (sign << 31) + (exponent << 23) + mantissa;
    return IntBitsToFloat(bits);
}

private static float IntBitsToFloat(int bits)
{
    unsafe
    {
        return *(float*) &bits;
    }
}

Наименее полезный подход:

static float NextFloat(Random random)
{
    // Not a uniform distribution w.r.t. the binary floating-point number line
    // which makes sense given that NextDouble is uniform from 0.0 to 1.0.
    // Uniform w.r.t. a continuous number line.
    //
    // The range produced by this method is 6.8e38.
    //
    // Therefore if NextDouble produces values in the range of 0.0 to 0.1
    // 10% of the time, we will only produce numbers less than 1e38 about
    // 10% of the time, which does not make sense.
    var result = (random.NextDouble()
                  * (Single.MaxValue - (double)Single.MinValue))
                  + Single.MinValue;
    return (float)result;
}

Строка с плавающей запятой из: Intel Architecture Software Developer's Manual Volume 1 : Базовая архитектура. Ось Y является логарифмической (основание 2), поскольку последовательные двоичные числа с плавающей запятой не отличаются линейно.

Comparison of distributions, logarithmic Y-axis

63
ответ дан 7 November 2019 в 10:07
поделиться

Есть ли причина не использовать Random.NextDouble , а затем преобразовать его в float ? Это даст вам число с плавающей запятой от 0 до 1.

Если вам нужна другая форма «лучшего», вам нужно указать свои требования. Обратите внимание, что Random не следует использовать для конфиденциальных вопросов, таких как финансы или безопасность - и вам обычно следует повторно использовать существующий экземпляр во всем приложении или по одному на поток (как Random isn ' t потокобезопасный).

РЕДАКТИРОВАТЬ: Как было предложено в комментариях, чтобы преобразовать это в диапазон float.MinValue , float.MaxValue :

// Perform arithmetic in double type to avoid overflowing
double range = (double) float.MaxValue - (double) float.MinValue;
double sample = rng.NextDouble();
double scaled = (sample * range) + float.MinValue;
float f = (float) scaled;

РЕДАКТИРОВАТЬ: Теперь вы упомянули, что это для модульного тестирования я не уверен, что это идеальный подход. Вместо этого вам, вероятно, следует протестировать с конкретными значениями - убедитесь, что вы тестируете образцы в каждой из соответствующих категорий - бесконечности, NaN, денормальные числа, очень большие числа, ноль и т. Д.

26
ответ дан 7 November 2019 в 10:07
поделиться

Я использовал несколько иной подход, чем другие

static float NextFloat(Random random)
{
    double val = random.NextDouble(); // range 0.0 to 1.0
    val -= 0.5; // expected range now -0.5 to +0.5
    val *= 2; // expected range now -1.0 to +1.0
    return float.MaxValue * (float)val;
}

Комментарии объясняют, что я делаю. Получите следующее значение double, преобразуйте это число в значение от -1 до 1, а затем умножьте его на float.MaxValue .

3
ответ дан 7 November 2019 в 10:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: