C# Random: заготовленные случайности

Определенные типы задач, которые решает разработчик, требуют от него использования случайных величин. Предположим, нужно имитировать случайное выпадение какой-то грани игрального кубика или бросить жребий, выбрав вариант «орел» или «решка». В C# есть возможность «подбросить монетку» и взять псевдослучайные числовые значения. Они генерируются классом Random . Вот о нем и предлагаем поговорить более подробно.
Класс Random
Предположим, мы имитируем игру «покер на костях». Пять кубиков бросаются, и с каждым броском мы получаем пять значений, каждое из которых будет в пределах от одного до шести. Консольное приложение, генерирующее шесть значений, будет таким:
Мы использовали метод Next и указали в качестве аргумента диапазон значений (левое число включается, правое — нет).

Зерно выборки
При использовании генератора случайных чисел следует иметь в виду, что метод генерации является псевдослучайным. Он ориентируется на таком параметре как «зерно выборки» ( seed ), которое определяет псевдослучайное значение. Для имитации этого «зерна выборки» используются внутренние системные параметры, которые все время меняются, например, текущее время. Если вы будете использовать в коде «зерно выборки» с одним и тем же числовым значением, то и величина на выходе будет одной и той же.
rand() – генератор случайных чисел в C++

Не всегда надо заполнять числовые одномерные и двумерные массивы порядковыми номерами или конкретными значениями. Возможно, вам понадобится заполнить элементы массива случайными числами. В С++ для этого есть специальные фyнкции rand() и srand() .
Они находятся в библиoтечном файле cstdlib , поэтому чтобы их применять в программе, необходимо подключить этот библиотечный файл: #include <cstdlib> или #include <stdlib.h> (для старых компиляторов).
Если воспользоваться только функцией rand() – будем получать одинаковые “случайные числа” от запyска к запуску. Наберите следующий код и откомпилируйте программу несколько раз. Обратите внимание, что “случайные числа” всегда будут одинаковы.
Случайное число генерируется в строке 11 и записывается в i -й элемент массива randomDigits . В следующей строке просим его показать. Запуская программу будем видеть каждый раз oдни и тe же числa:

Получается, что числа генерируются не совсем случайные. Чтобы добиться “настоящей” случайности чисел при повторных запуска x программы, необходимо применить функцию srand() до функции rand() . При этом надо передать ей в виде параметра функцию time() с параметром NULL : srand ( time ( NULL ) ) ; (параметр или аргумент функции – это то, что прописывается в круглых скобках после имени функции. Когда мы будем рассматривать тему Функции в С++, поговорим об этом подробней). Таким образом srand() получает в виде параметра текущее системное время, которое при каждом запускe программы будет разным. Это позволит функции rand() каждый раз генерировать именно случайные числа. Для использования time() необходимо подключить библиотечный файл ctime ( time.h для более старых компиляторов): #include <ctime> .
Пробуйте запускать. Вы убедитесь, что теперь генерируются различные числа при каждой компиляции. У меня получился такой результат:

Первая компиляция

Вторая компиляция
Все выглядит неплохо. Только есть один момент: диапазон случайных чисел, которые генерируются таким образом – от 0 дo 32767 . Возможно вам понадобится заполнить массив числами от 200 дo 300, от 0.1 дo 1, от -20 дo 20. Такую генерацию случайных чисел возможно и несложно реализовать. В примере рассмотрим несколько случаев:
В первом цикле for происходит генерация случайных чисел определённых диапазонов и их запись в соответствующие массивы. В каждом шаге цикла будут генерироваться новыe случайные числа. Возможно кому-то сложно разобраться как это происходит. Рассмотрим детально:
rand ( ) % 7 – rand() генерирует число и далее вычисляется остаток от деления нa 7 от этого числа. Понятно, что это могут быть числа только oт 0 до 6. Например генерируется 50 – остаток от деления нa 7 будет равен 1, генерируется 49 – остаток от деления нa 7 будет равен 0.
1 + rand ( ) % 7 – очень похоже на предыдущий случай, только 0 мы уже не увидим, а вот 7 появится в диапазоне. Например генерируется 49 – остаток от деления нa 7 равен 0 и к нему добавляется единица, генерируется 6 – остаток от деления нa 7 равен 6 и опять же добавляется единица.
200 + rand ( ) % 101 – даст нам число от 200 до 300. Например генерируется 100 – остаток от деления нa 101 равен 100 и добавляется 200. Получаем число 300. Генерируется 202: 200 + (202 % 101)= 200 + 0 = 200.
rand ( ) % 41 — 20 – oт – 20 дo 20. Например генерируется 1: (1 % 40) – 20 = 1 – 20 = -19; генерируется 30: 30 – 20 = 10.
0.01 * ( rand ( ) % 101 ) – oт 0.01 дo 1. Например генерируется 55: 0.01* 55 = 0.55.

