Обновлено:
Код случайных чисел
Компьютеры – устройства детерминированные, и «настоящую» случайность получить сложно. В программировании для этого используют алгоритмы, называемые генераторами псевдослучайных чисел (ГПСЧ). Они выдают последовательность чисел, которая лишь статистически похожа на случайную.
Если вам нужно быстро получить случайное значение, можно воспользоваться готовым инструментом ниже.
Основные алгоритмы генерации
Выбор алгоритма зависит от требований к качеству случайности и скорости работы. В стандартных библиотеках языков программирования обычно реализованы проверенные временем методы.
Линейный конгруэнтный метод (LCG)
Один из самых старых и простых алгоритмов. Формула выглядит так: X[i] = (X[i-1] * A + B) % M.
- Скорость: Очень высокая.
- Качество: Низкое. Период (длина уникальной последовательности) ограничен модулем M.
- Применение: Старые реализации
rand()в C/C++, простые игры. Для серьёзных задач не подходит из-за предсказуемости и корреляции между соседними числами.
Вихрь Мерсена (Mersenne Twister)
Золотой стандарт для некриптографических задач. В C++ он реализован как std::mt19937.
- Скорость: Высокая, немного медленнее LCG.
- Качество: Очень высокое. Период огромен – 2¹⁹⁹³⁷−1.
- Особенности: Требует много памяти для внутреннего состояния (около 2,5 Кбайт). Это наиболее рекомендуемый ГПСЧ для общего назначения coding.likeflow.ru.
PCG (Permuted Congruential Generator)
Современное семейство генераторов, которое позиционируется как улучшенная версия LCG. Оно сложнее для предсказания, чем многие известные генераторы, но, как и Вихрь Мерсена, не является криптографически стойким coding.likeflow.ru.
Как написать код случайных чисел на C++
Современный C++ (начиная с C++11) отказывается от устаревших rand() и srand() в пользу заголовка <random>. Он позволяет гибко настраивать генератор и распределение вероятностей.
Инициализация (Seeding)
Все ГПСЧ детерминированы: при одном и том же начальном состоянии (seed) они выдадут одну и ту же последовательность. Чтобы получать разные числа при каждом запуске, нужно инициализировать генератор уникальными данными.
Лучший способ – использовать std::seed_seq, объединяющую несколько источников энтропии (время, id потока, аппаратный шум):
#include <random>
#include <chrono>
#include <thread>
int main() {
// Сбор энтропии из разных источников
uint64_t now1 = std::chrono::high_resolution_clock::now().time_since_epoch().count();
uint64_t now2 = std::chrono::system_clock::now().time_since_epoch().count();
uint64_t thread_id = std::hash<std::thread::id>{}(std::this_thread::get_id());
std::random_device rd;
uint64_t entropy = rd();
// Инициализация генератора
std::seed_seq seeds{now1, now2, thread_id, entropy};
std::mt19937 generator(seeds);
// Использование generator()...
}
Работа с распределениями
Генератор выдаетraw числа (обычно в большом диапазоне). Чтобы получить число в нужном диапазоне (например, от 1 до 6) или с определенным законом распределения (нормальным, равномерным), используются классы распределений.
Важно: не создавайте объект распределения заново на каждом шаге цикла, так как у некоторых распределений есть внутреннее состояние.
std::mt19937 generator(std::random_device{}());
std::uniform_int_distribution<int> distribution(1, 100); // Числа от 1 до 100
for (int i = 0; i < 10; ++i) {
std::cout << distribution(generator) << " ";
}
Реализация на JavaScript
В JavaScript нет встроенного средства для задания seed у Math.random(), но часто возникает задача создать генератор, который выдает одинаковые значения при нескольких запусках с одним и тем же начальным числом. Это полезно для тестирования или процедурной генерации контента.
Простейший вариант – реализация линейного конгруэнтного метода или использование модульной арифметики. Пример функции, которая принимает seed и возвращает число от 0 до 9:
function simpleRandom(seed, index) {
const num = 2147483647; // Большое простое число (Mersenne prime)
// Возводим основу (index + константа) в степень seed и берем остаток
const result = Math.pow(index + 2, seed) % num;
return Math.abs(result % 10); // Оставляем цифру от 0 до 9
}
const seed = 42;
for (let i = 0; i < 5; i++) {
console.log(simpleRandom(seed, i));
}
Такой подход гарантирует повторяемость последовательности, если seed не меняется coding.likeflow.ru.
std::random_device и аппаратная случайность
Помимо псевдослучайных алгоритмов, существует std::random_device. Он запрашивает случайные данные у операционной системы или процессора.
- Linux/macOS: Читает из
/dev/urandom(источник энтропии ядра). - Windows: Вызывает
RtlGenRandom. - Процессоры: Может использовать инструкцию
RDRAND.
Однако полагаться на random_device как основной генератор рискованно: на некоторых платформах (например, MinGW) он может быть реализован как обычный ГПСЧ и выдавать предсказуемые числа. К тому же, он работает медленнее математических алгоритмов coding.likeflow.ru.
Безопасность и предсказуемость
Ни std::mt19937, ни линейный конгруэнтный метод не подходят для задач информационной безопасности (генерация ключей, токенов, солей для паролей). Зная состояние Вихря Мерсена (всего 624 числа), можно восстановить всю последовательность.
Для шифрования используйте специализированные библиотеки, обращающиеся к криптографически стойким генераторам (CSPRNG), которые постоянно обновляют состояние энтропии.
Статья носит ознакомительный характер и не является руководством по созданию криптографических систем.
Часто задаваемые вопросы
Почему при каждом запуске программы случайные числа повторяются?
Это происходит из-за отсутствия или одинаковой инициализации генератора (seed). Если_seed_ не задан или фиксирован, алгоритм выдает одну и ту же последовательность. Используйте текущее время или аппаратный источник энтропии для уникальности.
Можно ли использовать стандартный rand() для серьёзных задач?
Не рекомендуется. Функция rand() часто реализована через линейный конгруэнтный метод, который имеет короткий период и низкое качество случайности. Лучше использовать std::mt19937 (Вихрь Мерсена) или современные аналоги.
Подходят ли эти генераторы для шифрования и паролей?
Нет, стандартные ГПСЧ (псевдослучайные генераторы) не являются криптографически стойкими. Их последовательность можно предсказать, зная предыдущие значения. Для защиты данных нужны специализированные криптографические генераторы.
Что такое std::random_device и когда его применять?
Это интерфейс к аппаратному источнику энтропии (например, RDRAND или /dev/urandom). Его используют для инициализации других генераторов (seeding), но не для основной генерации, так как он может работать медленно или быть недоступен на некоторых платформах.
Похожие калькуляторы и статьи
- Массив случайными числами: как создать в JavaScript
- Заполнить случайными числами: онлайн и в коде
- Что такое рандомное число: генераторы, алгоритмы и применение
- Рандомный код: генерация случайных чисел в JavaScript
- Случайное трехзначное число: генерация в JavaScript и онлайн-калькулятор
- Генератор рандомных чисел в диапазоне онлайн