Зачем использовать class C # System.Random вообще, а не System.Security.Cryptography.RandomNumberGenerator?
Почему кто-нибудь вообще использует генератор случайных чисел «стандартного» из System.Random, а не всегда используя криптографически безопасный генератор случайных чисел из System.Security.Cryptography.RandomNumberGenerator (или его подclassы, поскольку RandomNumberGenerator является абстрактным)?
Нейт Лоусон рассказывает нам в своей презентации Google Tech Talk « Crypto Strikes Back » в минуту 13:11, чтобы не использовать «стандартные» генераторы случайных чисел из Python, Java и C # и вместо этого использовать криптографически безопасную версию.
Я знаю разницу между двумя версиями генераторов случайных чисел (см. Вопрос 101337 ).
- Почему rand () дает одну и ту же последовательность чисел при каждом запуске?
- Случайный взвешенный выбор
- Как создать случайную буквенно-цифровую строку?
- реализация rand ()
- Программа генерирует одинаковые случайные числа в каждом прогоне?
Но какое обоснование заключается в том, чтобы не всегда использовать защищенный генератор случайных чисел? Зачем использовать System.Random? Возможно, производительность?
- Как лаконично, переносимо и основательно семя mn19937 PRNG?
- Lua math.random не работает
- Как использовать функцию srand () с time.h?
- Как уменьшить числа из rand ()?
- Создание случайных чисел в массиве
- Сколько случайных элементов перед MD5 вызывает столкновения?
- Возьмите n случайных элементов из списка ?
- Как работают модуль и rand ()?
Скорость и намерение. Если вы генерируете случайное число и не нуждаетесь в безопасности, зачем использовать медленную криптофункцию? Вам не нужна безопасность, поэтому почему кто-то другой считает, что число может быть использовано для чего-то безопасного, когда этого не будет?
Помимо скорости и более полезного интерфейса ( NextDouble()
т. Д.), Также можно сделать повторяемую случайную последовательность, используя фиксированное начальное значение. Это очень полезно, в том числе во время тестирования.
Random gen1 = new Random(); // auto seeded by the clock Random gen2 = new Random(0); // Next(10) always yields 7,8,7,5,2,....
Прежде всего, в сообщении, которое вы связываете, речь идет только о случайных числах в целях безопасности. Поэтому он не требует, чтобы Random
был плохим для небезопасных целей.
Но я утверждаю, что это так. Реализация .net 4 Random
имеет недостатки несколькими способами. Я рекомендую использовать его только в том случае, если вы не заботитесь о качестве ваших случайных чисел. Я рекомендую использовать лучшие сторонние реализации.
Недостаток 1: посев
Серийный конструктор по умолчанию использует текущее время. Таким образом, все экземпляры Random
созданные с помощью конструктора по умолчанию в течение короткого периода времени (около 10 мс), возвращают одну и ту же последовательность. Это документировано и «по дизайну». Это особенно раздражает, если вы хотите multithreading вашего кода, так как вы не можете просто создать экземпляр Random
в начале выполнения каждого streamа.
Обходной путь должен быть особенно осторожным при использовании конструктора по умолчанию и при необходимости вручную выделении семян.
Другая проблема здесь в том, что семенное пространство довольно мало (31 бит). Поэтому, если вы создаете 50k экземпляров Random
с совершенно случайными семенами, вы, вероятно, получите одну последовательность случайных чисел дважды (из-за парадокса рождения ). Таким образом, ручное сеяние также не так просто.
Ошибка 2: распределение случайных чисел, возвращаемых Next(int maxValue)
, смещено
Существуют параметры, для которых Next(int maxValue)
явно не является однородным. Например, если вы вычислите r.Next(1431655765) % 2
вы получите 0
примерно в 2/3 образцов. (Пример кода в конце ответа.)
Недостаток 3: метод NextBytes()
неэффективен.
Стоимость байта NextBytes()
на байты NextBytes()
такая же, как и стоимость генерации целой выборки с Next()
. Из этого я подозреваю, что они действительно создают один образец за каждый байт.
Лучшая реализация с использованием 3 байтов из каждого образца будет ускорять NextBytes()
почти на 3.
Благодаря этому недостатку Random.NextBytes()
только на 25% быстрее, чем System.Security.Cryptography.RNGCryptoServiceProvider.GetBytes
на моей машине (Win7, Core i3 2600MHz).
Я уверен, что если кто-то проверит исходный / декомпилированный байт-код, они найдут еще больше недостатков, чем я обнаружил в своем анализе с помощью черного ящика.
Образцы кода
r.Next(0x55555555) % 2
сильно предвзято:
Random r = new Random(); const int mod = 2; int[] hist = new int[mod]; for(int i = 0; i < 10000000; i++) { int num = r.Next(0x55555555); int num2 = num % 2; hist[num2]++; } for(int i=0;i
Представление:
byte[] bytes=new byte[8*1024]; var cr=new System.Security.Cryptography.RNGCryptoServiceProvider(); Random r=new Random(); // Random.NextBytes for(int i=0;i<100000;i++) { r.NextBytes(bytes); } //One sample per byte for(int i=0;i<100000;i++) { for(int j=0;j>16); bytes[j+1]=(byte)(num>>8); bytes[j]=(byte)num; } //Yes I know I'm not handling the last few bytes, but that won't have a noticeable impact on performance } //Crypto for(int i=0;i<100000;i++) { cr.GetBytes(bytes); }
System.Random намного более эффективен, так как он не генерирует криптографически безопасные случайные числа.
Простой тест на моей машине, заполняющий буфер размером 4 байта случайными данными, 1,000,000 раз занимает 49 мс для Random, но 2845 мс для RNGCryptoServiceProvider. Обратите внимание: если вы увеличиваете размер буфера, который вы заполняете, разница сужается, поскольку накладные расходы для RNGCryptoServiceProvider менее актуальны.
Наиболее очевидные причины уже упоминались, так что здесь более неясным: криптографические PRNG обычно должны постоянно поддаваться «реальной» энтропии. Таким образом, если вы слишком часто используете CPRNG, вы можете истощить пул энтропии системы, который (в зависимости от реализации CPRNG) либо ослабит его (тем самым позволяя злоумышленнику предсказать его), либо он будет блокироваться при попытке заполнить его энтропийный пул (таким образом, он становится вектором атаки для атаки DoS).
В любом случае ваше приложение теперь стало вектором атаки для других, совершенно несвязанных приложений, которые, в отличие от вашего, фактически зависят от криптографических свойств CPRNG.
Это настоящая проблема реального мира, BTW, которая наблюдалась на безгласных серверах (которые, естественно, имеют довольно небольшие энтропийные пулы, потому что им не хватает энтропийных источников, таких как ввод мыши и клавиатуры) под управлением Linux, где приложения неправильно используют /dev/random
kernel CPRNG для всех видов случайных чисел, тогда как правильное поведение будет состоять в том, чтобы прочитать небольшое начальное значение из /dev/urandom
и использовать его для семени собственного PRNG.
Если вы программируете онлайн-карточную игру или лотерею, вам нужно убедиться, что последовательность почти невозможна. Однако, если вы показываете пользователей, скажем, цитату дня, производительность более важна, чем безопасность.
Это было обсуждено в некоторой степени, но в конечном итоге проблема производительности является вторичным соображением при выборе ГСЧ. Существует огромное количество RNG, а консервированный Lehmer LCG, который состоит из большинства системных RNG, не самый лучший и даже не самый быстрый. На старых, медленных системах это был отличный компромисс. Этот компромисс редко бывает актуальным в наши дни. Дело сохраняется в современных системах прежде всего потому, что A) вещь уже построена, и в этом случае нет реальной причины «изобретать колесо», а B) для того, для чего огромная масса людей будет ее использовать, это ‘достаточно хорошо’.
В конечном счете, выбор RNG сводится к соотношению риска / вознаграждения. В некоторых приложениях, например, в видеоигре, нет никакого риска. RHG Lehmer более чем достаточен и мал, краток, быстр, хорошо понят и «в коробке».
Если это приложение, например, он-лайн игра в покер или лотерея, где есть реальные призы, и реальные деньги вступают в игру в какой-то момент в уравнении, «в поле» Лемер больше не является адекватным. В 32-битной версии он имеет только 2 ^ 32 возможных допустимых состояния, прежде чем он начнет цикл в лучшем случае . В эти дни это открытая дверь для атаки грубой силы. В таком случае разработчик захочет перейти к чему-то вроде RNG Very Long Period некоторых видов и, возможно, запустит его у криптографически сильного провайдера. Это дает хороший компромисс между скоростью и безопасностью. В таком случае человек будет искать что-то вроде Mersenne Twister или нескольких рекурсивных генераторов .
Если приложение представляет собой нечто вроде передачи большого количества финансовой информации по сети, теперь существует огромный риск, и он сильно перевешивает любую возможную наgradleу. Есть все еще бронированные автомобили, потому что иногда вооруженные люди – это единственная безопасность, которая адекватна, и поверьте мне, если бы бригада специальных людей с танками, бойцами и вертолетами была финансово осуществима, это был бы метод выбора. В подобном случае использование криптографически сильного RNG имеет смысл, потому что любой уровень безопасности, который вы можете получить, это не так много, как вы хотите. Таким образом, вы возьмете столько, сколько сможете найти, а стоимость – очень отдаленная проблема второго места, либо во времени, либо в деньгах. И если это означает, что каждая случайная последовательность занимает 3 секунды для генерации на очень мощном компьютере, вы будете ждать 3 секунды, потому что в схеме вещей это тривиальная стоимость.
Обратите внимание, что class System.Random в C # неверно закодирован, поэтому его следует избегать.
https://connect.microsoft.com/VisualStudio/feedback/details/634761/system-random-serious-bug#tabs
Не всем нужны криптографически безопасные случайные числа, и они могут извлечь выгоду из более быстрого простого ппнга. Возможно, более важно то, что вы можете контролировать последовательность для номеров System.Random.
В симуляции с использованием случайных чисел, которые вы можете захотеть воссоздать, вы повторно запускаете симуляцию с тем же семенем. Это может быть полезно для отслеживания ошибок, когда вы хотите также восстановить данный сценарий с ошибками – запуск вашей программы с помощью той же последовательности случайных чисел, которая разбила программу.
Если мне не нужна безопасность, т. Е. Я просто хочу, чтобы относительно неопределенное значение не было криптографически сильным, у Random есть гораздо более простой интерфейс для использования.
Различные потребности требуют разных ГСЧ. Для криптования вы хотите, чтобы ваши случайные числа были как можно более случайными. Для моделирования Монте-Карло вы хотите, чтобы они заполнили пространство равномерно и чтобы запустить RNG из известного состояния.
Random
не является генератором случайных чисел, он является детерминированным генератором псевдослучайных последовательностей, который берет свое название по историческим причинам.
Причиной использования System.Random
является то, что вы хотите получить эти свойства, а именно детерминированную последовательность, которая гарантирована для получения той же последовательности результатов при инициализации с одним и тем же семенем.
Если вы хотите улучшить «случайность», не жертвуя интерфейсом, вы можете наследовать от System.Random
переопределяя несколько методов.
Зачем вам нужна детерминированная последовательность
Одна из причин иметь детерминированную последовательность, а не истинную случайность, состоит в том, что она повторяема.
Например, если вы используете численное моделирование, вы можете инициализировать последовательность с (истинным) случайным числом и записать, какой номер был использован .
Затем, если вы хотите повторить ту же симуляцию, например, для целей отладки, вы можете сделать это, вместо этого инициализируя последовательность с записанным значением.
Зачем вам нужна эта конкретная, не очень хорошая последовательность
Единственная причина, по которой я могу думать, – это обратная совместимость с существующим кодом, который использует этот class.
Короче говоря, если вы хотите улучшить последовательность, не изменяя остальную часть своего кода, продолжайте.
Я написал игру (Crystal Sliders на iPhone: Here ), которая выложила бы «случайную» серию драгоценных камней (изображений) на карте, и вы поменяли бы карту, как вы ее хотели, и выберите их, и они ушли. – Как и в Bejeweled. Я использовал Random (), и он был засеян числом 100ns тиков с момента загрузки телефона, довольно случайным семенем.
Мне показалось удивительным, что это создаст игры, которые были почти идентичны друг другу – из 90 или около того драгоценных камней, из двух цветов, я бы получил два ТОЧНО то же самое, за исключением от 1 до 3 драгоценных камней! Если вы переворачиваете 90 монет и получаете одинаковые рисунки, за исключением 1-3 флип, это очень маловероятно! У меня есть несколько снимков экрана, которые показывают их одинаково. Я был шокирован тем, насколько плох System.Random () был! Я предположил, что я ДОЛЖЕН написать что-то ужасное в своем коде и неправильно использовал его. Я был неправ, хотя это был генератор.
В качестве эксперимента – и окончательного решения, я вернулся к генератору случайных чисел, который я использовал с 1985 года или около того, – что лучше всего. Это быстрее, имеет период 1.3 * 10 ^ 154 (2 ^ 521), прежде чем он повторится. Первоначальный алгоритм был засеян 16-разрядным номером, но я изменил его на 32-битное число и улучшил начальное посев.
Оригинальный находится здесь:
ftp://ftp.grnet.gr/pub/lang/algorithms/c/jpl-c/random.c
За эти годы я бросил каждый случайный номер теста, о котором я мог подумать, и он прошел мимо всех них. Я не ожидаю, что он имеет значение как криптографический, но он возвращает число так же быстро, как «return * p ++;» пока он не закончится из 521 бит, а затем он быстро выполняет бит над битами для создания новых случайных.
Я создал оболочку C #, назвав ее JPLRandom (), реализовал тот же интерфейс, что и Random (), и изменил все места, где я назвал их в коде.
Разница была VASTLY лучше – OMG я был поражен – не должно быть никакого способа, я мог бы сказать, просто глядя на экраны из 90 или около того драгоценных камней в шаблоне, но я сделал экстренный выпуск моей игры после этого.
И я никогда не буду использовать System.Random () для чего-либо снова. Я ОСНАЩЕН, что их версия сдувается чем-то, кому сейчас 30 лет!
-Траддерхут-игры