Чтобы попрактиковаться, попробуйте решить задачу: компьютер “загадывает” число oт 1 дo 7, a пользователь должен его отгадать. Если не получится – смотрите наш вариант решения:
Генерация случайных чисел в C#
Генерация случайных чисел является неотъемлемой и важной частью многих криптографических операций. Например, криптографические ключи должны выбираться настолько случайно, насколько это, в принципе, возможно. В .NET C# есть несколько вариантов генерации случайных чисел.
Класс Random
Этот класс представляет генератор псевдослучайных чисел. То есть, в работе этого класса заложен алгоритм, который генерирует последовательность чисел, отвечающую определенным статистическим критериям случайности.
Конструкторы класса Random
У класса определено два конструктора. Первый конструктор — без параметров, создает новый экземпляр класса и инициализирует его значением по умолчанию. В .NET Framework начальное значение по умолчанию зависит от текущего времени. В .NET Core и более поздних версиях платформы начальное значение по умолчанию создается генератором псевдослучайных чисел потока. Конструктор без параметров используется наиболее часто и позволяет создавать генераторы случайных чисел с оптимальной производительностью и вероятностью распределения случайной величины. Например, так мы можем создать экземпляр Random , используя этот конструктор:
Второй конструктор позволяет создавать новый экземпляр Random и инициализировать его заданным значением, например
Этот конструктор стоит использовать в том случае, если Вам необходимо получать каждый раз одну и ту же серию псевдослучайных чисел, например, для отладки приложения. Чтобы продемонстрировать наглядно, что имеется в виду, напишем следующий пример:
здесь мы создаем массив из пяти псевдослучайных байтов и выводим его в консоль. Вот, что мы можем увидеть, если перезапустим наше приложение, скажем, 3 раза:
Три запуска — три различных серии случайных чисел. Теперь воспользуемся конструктором с начальным значением и также попытаемся сформировать три серии случайных чисел:
Три запуска — три серии одинаковых чисел.
Вот, собственно, наглядная демонстрация того, чем принципиально отличаются конструкторы у Random .
Методы и свойства Random
Чтобы сгенерировать случайное число, у класса Random предусмотрены следующие методы:
| Метод | Описание |
|---|---|
| Next() | Возвращает неотрицательное случайное целое число. |
| Next(Int32) | Возвращает неотрицательное случайное целое число, которое меньше указанного максимального значения. |
| Next(Int32, Int32) | Возвращает случайное целое число в указанном диапазоне. |
| Next Bytes(Byte[]) | Заполняет элементы указанного массива байтов случайными числами. |
| Next Double() | Возвращает случайное число с плавающей запятой, которое больше или равно 0,0 и меньше 1,0. |
| NextSingle() | Возвращает случайное число с плавающей запятой в диапазоне от 0,0 до 1,0. |
Пример использования класса Random в приложениях C#
или, если необходимо получить одно случайное число, то можно воспользоваться методами Next :
Если вы планируете использовать объекты класса Random в потоках, то необходимо использовать потокобезопасный экземпляр, используя свойство Shared класса, например, так:
Проблемы с Random
По данным разработчиков, в большинстве систем Windows объекты Random , созданные с интервалом в 15 миллисекунд друг от друга, скорее всего, будут иметь одинаковые начальные значения. Возможно, что это утверждение относится только к .NET Framework (или же информация на сайте Microsoft устарела), так как для .NET 7 пример этой проблемы, предоставленный самой Microsoft, у меня показывал абсолютно различные ряды случайных чисел, несмотря на соблюдение всех условий — создание двух экземпляров Random в течение 15 мс и менее, ряд на 100 элементов и т.д.
Класс RandomNumberGenerator
Этот класс расположен в пространстве имен System.Security.Cryptography и предназначен для создания криптографически надежных случайных значений. Класс абстрактный, поэтому создавать напрямую мы его не можем, но можем использовать классы, его реализующие, например, классом RNGCrypto Service Provider , который на данный момент не рекомендуется использовать или же, как рекомендуют разработчики из Microsoft — использовать статические методы класса. Посмотрим как работает RandomNumberGenerator .
Статические методы RandomNumberGenerator
Если не создавать наследника для RandomNumberGenerator , то для генерации случайных чисел мы можем воспользоваться следующими статическими методами класса:
| Метод | Описание |
|---|---|
| Create() | Создает экземпляр реализации по умолчанию криптографического генератора случайных чисел, позволяющего генерировать случайные данные. |
| Fill(Span<Byte>) | Заполняет диапазон криптостойкими случайными байтами. |
| Get Bytes(Int32) | Создает массив байтов с криптографически строгой случайной последовательностью значений. |
| Get Int32(Int32) | Создает случайное целое число от 0 (включительно) до указанного исключенного верхнего предела, используя генератор криптостойких случайных чисел. |
| Get Int32(Int32, Int32) | Создает случайное целое число от указанного нижнего предела (включенного ) до указанного верхнего предела (исключая его), используя генератор криптостойких случайных чисел. |
Примеры использования этих методов представлены ниже:
Проблемы с RandomNumberGenerator
RandomNumberGenerator использует более сложные алгоритмы для генерации случайных чисел и их последовательностей, а за качество нам приходится «платить» скоростью. Поэтому при прочих равных условиях RandomNumberGenerator будет работать всегда медленнее, чем Random . Ниже в таблице, для примера, показано время в миллисекундах которое потребовалось двум генераторам для генерации 1000 000 случайных величин
| # цикла | Random | RandomNumberGenerator |
|---|---|---|
| 1 | 9 | 187 |
| 2 | 10 | 184 |
| 3 | 9 | 182 |
Итого
Для генерации случайных чисел в C# могут использоваться два класса — Random и RandomNumberGenerator . При этом, класс Random обладает большей производительностью, однако, является менее надежным, чем RandomNumberGenerator , который использует более сложные алгоритмы генерации случайных величин и используется в работе алгоритмов шифрования.
Генерирование случайных чисел. Функции rand() , srand() , time() . Примеры
В языке C++ существуют средства для генерирования случайных чисел. Чтобы сгенерировать случайное число используется функция rand() , которая размещается в библиотечном файле stdlib.h . Синтаксис объявления функции следующий:
Функция возвращает случайное целочисленное значение, которое лежит в пределах от 0 до 32767.
Пример.
здесь startValue – целочисленное значение, которое служит отправной точкой для генерирования последовательности случайных чисел функцией rand() . Изменяя значение startValue , можно получать разные последовательности случайных чисел.
Пример.
то каждый раз при запуске программы будет создана новая отправная точка в генерировании последовательности чисел функцией rand() . И, как следствие, будут получаться разные последовательности случайных чисел.
Пример. В примере демонстрируется генерирование последовательности из двух случайных чисел. Каждый раз при запуске программы будет получена новая последовательность.
4. Как сгенерировать случайное целое число в заданных пределах? Пример
В примере приведена функция GetRandomNumber() , которая генерирует случайное число в заданных пределах.
5. Заполнение двумерной матрицы случайными целыми числами в указанных пределах. Пример
Условие задачи. Дана двумерная матрица порядка n ( n столбцов, n строк) целых чисел. Найти наибольшее из значений элементов, которые размещены в закрашенной части матрицы. Значение элементов матрицы формируются случайным образом и находятся в пределах [-5; +5].
Текст программы следующий
6. Как сгенерировать случайное число с плавающей запятой в указанных пределах? Пример
В примере демонстрируется функция GetRandomNumberFloat() , которая генерирует случайное число с плавающей запятой в указанных пределах.