Как сделать рандом в с

от admin

# Generating Random Numbers in C#

This example generates random values between 0 and 2147483647.

# Generate a random int in a given range

Generate a random number between minValue and maxValue — 1 .

# Generating the same sequence of random numbers over and over again

When creating Random instances with the same seed, the same numbers will be generated.

# Create multiple random class with different seeds simultaneously

Two Random class created at the same time will have the same seed value.

Using System.Guid.NewGuid().GetHashCode() can get a different seed even in the same time.

Another way to achieve different seeds is to use another Random instance to retrieve the seed values.

This also makes it possible to control the result of all the Random instances by setting only the seed value for the rndSeeds . All the other instances will be deterministically derived from that single seed value.

# Generate a Random double

Generate a random number between 0 and 1.0. (not including 1.0)

# Generate a random character

Generate a random letter between a and z by using the Next() overload for a given range of numbers, then converting the resulting int to a char

# Generate a number that is a percentage of a max value

A common need for random numbers it to generate a number that is X% of some max value. this can be done by treating the result of NextDouble() as a percentage:

# Syntax
# Parameters
Parameters Details
Seed A value for generating random numbers. If not set, the default value is determined by the current system time.
minValue Generated numbers won’t be smaller than this value. If not set, the default value is 0.
maxValue Generated numbers will be smaller than this value. If not set, the default value is Int32.MaxValue .
return value Returns a number with random value.
# Remarks

The random seed generated by the system isn’t the same in every different run.

How do I generate a random integer in C#?

The Random class is used to create random numbers. (Pseudo-random that is of course.).

If you are going to create more than one random number, you should keep the Random instance and reuse it. If you create new instances too close in time, they will produce the same series of random numbers as the random generator is seeded from the system clock.

The question looks very simple but the answer is bit complicated. If you see almost everyone has suggested to use the Random class and some have suggested to use the RNG crypto class. But then when to choose what.

For that we need to first understand the term RANDOMNESS and the philosophy behind it.

I would encourage you to watch this video that I made which goes in depth in the philosophy of RANDOMNESS using C# https://www.youtube.com/watch?v=tCYxc-2-3fY

First thing let us understand the philosophy of RANDOMNESS. When we tell a person to choose between RED, GREEN and YELLOW what happens internally. What makes a person choose RED or YELLOW or GREEN?

c# Random

Some initial thought goes into the persons mind which decides his choice, it can be favorite color , lucky color and so on. In other words some initial trigger which we term in RANDOM as SEED.This SEED is the beginning point, the trigger which instigates him to select the RANDOM value.

Now if a SEED is easy to guess then those kind of random numbers are termed as PSEUDO and when a seed is difficult to guess those random numbers are termed SECURED random numbers.

For example a person chooses is color depending on weather and sound combination then it would be difficult to guess the initial seed.

c# Random

Now let me make an important statement:-

*“Random” class generates only PSEUDO random number and to generate SECURE random number we need to use “RNGCryptoServiceProvider” class.

c# Random

Random class takes seed values from your CPU clock which is very much predictable. So in other words RANDOM class of C# generates pseudo random numbers , below is the code for the same.

While the RNGCryptoServiceProvider class uses OS entropy to generate seeds. OS entropy is a random value which is generated using sound, mouse click, and keyboard timings, thermal temp etc. Below goes the code for the same.

Генерация случайных чисел в 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 , который использует более сложные алгоритмы генерации случайных величин и используется в работе алгоритмов шифрования.

Генерация случайных чисел в .NET

В качестве примера кода приводится примерно следующее:

Итак, что здесь неправильно?

Объяснение

Класс Random не является истинным генератором случайных чисел, он содержит генератор псевдослучайных чисел. Каждый экземпляр класса Random содержит некоторое внутреннее состояние, и при вызове метода Next (или NextDouble, или NextBytes) метод использует это состояние для возврата числа, которое будет казаться случайным. После этого внутреннее состояние меняется таким образом, чтобы при следующем вызове метода Next он возвратил другое кажущееся-случайным число, отличное от возвращённого ранее.

Все «внутренности» работы класса Random полностью детерминистичны. Это значит, что если вы возьмёте несколько экземпляров класса Random с одинаковым начальным состоянием, которое задаётся через параметр конструктора seed, и для каждого экземпляра вызовите определённые методы в одинаковом порядке и с одинаковыми параметрами, то в конце вы получите одинаковые результаты.

