Случайное число на большом расстоянии, это так? - PullRequest
41 голосов
/ 11 июля 2011

Может кто-нибудь проверить этот метод.Мне нужно длинное число типа в диапазоне двух длинных.Я использую функцию .NET Random.Next (min, max), которая возвращает int.Верны ли мои рассуждения, если я просто разделю длинное на 2, сгенерирую случайное число и, наконец, умножу его на 2?Или я слишком полон энтузиазма ... Я понимаю, что мое случайное разрешение уменьшится, но есть ли другие ошибки, которые не приведут к такому случайному числу.

Ответы [ 13 ]

76 голосов
/ 11 июля 2011

Почему бы вам просто не сгенерировать два случайных Int32 значения и не сделать из них одно Int64?

long LongRandom(long min, long max, Random rand) {
    long result = rand.Next((Int32)(min >> 32), (Int32)(max >> 32));
    result = (result << 32);
    result = result | (long)rand.Next((Int32)min, (Int32)max);
    return result;
}

Извините, я забыл добавить границы в первый раз.Добавлены параметры min и max.Вы можете проверить это так:

long r = LongRandom(100000000000000000, 100000000000000050, new Random());

Значения r будут лежать в желаемом диапазоне.

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

long LongRandom(long min, long max, Random rand) {
    byte[] buf = new byte[8];
    rand.NextBytes(buf);
    long longRand = BitConverter.ToInt64(buf, 0);

    return (Math.Abs(longRand % (max - min)) + min);
}

Это выглядит довольно хорошо с точки зрения распределения значений (судя по очень простым тестам, которые я провел).

26 голосов
/ 11 июля 2011

Это создает случайный Int64 с использованием случайных байтов, избегая смещения по модулю путем повторной попытки, если число находится за пределами безопасного диапазона.

static class RandomExtensions
{
   public static long RandomLong(this Random rnd)
   {
      byte[] buffer = new byte[8];
      rnd.NextBytes (buffer);
      return BitConverter.ToInt64(buffer, 0);
   }

   public static long RandomLong(this Random rnd, long min, long max)
   {
      EnsureMinLEQMax(ref min, ref max);
      long numbersInRange = unchecked(max - min + 1);
      if (numbersInRange < 0)
         throw new ArgumentException("Size of range between min and max must be less than or equal to Int64.MaxValue");

      long randomOffset = RandomLong(rnd);
      if (IsModuloBiased(randomOffset, numbersInRange))
         return RandomLong(rnd, min, max); // Try again
      else
         return min + PositiveModuloOrZero(randomOffset, numbersInRange);
   }

   static bool IsModuloBiased(long randomOffset, long numbersInRange)
   {
      long greatestCompleteRange = numbersInRange * (long.MaxValue / numbersInRange);
      return randomOffset > greatestCompleteRange;
   }

   static long PositiveModuloOrZero(long dividend, long divisor)
   {
      long mod;
      Math.DivRem(dividend, divisor, out mod);
      if(mod < 0)
         mod += divisor;
      return mod;
   }

   static void EnsureMinLEQMax(ref long min, ref long max)
   {
      if(min <= max)
         return;
      long temp = min;
      min = max;
      max = temp;
   }
}
24 голосов
/ 27 октября 2012

Некоторые другие ответы здесь имеют две проблемы: наличие смещения по модулю и невозможность правильно обработать значения max = long.MaxValue. ( Ответ Мартина не имеет проблем, но его код неоправданно медленен с большими диапазонами.)

Следующий код исправит все эти проблемы:

//Working with ulong so that modulo works correctly with values > long.MaxValue
ulong uRange = (ulong)(max - min);

//Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
//for more information.
//In the worst case, the expected number of calls is 2 (though usually it's
//much closer to 1) so this loop doesn't really hurt performance at all.
ulong ulongRand;
do
{
    byte[] buf = new byte[8];
    random.NextBytes(buf);
    ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
} while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);

return (long)(ulongRand % uRange) + min;

Следующий полностью документированный класс может быть добавлен в вашу кодовую базу для простой и простой реализации этого решения. Как и весь код в Stackoverflow, он лицензируется под CC-атрибуцией, так что вы можете свободно использовать его для того, что вы хотите.

using System;

namespace MyNamespace
{
    public static class RandomExtensionMethods
    {
        /// <summary>
        /// Returns a random long from min (inclusive) to max (exclusive)
        /// </summary>
        /// <param name="random">The given random instance</param>
        /// <param name="min">The inclusive minimum bound</param>
        /// <param name="max">The exclusive maximum bound.  Must be greater than min</param>
        public static long NextLong(this Random random, long min, long max)
        {
            if (max <= min)
                throw new ArgumentOutOfRangeException("max", "max must be > min!");

            //Working with ulong so that modulo works correctly with values > long.MaxValue
            ulong uRange = (ulong)(max - min);

            //Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
            //for more information.
            //In the worst case, the expected number of calls is 2 (though usually it's
            //much closer to 1) so this loop doesn't really hurt performance at all.
            ulong ulongRand;
            do
            {
                byte[] buf = new byte[8];
                random.NextBytes(buf);
                ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
            } while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);

            return (long)(ulongRand % uRange) + min;
        }

        /// <summary>
        /// Returns a random long from 0 (inclusive) to max (exclusive)
        /// </summary>
        /// <param name="random">The given random instance</param>
        /// <param name="max">The exclusive maximum bound.  Must be greater than 0</param>
        public static long NextLong(this Random random, long max)
        {
            return random.NextLong(0, max);
        }

