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

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

//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" имеют равное значение.

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

Ответ 1

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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


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

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

Ответ 4

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

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

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

Итак, вы можете позвонить:

var i = Util.GetRandom();

.

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

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

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

Но не:

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

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

И никогда:

new Random().Next();

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

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

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

Ответ 8

Если вы хотите использовать функцию, которая создает экземпляр Random каждый раз, когда вы можете использовать это:

Threading.Thread.Sleep(1)
Dim random As New Random(System.DateTime.Now.Millisecond)
Dim secret = random.Next(10000, 100000)

Ответ 9

Это приведет к генерации случайных чисел. Подстройте его по мере необходимости.

private static void Main(string[] args)
{
    var randoms = GetRandomNumbers(1, 1000, 50);

}

private static IEnumerable<int> GetRandomNumbers(int low, int high, int numberOfRandoms)
{
    Random rnd = new Random();
    var unique = new HashSet<int>();

    do
    {
        int num = rnd.Next(low, high);
        if (unique.Contains(num)) continue;
        unique.Add(num);
    } while (unique.Count < numberOfRandoms);

    return unique.ToList();
}

(== Try Me ==)