как узнать какое число выпадет в генераторе случайных чисел
Подробно о генераторах случайных и псевдослучайных чисел
Введение
Как отличить случайную последовательность чисел от неслучайной?
Чуть более сложный пример или число Пи
Последовательность цифры в числе Пи считается случайной. Пусть генератор основывается на выводе бит представления числа Пи, начиная с какой-то неизвестной точки. Такой генератор, возможно и пройдет «тест на следующий бит», так как ПИ, видимо, является случайной последовательностью. Однако этот подход не является критографически надежным — если криптоаналитик определит, какой бит числа Пи используется в данный момент, он сможет вычислить и все предшествующие и последующие биты.
Данный пример накладывает ещё одно ограничение на генераторы случайных чисел. Криптоаналитик не должен иметь возможности предсказать работу генератора случайных чисел.
Отличие генератора псевдослучайных чисел (ГПСЧ) от генератора случайных чисел (ГСЧ)
Источники энтропии используются для накопления энтропии с последующим получением из неё начального значения (initial value, seed), необходимого генераторам случайных чисел (ГСЧ) для формирования случайных чисел. ГПСЧ использует единственное начальное значение, откуда и следует его псевдослучайность, а ГСЧ всегда формирует случайное число, имея в начале высококачественную случайную величину, предоставленную различными источниками энтропии.
Энтропия – это мера беспорядка. Информационная энтропия — мера неопределённости или непредсказуемости информации.
Можно сказать, что ГСЧ = ГПСЧ + источник энтропии.
Уязвимости ГПСЧ
Линейный конгруэнтный ГПСЧ (LCPRNG)
Распространённый метод для генерации псевдослучайных чисел, не обладающий криптографической стойкостью. Линейный конгруэнтный метод заключается в вычислении членов линейной рекуррентной последовательности по модулю некоторого натурального числа m, задаваемой следующей формулой:
где a (multiplier), c (addend), m (mask) — некоторые целочисленные коэффициенты. Получаемая последовательность зависит от выбора стартового числа (seed) X0 и при разных его значениях получаются различные последовательности случайных чисел.
Для выбора коэффициентов имеются свойства позволяющие максимизировать длину периода(максимальная длина равна m), то есть момент, с которого генератор зациклится [1].
Пусть генератор выдал несколько случайных чисел X0, X1, X2, X3. Получается система уравнений
Решив эту систему, можно определить коэффициенты a, c, m. Как утверждает википедия [8], эта система имеет решение, но решить самостоятельно или найти решение не получилось. Буду очень признателен за любую помощь в этом направлении.
Предсказание результатов линейно-конгруэнтного метода
Основным алгоритмом предсказания чисел для линейно-конгруэнтного метода является Plumstead’s — алгоритм, реализацию, которого можно найти здесь [4](есть онлайн запуск) и здесь [5]. Описание алгоритма можно найти в [9].
Простая реализация конгруэнтного метода на Java.
Отправив 20 чисел на сайт [4], можно с большой вероятностью получить следующие. Чем больше чисел, тем больше вероятность.
Взлом встроенного генератора случайных чисел в Java
Многие языки программирования, например C(rand), C++(rand) и Java используют LСPRNG. Рассмотрим, как можно провести взлом на примере java.utils.Random. Зайдя в исходный код (jdk1.7) данного класса можно увидеть используемые константы
Метод java.utils.Randon.nextInt() выглядит следующим образом (здесь bits == 32)
Результатом является nextseed сдвинутый вправо на 48-32=16 бит. Данный метод называется truncated-bits, особенно неприятен при black-box, приходится добавлять ещё один цикл в brute-force. Взлом будет происходить методом грубой силы(brute-force).
Пусть мы знаем два подряд сгенерированных числа x1 и x2. Тогда необходимо перебрать 2^16 = 65536 вариантов oldseed и применять к x1 формулу:
до тех пор, пока она не станет равной x2. Код для brute-force может выглядеть так
Вывод данной программы будет примерно таким:
Несложно понять, что мы нашли не самый первый seed, а seed, используемый при генерации второго числа. Для нахождения первоначального seed необходимо провести несколько операций, которые Java использовала для преобразования seed, в обратном порядке.
И теперь в исходном коде заменим
crackingSeed.set(seed);
на
crackingSeed.set(getPreviousSeed(seed));
И всё, мы успешно взломали ГПСЧ в Java.
Взлом ГПСЧ Mersenne twister в PHP
Рассмотрим ещё один не криптостойкий алгоритм генерации псевдослучайных чисел Mersenne Twister. Основные преимущества алгоритма — это скорость генерации и огромный период 2^19937 − 1, На этот раз будем анализировать реализацию алгоритма mt_srand() и mt_rand() в исходном коде php версии 5.4.6.
Можно заметить, что php_mt_reload вызывается при инициализации и после вызова php_mt_rand 624 раза. Начнем взлом с конца, обратим трансформации в конце функции php_mt_rand(). Рассмотрим (s1 ^ (s1 >> 18)). В бинарном представление операция выглядит так:
10110111010111100111111001110010 s1
00000000000000000010110111010111100111111001110010 s1 >> 18
10110111010111100101001110100101 s1 ^ (s1 >> 18)
Видно, что первые 18 бит (выделены жирным) остались без изменений.
Напишем две функции для инвертирования битового сдвига и xor
Тогда код для инвертирования последних строк функции php_mt_rand() будет выглядеть так
Если у нас есть 624 последовательных числа сгенерированных Mersenne Twister, то применив этот алгоритм для этих последовательных чисел, мы получим полное состояние Mersenne Twister, и сможем легко определить каждое последующее значение, запустив php_mt_reload для известного набора значений.
Область для взлома
Если вы думаете, что уже нечего ломать, то Вы глубоко заблуждаетесь. Одним из интересных направлений является генератор случайных чисел Adobe Flash(Action Script 3.0). Его особенностью является закрытость исходного кода и отсутствие задания seed’а. Основной интерес к нему, это использование во многих онлайн-казино и онлайн-покере.
Есть много последовательностей чисел, начиная от курса доллара и заканчивая количеством времени проведенным в пробке каждый день. И найти закономерность в таких данных очень не простая задача.
Задание распределения для генератора псевдослучайных чисел
Для любой случайной величины можно задать распределение. Перенося на пример с картами, можно сделать так, чтобы тузы выпадали чаще, чем девятки. Далее представлены несколько примеров для треугольного распределения и экспоненциального распределения.
Треугольное распределение
Приведем пример генерации случайной величины с треугольным распределением [7] на языке C99.
Экспоненциальное распределение
Тесты ГПСЧ
Некоторые разработчики считают, что если они скроют используемый ими метод генерации или придумают свой, то этого достаточно для защиты. Это очень распространённое заблуждение. Следует помнить, что есть специальные методы и приемы для поиска зависимостей в последовательности чисел.
Одним из известных тестов является тест на следующий бит — тест, служащий для проверки генераторов псевдослучайных чисел на криптостойкость. Тест гласит, что не должно существовать полиномиального алгоритма, который, зная первые k битов случайной последовательности, сможет предсказать k+1 бит с вероятностью большей ½.
В теории криптографии отдельной проблемой является определение того, насколько последовательность чисел или бит, сгенерированных генератором, является случайной. Как правило, для этой цели используются различные статистические тесты, такие как DIEHARD или NIST. Эндрю Яо в 1982 году доказал, что генератор, прошедший «тест на следующий бит», пройдет и любые другие статистические тесты на случайность, выполнимые за полиномиальное время.
В интернете [10] можно пройти тесты DIEHARD и множество других, чтобы определить критостойкость алгоритма.
Каким образом можно угадать любое случайное число, которое было сгенериновано сайтом?
Раньше в качестве «генераторов случайных чисел» часто использовали компьютерные алгоритмы. Это не совсем корректно, потому что результаты, хоть и с большим трудом и с помощью вычислительных машин, было возможно предсказать. Так, например, один умник из США смог обмануть систему и просчитать алгоритм какой-то игры на деньги.
В настоящее время среди генераторов случайных чисел можно выделить два типа: генераторы псевдослучайных чисел, и генераторов настоящих случайных чисел (названия импровизированные).
Псевдослучайные числа, как видно из названия, не совсем случайны: для их генерации не используются физические источники. Их генерируют с помощью сугубо математического алгоритма. Например, берётся некоторое число (допустим то, которое выпало на кубике), затем перемножается с ещё одним числом (выпавшем при очередном подбрасывании кубика), затем возводится в куб, из результата вычитается ещё одно число, выпавшее на кубике, из получившегося числа извлекается корень третьей степени и из результата берется одиннадцатая цифра после запятой. На первый взгляд кажется, что такое число невозможно предсказать, но на самом деле, если идеально рассчитать траекторию кубика, то можно предсказать и результат. Именно такой алгоритм и описывает работу генераторов псевдослучайных чисел, только вместо кубика используется, например, количество пользователей сайта в данный момент времени.
С генераторами настоящих случайных чисел всё несколько сложнее. Для их работы необходимы физические источники, так как результаты должны быть детерминированы. Идеальным физическим источником является квантовая система: насколько известно современной науке, поведение квантовой системы абсолютно непредсказуемо и хаотично. Проблема такого источника, в первую очередь, заключается в дороговизне. Поэтому большинство сайтов, предоставляющих услуги генерации случайных чисел, в своих целях используют молнию: предсказать траекторию и место удара молнии практически невозможно. При большом желании и наличии мощных вычислительных машин, можно только распределить вероятность попадания молнии в то или иное место.
Таким образом, отвечая на вопрос, можно сказать, что для предсказания «случайного» числа, нужно понимать, с каким сайтом вы имеете дело. Если это очень старый сайт, то есть вариант понаблюдать за его поведением и результатами, и вывести алгоритм. Если это генератор псевдослучайных чисел, то можно попробовать понять, откуда сайт берёт первоначальные числа (как в примере с кубиком). Часто это количество запросов в каком-либо поисковике за единицу времени, либо количество посетителей сайта. Если речь о генераторе настоящих случайных чисел, то здесь с огромными усилиями можно лишь определить наиболее вероятный исход.
Как устроены генераторы чисел?
Чтобы понять, как действует генератор случайных чисел, нужно разобраться в его устройстве и в том, на чем он основывается. Генератор чисел создает определенный порядок абсолютно независящих друг от друга чисел, основываясь на определенных параметрах рассматриваемого элемента, процесса, действия и так далее. В связи с тем, что речь идет о случайных числах, то и параметры изменяются хаотично.
Существует много распространенных и достаточно примитивных примеров, на которых можно рассмотреть его устройство: подкидывание любой монеты, бросок игрального кубика и так далее.
Принцип устройства
В работе генератора случайных чисел активно задействован ряд теорий (в частности, теория хаоса). С этим и связана абсолютная непредсказуемость выпадения того или иного шарика в лототроне, определенной грани игральной кости, загаданной до броска стороны монетки. Однако стоит отметить, что и в науке такой аппарат играет важную роль: в первую очередь для статистических исследований.
Что такое случайность и как её создать?
Основным условием, крайне важным для соблюдения правильных и честных принципов работы системы ГСЧ, является абсолютно равная вероятность на выпадение любого из возможных чисел, которые только могут выпасть в созданной системе. При этом соблюдается полная независимость от того фактора, какие еще числа выпали до или после этого.
Это можно объяснить более простым языком: в генераторе истинно случайных чисел просто нельзя выстроить порядок и зависимость выпадающих цифр. Допустим, если вы бросаете первый раз шестигранную игральную кость, то у вас может выпасть абсолютно любое число от 1 до 6 с одинаковой вероятностью 16,(6)%. И независимо от того, какая цифра выпала, она с аналогичной вероятностью может повторно выпасть при втором, сотом, тысячном бросках.
Псевдослучайность
Также существует генератор псевдослучайных последовательностей. Несмотря на то, что на первый взгляд в нем тоже очевидно отсутствие закономерностей, подобный генератор с конечным числом внутренних состояний повторится, хотя это может произойти после очень длительной цепочки чисел.
Номера в лотереях, которые чаще других приносят удачу
Принцип лотереи устроен таким образов, что выигрышная комбинация может состоять из любых случайных чисел. Но, как показывает практика, некоторые номера по необъяснимым причинам чаще остальных присутствуют в выигрышных комбинациях. Этот феномен заинтересовал специалистов, и они провели исследование изучающее поведение разных номеров. За основу была взята лотерейная статистика практически со всего земного шара.
Рейтинг удачных чисел
Эксперимент проводилось в 2017 году. Специалисты сравнили данные большой выборки разнообразных лотерей и определили номера, которые позволяли выигрывать чаще всего. В рамках анализа проанализировано пятнадцать самых популярных лотерей, в том числе Ирландская лотерея, игра Euromillions, PowerBall и многие другие представители игорной индустрии. В результате кропотливой работы был сформирован перечень самых удачливых номеров.
Перечень удачных номеров
Перечень неудачных номеров
Анти-рейтинг удачливых чисел
Многие люди, прочитав эту статью, могут сделать вывод, что не надо смотреть на перечисленные номера, так как они реже остальных встречаются в выигрышных комбинациях. Но развивая эту теорию дальше, можно выявить интересную закономерность. Выбирая номера, которые остальные игроки игнорируют, вы повышаете размер вероятного джекпота при равных шансах на победу. Ведь в случае выигрыша вам не придется делить его с другими участниками.
Система для выбора номеров в лотерейных билетах
Теперь читатели знают список самых удачливых и самых неудачных номеров в лотерейных билетах. Какие же выбрать? Руководствоваться при выборе этой информацией или опираться на свои собственные счастливые цифры?
Быстрота и удобство онлайн-лотерей позволят вам опробовать обе техники и сделать вывод самостоятельно. По данным исследования чаще всего в выигрышных комбинациях присутствуют числа 16, 22, 3, 6, а также 28 и 37. Однако многие игроки составляют комбинации исходя из собственно разработанных стратегий. И такой подход тоже работает. Большое количество лотерейных победителей доказали это на собственном примере.
Игрок из Канады воспользовался необычным способом выбора чисел и стал обладателем внушительного джекпота, который составил шестьдесят миллионов долларов. На протяжении тридцати лет он отмечал в лотерейных билетах одни и те же цифры. И наконец-то такое упорство было вознаграждено, канадец стал долларовым миллионером. При этом Ричарду Люстингу не только удалось победить. Он стал обладателем одного из самых больших выигрышей за время существования лотереи USA Powerball. На этом удача его не покинула, он входил в число победителей несколько раз.
Любитель лотерейных розыгрышей стал автором книги, в которой он подробно рассказывает о выбранной стратегии и дает практические советы, как выиграть в лотерею другим игрокам. Такой предприимчивости можно позавидовать.
Даже несмотря на исследования и советы опытных игроков, выбор чисел всегда остается за вами. Однако если вы нуждаетесь в подсказке, то можно попробовать поставить на счастливые номера. Поучаствуйте в лотерейном розыгрыше прямо сейчас и проверьте уровень своего везения на практике.
Разбор алгоритмов генерации псевдослучайных чисел
Я работаю программистом в игровой студии IT Territory, а с недавних пор перешел на направление экспериментальных проектов, где мы проверяем на прототипах различные геймплейные гипотезы. И работая над одним из прототипов мы столкнулись с задачей генерации случайных чисел. Я хотел бы поделиться с вами полученным опытом: расскажу о псевдогенераторах случайных чисел, об альтернативе в виде хеш-функции, покажу, как её можно оптимизировать, и опишу комбинированные подходы, которые мы применяли в проекте.
Случайными числами пользовались с самого зарождения математики. Сегодня их применяют во всевозможных научных изысканиях, при проверке математических теорем, в статистике и т.д. Также случайные числа широко используются в игровой индустрии для генерирования 3D-моделей, текстур и целых миров. Их применяют для создания вариативности поведения в играх и приложениях.
Есть разные способы получения случайных чисел. Самый простой и понятный — это словари: мы предварительно собираем и сохраняем набор чисел и по мере надобности берём их по очереди.
К первым техническим способам получения случайных чисел можно отнести различные генераторы с использованием энтропии. Это устройства, основанные на физических свойствах, например, емкости конденсатора, шуме радиоволн, длительности нажатия на кнопку и так далее. Хоть такие числа действительно будут случайными, у таких способов отсутствует важный критерий — повторяемость.
ERNIE 1 — аппаратный генератор случайных чисел, созданный в 1957 году
Сегодня мы с вами поговорим о генераторах псевдослучайных чисел — вычисляемых функциях. К ним предъявляются следующие требования:
Длинный период. Любой генератор рано или поздно начинает повторяться, и чем позже это случится, тем лучше, тем непредсказуемее будет результат.
Портируемость алгоритма на различные системы.
Скорость получения последовательности. Чем быстрее, тем лучше.
Повторяемость результата. Это очень важный показатель. От него зависят все компьютерные игры, которые используют генераторы миров и различные системы с аналогичной функциональностью. Воспроизводимость даёт нам общий контент для всех, то есть мы можем генерировать на отдельных клиентах одинаковое содержимое. Также мы можем генерировать контент на лету в зависимости от входных данных, например, от местоположения игрока в мире. Ещё повторяемость случайных чисел используется для сохранения конкретного контента в виде зерна. То есть мы можем держать у себя только какое-то число или массив чисел, на основе которых будут генерироваться нужные нам параметры для заранее отобранного контента.
Зерно
Зерно — это основа генерирования. Оно представляет собой число или вектор чисел, который мы отправляем при инициализации генератора.
Решить эту проблему можно будет с помощью разделения одного генератора на несколько отдельных.
То есть берём несколько генераторов и задаём им разные зёрна. Но тут мы можем столкнуться со второй проблемой: нельзя гарантировать случайность i-тых элементов разных последовательностей с разными зёрнами.
На иллюстрации изображён результат генерирования нулевого элемента последовательности с помощью стандартной библиотекой C#. Мы постепенно меняли зерно от 0 до N.
Качество генератора
Предлагаю оценивать качество генератора с помощью изображений разного типа. Первый тип — это просто сгенерированная последовательность, который мы визуализируем с помощью первых трёх байтов полученного числа, конвертированных в RGB-представление.
Второй тип изображений — это пространственная интерпретация сгенерированной последовательности. Мы берём первые два бита числа (Х и Y), затем считаем количество попаданий в заданные точки и при визуализации вычитаем из 1 отношение количества попаданий в конкретный пиксель к максимальному количеству попаданий в какой-то другой пиксель. Черные пиксели — это точка, куда мы попадаем чаще всего, а белые — куда мы либо почти, либо совсем не попали.
Сравнение генераторов
Стандартные средства C#
Ниже я сравнил стандартный генератор из библиотеки С# и линейную последовательность. Первый столбец слева — это случайная последовательность от 0 до N в рамках одного зерна. В центре вверху показаны нулевые элементы случайных последовательностей при разных зёрнах от 0 до N. Вторая линейная последовательность — это числа от 0 до N, которые я визуализировал нашим алгоритмом.
В рамках одного зерна генератор действительно создаёт случайное число. Но при этом для i-тых элементов последовательностей с разным зерном прослеживается паттерн, который схож с паттерном линейной последовательности.
Линейный конгруэнтный генератор (LCG)
Давайте рассмотрим другие алгоритмы. Деррик Генри в 1949 году создал линейный конгруэнтный генератор, который подбирает некие коэффициенты и с их помощью выполняет возведения в степень со сдвигом.
При генерировании с одним зерном паттерн нигде не образуется. Но при использовании i-тых элементов в последовательностях с различными зёрнами паттерн начинает прослеживаться. Причём его вид будет зависеть исключительно от коэффициентов, которые мы подобрали для генератора. Например, есть частный случай линейного конгруэнтного генератора — Randu.
XorShift
Давайте теперь посмотрим на более свежую разработку — XorShift. Этот алгоритм просто выполняет операцию Xor и сдвигает байт в несколько раз. У него тоже будет прослеживаться паттерн для i-тых элементов последовательностей.
Вихрь Мерсенна
Неужели не существует генераторов без паттерна? Такой генератор есть — это вихрь Мерсенна. У этого алгоритма очень большой период, из-за чего появление паттерна на некотором количестве чисел физически невозможно. Однако и сложность этого алгоритма достаточно велика, в двух словах его не объяснить.
Unity — Random
Из других разработок стоит упомянуть генератор от компании Unity — Random, который используется в наборе стандартных библиотек для работы с Unity. При использовании первых элементов последовательности для разных зёрен у него будет прослеживаться паттерн, но при увеличении индекса паттерн исчезает и получается действительно случайная последовательность.
Перемешанный конгруэнтный генератор (PCG)
Противоположностью юнитёвского Random’a является перемешанный конгруэнтный генератор. Его особенность в том, что для первых элементов с различными зёрнами отсутствует ярко выраженный паттерн. Но при увеличении индекса он всё же возникает.
Длительность последовательного генерирования
Это важная характеристика генераторов. В таблице приведена длительность для алгоритмов в миллисекундах. Замеры проводились на моём MacBook Pro 2019 года.
0 seed 0..n
100 seed 0..n
Вихрь Мерсенна работает дольше всего, но даёт качественный результат. Стандартный генератор Random из библиотеки C# подходит для задач, в которых случайность вторична и не имеет какой-то значимой роли, то есть его можно использовать в рамках одного зерна. LCG (линейный конгруэнтный генератор) — это уже более серьёзный алгоритм, но требуется время на подбор нужных коэффициентов, чтобы получить адекватный паттерн. XorShift — самый быстрый алгоритм из всех рассмотренных. Его можно использовать там, где нужно быстро получить случайное значение, но помните про ярко выраженный паттерн с повторяющимся значением. Unity Random и PCG (перемешанный конгруэнтный генератор) сопоставимы по длительности работы, поэтому в разных ситуациях мы можем менять их местами: для длительных последовательностей использовать Unity, а для коротких — PCG.
Альтернатива генераторам — хеш-функции
Хеш-функции (функции свёртки) по определённому алгоритму преобразуют массив входных данных произвольной длины в строку заданной длины. Они позволяют быстрее искать данные, это свойство используется в хеш-таблицах. Также для хеш-функций характерна равномерность распределения, так называемый лавинный эффект. Это означает, что изменение малого количества битов во входном тексте приведёт к лавинообразному и сильному изменению значений выходного массива битов. То есть все выходные биты зависят от каждого входного бита.
Требования к генераторам на основе хеш-функций предъявляются те же самые, что и к простым генераторам, кроме длительности получения последовательности. Дело в том, что такому генератору можно отправить на вход одновременно зерно и требуемое состояние, потому что хеш-функции принимают на вход массивы данных.
Вот пример использования хеш-функции: можно либо создать конкретный класс, отправить туда зерно и постепенно запрашивать только конкретные состояния, либо написать статичную функцию, и отправить туда сразу и зерно, и конкретное состояние. Слева показан алгоритм работы MD5 из стандартной библиотеки C#.
Сделать генератор на основе хеш-функции можно так. Непосредственно при инициализации генератора задаём зерно, увеличиваем счётчик на 1 при запросе следующего значения и выводим результат хеша по зерну и счётчику.
Одни из самых популярных хеш-функций — это MurMur3 и WangHash.
MurMur3 не создаёт паттернов при использованиии i-тых элементов разных последовательностей при разных зёрнах. У WangHash статистические показатели образуют заметный паттерн. Но любую функцию можно прогнать через себя два раза и получить улучшенные показатели, как это показано в правом крайнем столбце WangDoubleHash.
Также сегодня активно развивается и набирает популярность алгоритм xxHash.
Забегая вперёд, скажу, что мы выбрали этот генератор для наших проектов и активно его используем.
Длительность последовательного генерирования у всех хеш-функций примерно одинакова. Однако у MD5 эта характеристика заметно отличается, но не потому, что алгоритм плохой, а потому что в стандартной реализации MD5 много разных состояний, которые влияют на быстродействие алгоритма.