Так что ж плохого в вышеприведённом коде? Плохо то, что мы используем новый экземпляр класса Random внутри каждой итерации цикла. Конструктор Random, не принимающий параметров, принимает значение текущей даты и времени как seed (начальное состояние). Итерации в цикле «прокрутятся» настолько быстро, что системное время «не успеет измениться» по их окончании; таким образом, все экземпляры Random получат в качестве начального состояния одинаковое значение и поэтому возвратят одинаковое псевдослучайное число.

Как это исправить?

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

Использование криптографического генератора случайных чисел

.NET содержит абстрактный класс RandomNumberGenerator, от которого должны наследоваться все реализации криптографических генераторов случайных чисел (далее — криптоГСЧ). Одну из таких реализаций содержит и .NET — встречайте класс RNGCryptoServiceProvider. Идея криптоГСЧ в том, что даже если он всё так же является генератором псевдослучайных чисел, он обеспечивает достаточно сильную непредсказуемость результатов. RNGCryptoServiceProvider использует несколько источников энтропии, которые фактически являются «шумами» в вашем компьютере, и генерируемую им последовательность чисел очень тяжело предсказать. Более того, «внутрикомпьютерный» шум может использоваться не только в качестве начального состояния, но и между вызовами следующих случайных чисел; таким образом, даже зная текущее состояние класса, этого не хватит для вычисления как следующих чисел, которые будут сгенерированы в будущем, так и тех, которые были сгенерированы ранее. Вообще-то точное поведение зависит от реализации. Помимо этого, Windows может использовать специализированное аппаратное обеспечение, являющееся источником «истинных случайностей» (например, это может быть датчик распада радиоактивного изотопа) для генерации ещё более защищённых и надёжных случайных чисел.

Сравним это с ранее рассматриваемым классом Random. Предположим, вы вызвали Random.Next(100) десять раз и сохранили результаты. Если вы имеете достаточно вычислительной мощи, то можете сугубо на основании этих результатов вычислить начальное состояние (seed), с которым был создан экземпляр Random, предсказать следующие результаты вызова Random.Next(100) и даже вычислить результаты предыдущих вызовов метода. Такое поведение катастрофически неприемлемо, если вы используете случайные числа для обеспечения безопасности, в финансовых целях и т.д. КриптоГСЧ работают существенно медленнее класса Random, но генерируют последовательность чисел, каждое из которых более независимо и непредсказуемо от значений остальных.

В большинстве случаев более низкая производительность не является препятствием — им является плохой API. RandomNumberGenerator создан для генерации последовательностей байтов — и всё. Сравните это с методами класса Random, где есть возможность получения случайного целого числа, дробного числа, а также набора байтов. Ещё одно полезное свойство — возможность получения случайного числа в указанном диапазоне. Сравните эти возможности с массивом случайных байтов, который выдаёт RandomNumberGenerator. Исправить ситуацию можно, создав свою оболочку (враппер) вокруг RandomNumberGenerator, которая будет преобразовывать случайные байты в «удобный» результат, однако это решение нетривиально.

Тем не менее, в большинстве случаев «слабость» класса Random вполне подходит, если вы сможете решить проблему, описанную в начале статьи. Посмотрим, что здесь можно сделать.

Используйте один экземпляр класса Random при множественных вызовах

Вот он, корень решения проблемы — использовать лишь один экземпляр Random при создании множества случайных чисел посредством Random.Next. И это очень просто — посмотрите, как можно изменить вышеприведённый код:

Теперь в каждой итерации будут разные числа, … но это ещё не всё. Что будет, если мы вызовем этот блок кода два раза подряд? Правильно, мы создадим два экземпляра Random с одинаковым начальным значением и получим два одинаковых набора случайных чисел. В каждом наборе числа будут различаться, однако между собой эти наборы будут равны.

Есть два способа решения проблемы. Во-первых, мы можем использовать не экземплярное, а статическое поле, содержащее экземпляр Random, и тогда вышеприведённый кусок кода создаст лишь один экземпляр, и будет его использовать, вызываясь сколько угодно раз. Во-вторых, мы можем вообще убрать оттуда создание экземпляра Random, переместив его «повыше», в идеале — на самый «верх» программы, где будет создан единичный экземпляр Random, после чего он будет передаваться во все места, где нужны случайные числа. Это отличная идея, которая красиво выражается зависимостями, но она будет работать до тех пор, пока мы используем лишь один поток.

Потокобезопасность

