Генерация случайных чисел в C#: от новичка до профи (решение топ-вопроса с Stack Overflow)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как сгенерировать случайное целое число в C#: разбираемся раз и навсегда

Вопрос генерации случайных чисел — один из самых популярных на Stack Overflow. Кажется, что может быть проще? Но на практике даже опытные разработчики на C# допускают классические ошибки: получают одинаковые значения при каждом запуске, сталкиваются с неслучайными последовательностями или забывают про потокобезопасность. Сегодня мы раз и навсегда разберем, как правильно генерировать случайные целые числа в C#, и ответим на все «почему» из того самого топа Stack Overflow.

Эта статья — ваш шанс перестать копировать код из интернета и начать понимать, что на самом деле происходит под капотом System.Random. Поехали!

Почему новички получают одно и то же число снова и снова?

Самый частый вопрос: «Я создаю Random и всегда получаю одно и то же число!». Давайте посмотрим на типичный код-ловушку:

// ❌ ПЛОХОЙ ПРИМЕР
for (int i = 0; i < 5; i++)
{
    Random rnd = new Random();
    Console.WriteLine(rnd.Next(1, 100));
}

Что выведет этот код? С вероятностью 99% — пять одинаковых чисел. Почему? Потому что Random использует системные часы для инициализации (seed). Когда вы создаете несколько экземпляров Random подряд, они получают один и тот же «сид», и следовательно — генерируют идентичную последовательность.

Правильное решение — создать один экземпляр Random и использовать его многократно:

// ✅ ПРАВИЛЬНЫЙ ПРИМЕР
Random rnd = new Random();
for (int i = 0; i < 5; i++)
{
    Console.WriteLine(rnd.Next(1, 100));
}

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

Random.Next(): три способа получить случайное целое

Класс System.Random предлагает три перегрузки метода Next(). Рассмотрим каждую с примерами:

1. Random.Next() — без параметров

Возвращает неотрицательное целое число в диапазоне от 0 до Int32.MaxValue - 1. То есть от 0 до 2 147 483 646.

Random rnd = new Random();
int anyNumber = rnd.Next();
Console.WriteLine(anyNumber); // Например: 1789034215

2. Random.Next(Int32) — с верхней границей

Возвращает число от 0 (включительно) до указанного максимума (исключительно).

int dice = rnd.Next(7); // 0, 1, 2, 3, 4, 5 или 6
Console.WriteLine(dice);

3. Random.Next(Int32, Int32) — диапазон [min, max)

Самый популярный вариант для генерации случайного целого числа C# в заданном диапазоне.

// Случайное число от 1 до 100 (включительно)
int score = rnd.Next(1, 101);
Console.WriteLine(score);

Важно запомнить: верхняя граница не включается. Чтобы получить число от 1 до 6 (как на кубике), пишите Next(1, 7).

Топ-3 ошибки при работе с Random в C#

Опираясь на реальные вопросы с Stack Overflow C# random, выделим самые частые грабли:

  • Ошибка №1: Создание нового Random в цикле. Мы уже разобрали — это приводит к одинаковым числам. Решение: вынесите new Random() за пределы цикла.
  • Ошибка №2: Random не потокобезопасен. Если несколько потоков используют один экземпляр Random без синхронизации, возможны сбои и нулевые значения. Используйте lock или ThreadLocal<Random>.
  • Ошибка №3: Использование Random для паролей или токенов. System.Random — это псевдослучайный генератор. Он предсказуем! Для безопасности нужен System.Security.Cryptography.RandomNumberGenerator.

Как генерировать криптостойкие случайные числа?

Если вы пишете код для генерации паролей, ключей шифрования или токенов аутентификации — забудьте про Random. Используйте криптостойкий случайный C# из пространства имен System.Security.Cryptography.

using System.Security.Cryptography;

public static int GetCryptographicRandomInt(int min, int max)
{
    if (min >= max) throw new ArgumentException("min должен быть меньше max");

    using (var rng = RandomNumberGenerator.Create())
    {
        byte[] randomNumber = new byte[4];
        rng.GetBytes(randomNumber);
        uint scale = BitConverter.ToUInt32(randomNumber, 0);
        return (int)(min + (max - min) * (scale / (uint.MaxValue + 1.0)));
    }
}

// Пример использования:
int secureCode = GetCryptographicRandomInt(1000, 9999);
Console.WriteLine(secureCode);

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

Генерация уникальных случайных чисел без повторов

Еще один популярный вопрос: как получить несколько случайных чисел, которые не повторяются? Например, для лотереи или перемешивания колоды. Простой Random.Next() не гарантирует уникальность.

Лучший подход — сначала создать список всех возможных значений, а затем перемешать его (алгоритм Фишера-Йетса):

public static List<int> GetUniqueRandomNumbers(int count, int min, int max)
{
    if (max - min + 1 < count)
        throw new ArgumentException("Диапазон слишком мал для запрошенного количества чисел.");

    List<int> numbers = Enumerable.Range(min, max - min + 1).ToList();
    Random rnd = new Random();

    // Перемешивание (алгоритм Фишера-Йетса)
    for (int i = numbers.Count - 1; i > 0; i--)
    {
        int j = rnd.Next(i + 1);
        (numbers[i], numbers[j]) = (numbers[j], numbers[i]);
    }

    return numbers.Take(count).ToList();
}

// Использование: 6 неповторяющихся чисел от 1 до 49
var lottery = GetUniqueRandomNumbers(6, 1, 49);
Console.WriteLine(string.Join(", ", lottery));

Этот метод гарантирует, что все сгенерированные числа будут различными и равномерно распределенными.

Random и многопоточность: как не сломать программу

Если

Блоги

Рекомендаций по книгам