Поколение (псевдо) случайных ограниченных значений (U) Int64 и Десятичное число

Примечание: Для пользы краткости следующее не различит между случайностью и псевдослучайностью. Кроме того, в этом контексте, ограниченных средствах между данной минутой и макс. значениями)

System.Random класс обеспечивает случайное поколение целых чисел, удваивается и массивы байтов. Использование Случайного. Затем, можно легко генерировать случайные ограниченные значения булевской переменной типа, Символа, (S) Байт, (U) Int16, (U) Int32. Используя Random.NextDouble(), можно так же генерировать ограниченные значения типов, Двойных и Единственных (насколько мое понимание этого типа идет). Поколение случайной строки (данной длины и алфавита) имеет также занятый прежде.

Рассмотрите остающиеся примитивные типы данных (исключая Объект): Десятичное число и (U) Int64. Их случайным поколением занялись также (Десятичное число, (U) Int64 использование Random.NextBytes()), но не при ограничении. Выборка отклонения (т.е. цикличное выполнение, пока сгенерированное значение не является желаемым диапазоном) могла теоретически использоваться, но это - очевидно, не практическое решение. Нормализация NextDouble() не будет работать, потому что это не имеет достаточного количества значащих цифр.

Короче говоря, я прошу надлежащую реализацию следующих функций:

long NextLong(long min, long max)
long NextDecimal(decimal min, decimal max)

Отметьте это с тех пор System.DateTime основан на ulong, первая функция допускала бы случайное ограниченное поколение таких структур также (подобный здесь, только в галочках вместо минут).

5
задан Ohad Schneider 27 November 2017 в 16:48
поделиться

3 ответа

[

] Предположим, вы знаете, как генерировать N случайных битов. Это довольно легко сделать либо с помощью []NextBytes[], либо повторными вызовами на []Random.Next[] с соответствующими ограничениями.[

]. [

] Чтобы сгенерировать длинный/длинный в нужном диапазоне, выясните, насколько велик диапазон и сколько битов требуется для его представления. Затем можно использовать отвергающую выборку, которая в худшем случае [] [] отвергнет половину сгенерированных значений (например, если вам нужно значение в диапазоне [0, 128], что означает, что вы сгенерируете [0, 255] несколько раз). Если вам нужен ненулевой диапазон, просто определите размер диапазона, сгенерируйте случайное значение в [0, размер], а затем добавьте базу.[

]. [

] Генерировать случайный десятичный знак значительно сложнее, я полагаю - кроме всего прочего, вам пришлось бы указать нужное вам распределение.[

]
6
ответ дан 18 December 2019 в 14:46
поделиться

Это должно сработать. Для десятичной запятой я использовал начальный подход Джона Скита к генерации случайных десятичных с (без ограничений). Для long я предоставил метод генерации случайных неотрицательных long s, который затем используется для создания значения в случайном диапазоне.

Обратите внимание, что для decimal результирующее распределение не является однородным на [minValue, maxValue]. Оно просто равномерно на всех битовых представлениях десятичных дробей, которые попадают в диапазон [minValue, maxValue]. Я не вижу простого способа обойти это без использования отвергающей выборки.

Для long результирующее распределение равномерно на [minValue, maxValue).

static class RandomExtensions {
    static int NextInt32(this Random rg) {
        unchecked {
            int firstBits = rg.Next(0, 1 << 4) << 28;
            int lastBits = rg.Next(0, 1 << 28);
            return firstBits | lastBits;
        }
    }

    public static decimal NextDecimal(this Random rg) {
        bool sign = rg.Next(2) == 1;
        return rg.NextDecimal(sign);
    }

    static decimal NextDecimal(this Random rg, bool sign) {
        byte scale = (byte)rg.Next(29);
        return new decimal(rg.NextInt32(),
                           rg.NextInt32(),
                           rg.NextInt32(),
                           sign,
                           scale);
    }

    static decimal NextNonNegativeDecimal(this Random rg) {
        return rg.NextDecimal(false);
    }

    public static decimal NextDecimal(this Random rg, decimal maxValue) {
        return (rg.NextNonNegativeDecimal() / Decimal.MaxValue) * maxValue; ;
    }

    public static decimal NextDecimal(this Random rg, decimal minValue, decimal maxValue) {
        if (minValue >= maxValue) {
            throw new InvalidOperationException();
        }
        decimal range = maxValue - minValue;
        return rg.NextDecimal(range) + minValue;
    }

    static long NextNonNegativeLong(this Random rg) {
        byte[] bytes = new byte[sizeof(long)];
        rg.NextBytes(bytes);
        // strip out the sign bit
        bytes[7] = (byte)(bytes[7] & 0x7f);
        return BitConverter.ToInt64(bytes, 0);
    }

    public static long NextLong(this Random rg, long maxValue) {
        return (long)((rg.NextNonNegativeLong() / (double)Int64.MaxValue) * maxValue);
    }

    public static long NextLong(this Random rg, long minValue, long maxValue) {
        if (minValue >= maxValue) {
            throw new InvalidOperationException();
        }
        long range = maxValue - minValue;
        return rg.NextLong(range) + minValue;
    }
}
9
ответ дан 18 December 2019 в 14:46
поделиться

На основе Джона Вот мой метод Скита:

public static long NextLong(this Random rnd, long min, long max) 
{
    if (max <= min) 
    {
        throw new Exception("Min must be less than max.");
    }

    long dif = max - min;

    var bytes = new byte[8];
    rnd.NextBytes(bytes);
    bytes[7] &= 0x7f; //strip sign bit

    long posNum = BitConverter.ToInt64(bytes, 0);
    while (posNum > dif)
    {
        posNum >>= 1;
    }

    return min + posNum;
}

Дайте мне знать, если вы заметите какие-либо ошибки.

0
ответ дан 18 December 2019 в 14:46
поделиться
Другие вопросы по тегам:

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