Генератор случайных чисел, генерирующий только одно случайное число - PullRequest
721 голосов
/ 20 апреля 2009

У меня есть следующая функция:

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}

Как я это называю:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);

Если я выполняю этот цикл с отладчиком во время выполнения, я получаю разные значения (что я и хочу). Однако если я поставлю точку останова на две строки ниже этого кода, все члены массива "mac" будут иметь одинаковое значение.

Почему это происходит?

Ответы [ 9 ]

984 голосов
/ 20 апреля 2009

Каждый раз, когда вы делаете new Random(), он инициализируется с помощью часов. Это означает, что в тесном цикле вы получаете одно и то же значение много раз. Вы должны хранить один Random экземпляр и продолжать использовать Next на том же экземпляре.

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}

Редактировать (см. Комментарии): зачем нам lock здесь?

По сути, Next собирается изменить внутреннее состояние экземпляра Random. Если мы сделаем это одновременно из нескольких потоков, вы могли бы утверждать, что "мы только что сделали результат еще более случайным", но то, что мы на самом деле делаем, потенциально может сломать внутренняя реализация, и мы могли бы также начать получать одинаковые числа из разных потоков, что может быть проблемой, а может и не быть. Гарантия того, что происходит внутри, является более важной проблемой; поскольку Random не не дает какие-либо гарантии безопасности потоков. Таким образом, есть два допустимых подхода:

  • синхронизировать, чтобы мы не обращались к нему одновременно из разных потоков
  • использовать разные Random экземпляров на поток

Либо может быть в порядке; но мьютексирование единственного экземпляра от нескольких вызывающих одновременно одновременно просто вызывает проблемы.

lock достигает первого (и более простого) из этих подходов; Тем не менее, другой подход может быть:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());

это тогда для каждого потока, поэтому вам не нужно синхронизировать.

110 голосов
/ 13 июля 2012

Для простоты повторного использования в вашем приложении может помочь статический класс.

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}

Вы можете использовать затем использовать статический случайный экземпляр с кодом, таким как

StaticRandom.Instance.Next(1, 100);
58 голосов
/ 23 июня 2009

Решение Марка может быть довольно дорогим, поскольку оно требует синхронизации каждый раз.

Мы можем обойти необходимость синхронизации, используя шаблон хранения для конкретного потока:


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

Измерьте две реализации, и вы увидите значительную разницу.

35 голосов
/ 31 марта 2013

Мой ответ от здесь :

Просто повторяя правильное решение :

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}

Так что вы можете позвонить:

var i = Util.GetRandom();

на всем протяжении.

Если вам строго необходим настоящий статический метод без сохранения состояния для генерации случайных чисел, вы можете положиться на Guid.

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}

Это будет немного медленнее, но может быть гораздо более случайным , чем Random.Next, по крайней мере из моего опыта.

Но не :

new Random(Guid.NewGuid().GetHashCode()).Next();

Создание ненужного объекта замедлит работу, особенно в цикле.

и никогда :

new Random().Next();

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

23 голосов
/ 20 апреля 2009

Я бы предпочел использовать следующий класс для генерации случайных чисел:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
13 голосов
/ 20 апреля 2009

1) Как сказал Марк Гравелл, попробуйте использовать ОДИН генератор случайных чисел. Всегда здорово добавить это в конструктор: System.Environment.TickCount.

2) Один совет. Допустим, вы хотите создать 100 объектов и предположить, что у каждого из них должен быть свой собственный генератор случайных чисел (удобно, если вы вычисляете НАГРУЗКИ случайных чисел за очень короткий период времени). Если бы вы делали это в цикле (создание 100 объектов), вы могли бы сделать это так (чтобы обеспечить полную случайность):

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);

Приветствие.

3 голосов
/ 19 октября 2018

Каждый раз, когда вы выполняете

Random random = new Random (15);

Неважно, если вы выполните его миллионы раз, вы всегда будете использовать одно и то же семя.

Если вы используете

Random random = new Random ();

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

Если вам нужны безопасные случайные числа, вы должны использовать класс

System.Security.Cryptography.RNGCryptoServiceProvider

public static int Next(int min, int max)
{
    if(min >= max)
    {
        throw new ArgumentException("Min value is greater or equals than Max value.");
    }
    byte[] intBytes = new byte[4];
    using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        rng.GetNonZeroBytes(intBytes);
    }
    return  min +  Math.Abs(BitConverter.ToInt32(intBytes, 0)) % (max - min + 1);
}

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

int randomNumber = Next(1,100);
2 голосов
/ 11 октября 2018

просто объявите переменную класса Random следующим образом:

    Random r = new Random();
    // ... Get three random numbers.
    //     Here you'll get numbers from 5 to 9
    Console.WriteLine(r.Next(5, 10));

, если вы хотите каждый раз получать разные случайные числа из вашего списка, тогда используйте

r.Next(StartPoint,EndPoint) //Here end point will not be included

Каждый раз, объявляя Random r = new Random() один раз.

1 голос
/ 04 июня 2014

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

public String GenerateRandom(Random oRandom, int iLongitudPin)
{
    String sCharacters = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
    int iLength = sCharacters.Length;
    char cCharacter;
    int iLongitudNuevaCadena = iLongitudPin; 
    String sRandomResult = "";
    for (int i = 0; i < iLongitudNuevaCadena; i++)
    {
        cCharacter = sCharacters[oRandom.Next(iLength)];
        sRandomResult += cCharacter.ToString();
    }
    return (sRandomResult);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...