Почему случайные числа в компьютере на самом деле не случайны
Компьютер — машина абсолютной предсказуемости: одни и те же входные данные всегда дают один и тот же результат. Откуда же тогда берётся random()? Спойлер: это умная подделка, и иногда подделка опасная.
Детерминированная машина в принципе не может выдать настоящую случайность — она может лишь блестяще её имитировать.
То, что вы получаете из обычного random(), — это псевдослучайность: длинная, заранее определённая последовательность, которая лишь притворяется хаосом.
Парадокс в самой основе
Компьютер устроен так, чтобы быть предсказуемым: одна и та же программа на одних и тех же данных даёт идентичный результат — иначе на него нельзя было бы положиться. Но настоящая случайность по определению непредсказуема. Получается противоречие: как детерминированное устройство выдаёт «случай»? Ответ — никак. Оно его подделывает.
Как работает генератор-фокусник
Большинство «случайных» чисел выдаёт псевдослучайный генератор (PRNG). Это формула, которая из текущего числа считает следующее. Вот игрушечный пример — линейный конгруэнтный метод:
seed = 42
def rand():
global seed
seed = (1103515245 * seed + 12345) % (2**31)
return seed
for _ in range(5):
print(rand())Числа на выходе выглядят как попало — скачут, не повторяются. Но это полностью детерминированная цепочка: каждое следующее число однозначно вытекает из предыдущего. Никакого хаоса, чистая арифметика.
Магическое слово seed
Стартовое число называется seed (зерно). И вот ключевой момент: один и тот же seed всегда порождает одну и ту же «случайную» последовательность. Именно поэтому в играх существуют сиды миров — введёте тот же сид в Minecraft, получите тот же мир. Это не баг, это суть PRNG.
Чтобы числа казались разными при каждом запуске, программы берут seed из чего-то меняющегося — например, из текущего времени в миллисекундах. Но если злоумышленник угадает seed, он предскажет всю последовательность вперёд и назад.
Когда подделка становится опасной
Для перемешивания плейлиста псевдослучайности достаточно. Но в безопасности она убийственна. Реальные истории:
- Онлайн-казино, где игроки вычислили seed PRNG и предсказывали карты.
- Криптокошельки, сгенерировавшие ключи на слабом генераторе, — их приватные ключи восстановили перебором seed и увели деньги.
- Уязвимость в Debian (2008): из-за ошибки seed брался лишь из крошечного диапазона, и тысячи SSH-ключей оказались предсказуемы.
Если будущее ваших «случайных» чисел можно предугадать, вся криптография рушится: пароли, токены, ключи шифрования держатся на непредсказуемости.
Где взять настоящую случайность
Раз арифметика не даёт истинного хаоса, его берут из физического мира — из энтропии:
| Тайминги | Микрозадержки между нажатиями клавиш, движениями мыши, приходом пакетов. |
| Шум железа | Тепловой шум транзисторов, джиттер тактовых генераторов. |
| Физика | Радиоактивный распад, квантовые эффекты — принципиально непредсказуемы. |
Операционные системы собирают эту энтропию в общий «пул» и выдают через специальный интерфейс. В Python для безопасности нужен не random, а модуль secrets; в вебе — crypto.getRandomValues, а не Math.random().
Вывод
Запомните разницу: для игр, симуляций и перемешивания — обычный PRNG отлично подходит, он быстрый и воспроизводимый. Для всего, что связано с безопасностью, — только криптостойкий источник с настоящей энтропией. Компьютер сам по себе случайности не создаёт; он либо честно имитирует её формулой, либо подсматривает хаос у реального мира. Перепутать эти два инструмента — классическая и дорогая ошибка.