it-swarm.com.ru

Генератор случайных чисел, генерирующий только одно случайное число

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

//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" будут иметь одинаковое значение.

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

721
Ivan Prodanov

Каждый раз, когда вы делаете 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());

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

984
Marc Gravell

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

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);
110
Phil

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

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


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);
    }
}

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

58
Hans Malherbe

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

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

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();

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

35
nawfal

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

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
23
fARcRY

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);

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

13
sabiland

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

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);
3
Joma

просто объявите переменную класса 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() один раз.

2
Abdul

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

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);
}
1
Marztres