        /// <summary>
        /// Returns a random long over all possible values of long (except long.MaxValue, similar to
        /// random.Next())
        /// </summary>
        /// <param name="random">The given random instance</param>
        public static long NextLong(this Random random)
        {
            return random.NextLong(long.MinValue, long.MaxValue);
        }
    }
}

Использование:

Random random = new Random();
long foobar = random.NextLong(0, 1234567890L);
7 голосов
/ 25 июля 2012

Вот решение, которое использует другие ответы, используя Random.NextBytes, но также уделяет пристальное внимание граничным случаям.Я структурировал это как набор методов расширения.Кроме того, я учел смещение по модулю, выбрав другое случайное число, которое выходит за пределы диапазона.

Одна из моих проблем (по крайней мере, для ситуации, в которой я пытался его использовать) заключается в том, что максимум обычно является исключительным, поэтому, если вы хотите бросить кубик, вы делаете что-то вроде Random.Next(0,7).Однако это означает, что вы никогда не сможете получить эту перегрузку, чтобы вернуть .MaxValue для типа данных (int, long, ulong, что у вас есть).Поэтому я добавил флаг inclusiveUpperBound для переключения этого поведения.

public static class Extensions
{
    //returns a uniformly random ulong between ulong.Min inclusive and ulong.Max inclusive
    public static ulong NextULong(this Random rng)
    {
        byte[] buf = new byte[8];
        rng.NextBytes(buf);
        return BitConverter.ToUInt64(buf, 0);
    }

    //returns a uniformly random ulong between ulong.Min and Max without modulo bias
    public static ulong NextULong(this Random rng, ulong max, bool inclusiveUpperBound = false)
    {
        return rng.NextULong(ulong.MinValue, max, inclusiveUpperBound);
    }

    //returns a uniformly random ulong between Min and Max without modulo bias
    public static ulong NextULong(this Random rng, ulong min, ulong max, bool inclusiveUpperBound = false)
    {
        ulong range = max - min;

        if (inclusiveUpperBound)
        {   
            if (range == ulong.MaxValue)
            {
                return rng.NextULong();
            }

            range++;
        }

        if (range <= 0)
        {
            throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
        }

        ulong limit = ulong.MaxValue - ulong.MaxValue % range;
        ulong r;
        do
        {
            r = rng.NextULong();
        } while(r > limit);

        return r % range + min;
    }

    //returns a uniformly random long between long.Min inclusive and long.Max inclusive
    public static long NextLong(this Random rng)
    {
        byte[] buf = new byte[8];
        rng.NextBytes(buf);
        return BitConverter.ToInt64(buf, 0);
    }

    //returns a uniformly random long between long.Min and Max without modulo bias
    public static long NextLong(this Random rng, long max, bool inclusiveUpperBound = false)
    {
        return rng.NextLong(long.MinValue, max, inclusiveUpperBound);
    }

    //returns a uniformly random long between Min and Max without modulo bias
    public static long NextLong(this Random rng, long min, long max, bool inclusiveUpperBound = false)
    {
        ulong range = (ulong)(max - min);

        if (inclusiveUpperBound)
        {   
            if (range == ulong.MaxValue)
            {
                return rng.NextLong();
            }

            range++;
        }

        if (range <= 0)
        {
            throw new ArgumentOutOfRangeException("Max must be greater than min when inclusiveUpperBound is false, and greater than or equal to when true", "max");
        }

        ulong limit = ulong.MaxValue - ulong.MaxValue % range;
        ulong r;
        do
        {
            r = rng.NextULong();
        } while(r > limit);
        return (long)(r % range + (ulong)min);
    }
}
2 голосов
/ 11 июля 2011

Ваш randomLong всегда будет четным, и вы исключите еще больше значений, потому что вы очень далеки от максимума для long. Максимум для long составляет 2 ^ 32 * max для int. Вы должны использовать Random.NextBytes.

1 голос
/ 17 июля 2015

Вы можете попробовать CryptoRandom из библиотеки Inferno :

public class CryptoRandom : Random
    // implements all Random methods, as well as:

    public byte[] NextBytes(int count)
    public long NextLong()
    public long NextLong(long maxValue)
    public long NextLong(long minValue, long maxValue)
1 голос
/ 11 июля 2011

Начните с минимума, добавьте случайный процент от разницы между минимальным и максимальным. Проблема в том, что NextDouble возвращает число x такое, что 0 <= x <1, поэтому есть вероятность, что вы никогда не достигнете максимального числа. </p>

long randomLong = min + (long)(random.NextDouble() * (max - min));
0 голосов
/ 01 мая 2017

Как насчет генерации байтов и преобразования в int64?

/* generate a byte array, then convert to unint64 */
var r = new Random(); // DONT do this for each call - use a static Random somewhere
var barray = new byte[64/8];
r.NextBytes(barray);
var rint64 = BitConverter.ToUInt64(barray, 0);

Видит, чтобы работать на меня (:

0 голосов
/ 27 октября 2016

Что не так с генерацией double, предназначенной для использования в качестве фактора для вычисления фактического значения long, начиная с максимального значения, которое может быть long?!

long result = (long)Math.Round( random.NextDouble() * maxLongValue );
  • NextDouble генерирует случайное число между [0.0, 0.99999999999999978] ( msdn doc )

  • Вы умножаете это случайное число на ваше maxLongValue.

  • Вы Math.Round этот результат, так что вы можете получить шанс получить maxLongValue в любом случае (например: имитировать, что вы получили 1,0 от NextDouble).

  • Вы возвращаетесь к long.
0 голосов
/ 17 марта 2016
private long randomLong()
{
    Random random = new Random();
    byte[] bytes = new byte[8];
    _random.NextBytes(bytes);
    return BitConverter.ToInt64(bytes, 0);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...