Класс Random не потокобезопасен. Учитывая то, как мы любим создавать один экземпляр и использовать его по всей программе на протяжении всего времени её выполнения (синглтон, привет!), отсутствие потокобезопасности становится реальной занозой. Ведь если мы используем один экземпляр одновременно в нескольких потоках, то есть вероятность обнуления его внутреннего состояния, и если это произойдёт, то с этого момента экземпляр станет бесполезным.

Снова-таки, здесь есть два пути решения проблемы. Первый путь по-прежнему предполагает использование одного экземпляра, однако на этот раз с использованием блокировки ресурса посредством монитора. Для этого необходимо создать оболочку вокруг Random, которая будет оборачивать вызов его методов в оператор lock, обеспечивающий эксклюзивный доступ к экземпляру для вызывающей стороны. Этот путь плох тем, что снижает производительность в многопоточно-интенсивных сценариях.

Другой путь, который я опишу ниже — использование по одному экземпляру на каждый поток. Единственное, нам нужно удостовериться, что при создании экземпляров мы используем разные начальные значения (seed), а потому мы не можем использовать конструкторы по умолчанию. Во всём остальном этот путь относительно прямолинеен.

Безопасный провайдер

К счастью, новый обобщённый класс ThreadLocal<T>, появившийся в .NET 4, очень сильно облегчает написание провайдеров, обеспечивающих по одному экземпляру на поток. Просто нужно в конструктор ThreadLocal передать делегат, который будет ссылаться на получение значения собственно нашего экземпляра. В данном случае я решил использовать единственное начальное значение (seed), инициализируя его при помощи Environment.TickCount (именно так действует конструктор Random без параметров). Далее полученное количество тиков инкрементируется каждый раз, когда нам нужно получить новый экземпляр Random для отдельного потока.

Нижепредставленный класс полностью статический и содержит лишь один публичный (открытый) метод GetThreadRandom. Этот метод сделан методом, а не свойством, в основном из-за удобства: благодаря этому все классы, которым нужен экземпляр Random, будут зависеть от Func<Random> (делегат, указывающий на метод, не принимающий параметров и возвращающий значение типа Random), а не от самого класса Random. Если тип предназначен для работы в одном потоке, он может вызвать делегат для получения единого экземпляра Random и после чего использовать его повсюду; если же тип должен работать в многопоточных сценариях, он может вызывать делегат каждый раз, когда ему требуется генератор случайных чисел. Нижеприведенный класс создаст столько экземпляров класса Random, сколько есть потоков, и каждый экземпляр будет стартовать с различного начального значения. Если нам нужно использовать провайдер случайных чисел как зависимость в других типах, мы можем сделать так: new TypeThatNeedsRandom(RandomProvider.GetThreadRandom) . Ну а вот и сам код:

Достаточно просто, не правда ли? Всё потому, что весь код направлен на выдачу правильного экземпляра Random. После того, как экземпляр создан и возвращён, совершенно неважно, что вы будете с ним делать дальше: все дальнейшие выдачи экземпляров совершенно не зависят от текущего. Конечно, клиентский код имеет лазейку для злонамеренного неправильного использования: он может получить один экземпляр Random и передать его в другие потоки вместо вызова в тех, других потоках, нашего RandomProvider.

Проблемы с дизайном интерфейса

Одна проблема всё равно остаётся: мы используем слабо защищённый генератор случайных чисел. Как упоминается ранее, существует намного более безопасная во всех отношениях версия ГСЧ в RandomNumberGenerator, реализация которого находится в классе RNGCryptoServiceProvider. Однако его API достаточно сложно использовать в стандартных сценариях.

Было бы очень приятно, если бы провайдеры ГСЧ в фреймворке имели отдельные «источники случайности». В таком случае мы могли бы иметь единый простой и удобный API, который бы поддерживался как небезопасной-но-быстрой реализацией, так и безопасной-но-медленной. Что-ж, мечтать не вредно. Возможно, подобный функционал появится в следующих версиях .NET Framework. Возможно, кто-то не из Microsoft предложит свою реализацию адаптера. (К сожалению, я не буду этим кем-то… правильная реализация подобной задумки удивительно сложна.) Вы также можете создать свой класс, отнаследовав его от Random и переопределив методы Sample и NextBytes, однако неясно, как именно они должны работать, и даже собственная реализация Sample может быть намного сложнее, нежели кажется. Может быть, в следующий раз…

Читать:
Почему при открытии диспетчера задач нагрузка 100 и сразу снижает

Похожие статьи