Почему кажется, что мой генератор случайных чисел не случайный в C#?

Я работаю в Microsoft Visual C# 2008 Express.

Я нашел этот фрагмент кода:

    public static int RandomNumber(int min, int max)
    {
        Random random = new Random();

        return random.Next(min, max);
    }

проблема в том, что я запускал его более 100 раз, и он ВСЕГДА дает мне один и тот же ответ, когда мои min = 0 и max = 1. Я получаю 0 каждый раз. (Я создал тестовую функцию для ее запуска - действительно - я получаю 0 каждый раз). Мне трудно поверить, что это совпадение... Есть ли что-то еще, что я могу сделать, чтобы проверить или проверить это? (Я повторил тест с min = 0 и max = 10 и первые 50-кратные результаты, результат всегда был "5", 2-й 50-кратный результат, всегда был "9".

?? Мне нужно что-то более последовательно случайное...

-Adeena

14 ответов

Решение

Проблема с min = 0 и max = 1 в том, что min включительно, а max эксклюзивно. Таким образом, единственное возможное значение для этой комбинации - 0.

random = new Random();

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

Не создавайте метод-обертку для Next. Это тратит впустую циклы, создавая новый экземпляр класса Random. Просто используйте тот же!

Random myRand = new Random();

for(int i = 0; i < 10; i++)
{
    Console.WriteLine(myRand.Next(0, 10).ToString());
}

Это должно дать вам десять случайных значений.

Как уже было сказано, Random является псевдослучайным (как и все реализации), и если вы создадите 100 экземпляров с одинаковым начальным числом, вы получите 100 экземпляров с одинаковыми результатами. Убедитесь, что вы используете класс повторно.

Кроме того, как говорили люди, будьте осторожны, что MinValue является эксклюзивным, а MaxValue - эксклюзивным. Для чего вы хотите, сделайте myRand.Next(0, 2).

Эта перегрузка Next() возвращает:

32-разрядное целое число со знаком больше или равно minValue и меньше maxValue; то есть диапазон возвращаемых значений включает minValue, но не MaxValue. Если minValue равно maxValue, возвращается minValue.

0 - единственно возможное значение для возврата. Возможно, вы хотите random.NextDouble(), который будет возвращать двойное значение от 0 до 1.

Вы всегда получаете 0, потому что Random.Next возвращает целые числа Вам нужно позвонить Random.NextDouble, который будет возвращать число от 0 до 1. Кроме того, вы должны повторно использовать ваш случайный экземпляр, например так:

[ThreadStatic]
static Random random;
public static Random Random { 
    get {
        if (random == null) random = new Random();
        return random;
    }
}
public static int RandomInteger(int min, int max)
{
    return Random.Next(min, max);
}
public static double RandomDouble() //Between 0 and 1
{ 
    return Random.NextDouble();
} 

Если вы хотите криптографически защищенные случайные числа, используйте RNGCryptoServiceProvider учебный класс; увидеть эту статью

РЕДАКТИРОВАТЬ: поток безопасности

Мин. Включительно, но не более. Проверьте API

Помимо проблемы 0-1, уже отмеченной в других ответах, ваша проблема является реальной, когда вы ищете диапазон 0-10 и получаете идентичные результаты 50 раз подряд.

new Random() должен возвращать случайное число с начальным числом, инициализированным из таймера (текущая секунда), но, очевидно, вы вызываете этот код 50 раз в секунду. MSDN предлагает: "Чтобы улучшить производительность, создайте один случайный случай, чтобы генерировать много случайных чисел во времени, вместо того, чтобы повторно создавать новый случайный случай, чтобы генерировать одно случайное число". Если вы создадите генератор случайных чисел один раз за пределами метода, это должно исправить вашу проблему "неслучайности", а также повысить производительность.

Также рассмотрите этот пост для лучшего генератора псевдослучайных чисел, чем поставляемый системой, если вам нужны псевдослучайные числа "более высокого качества".

Вы неправильно понимаете строку "random.Next(min, max)". "min" - место наименьшего числа, которое может быть сгенерировано случайным образом. В то время как "макс" находится на месте самого низкого числа, которое НЕ может быть сгенерировано, оно не находится на месте наибольшего числа, которое может быть получено. Поэтому, когда линия случайна. Далее (0, 1) вы в основном позволяете рисовать только 0.

Это дополнение к любым ответам, так как ответом на этот конкретный вопрос является то, что границы должны быть (0, 2), а не (0, 1).

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

public static class ThreadSafeRandom
{
    private static readonly Random seed = new Random();

    [ThreadStatic]
    private static Random random;

    public static int Next(int min, int max)
    {
        if (random == null)
        {
            lock (seed)
            {
                random = new Random(seed.Next());
            }
        }

        return random.Next(min, max);
    }

    // etc. for other members
}

Как уже упоминали другие, случайный объект, создаваемый несколько раз в секунду, использует ту же секунду, что и начальное число, поэтому я бы поместил конструктор Random за пределы цикла и передал его в качестве параметра, например так:

public static int RandomNumber(Random random, int min, int max)
{
    return random.Next(min, max);
}

Также, как упоминалось другими, max является эксклюзивным, поэтому, если вы хотите 0 или 1, вы должны использовать [0,2] в качестве [min,max] или некоторого большего максимума, а затем сделать двоичное И с 1.

public static int RandomOneOrZero(Random random)
{
    return random.Next(0, int.MaxValue) & 1;
}

Ваш диапазон не соответствует действительности. Минимальное значение включено в диапазон, а максимальное значение является исключительным в диапазоне (это означает, что оно не будет включено в диапазон). Вот почему он возвращает только 0.

Еще одно полезное замечание: создание экземпляра Random в методе не идеально, так как при вызове может быть получено одно и то же начальное значение. Поэтому вместо этого я бы сказал использовать:

      static Random gen = new Random();

public static int RandomNumber(int minValue, int maxValue){
    return gen.Next(minValue, maxValue);
}

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

   int seed = Convert.ToInt32(DateTime.Now.Millisecond.ToString().Substring(1, 2));
   int cnr = new Random(seed).Next(100);

Это грубо, но это работает!:-)

Конечно, он статистически генерирует одно и то же число каждые сто раз. В качестве альтернативы вы можете взять все три цифры или объединить их с другими значениями даты и времени, такими как секунды или около того.

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

В VB я всегда начинаю с функции Randomize(). Просто вызовите Randomize() и запустите вашу случайную функцию. Я также делаю следующее:

Function RandomInt(ByVal lower As Integer, ByVal upper As Integer) As Integer
    Return CInt(Int((upper - lower + 1) * Rnd() + lower))
End Function

Надеюсь это поможет!:)

Другие вопросы по тегам