Генерация случайных целых чисел в JavaScript: точный диапазон в 2025

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Случайные числа в JavaScript: от хаоса к порядку

Представьте: вы пишете игру, где нужно случайным образом выбрать сундук с сокровищем от 1 до 100. Или, может быть, создаете A/B тест, который выбирает случайный вариант для пользователя. Казалось бы, что может быть проще? Но дьявол, как всегда, кроется в деталях. Вопрос о генерации случайных целых чисел в JavaScript в конкретном диапазоне — один из самых популярных на Stack Overflow, и неспроста. Даже опытные разработчики иногда спотыкаются о подводные камни Math.random().

В этой статье мы разберем не только классические методы, но и лучшие практики 2025 года: как сделать код не только рабочим, но и безопасным, предсказуемым (в хорошем смысле) и производительным. Поехали!

Классика жанра: Math.random() и подводные камни

Начнем с фундамента. Встроенная функция Math.random() возвращает псевдослучайное число с плавающей точкой в диапазоне [0, 1). Ключевой момент: 0 включается, а 1 — нет. Это «нечестное» распределение, если вы хотите получить целые числа.

Вот самый распространенный (и самый неправильный) способ, который часто встречается в старых учебниках:

// ПЛОХОЙ пример: смещение вероятности
function getRandomBad(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

На первый взгляд, все верно. Но копнем глубже. Math.floor() округляет вниз. Если Math.random() генерирует число очень близкое к 1 (но не равное), умножение на (max - min + 1) даст значение меньше max + 1, и Math.floor() срежет его. Однако, из-за ограниченной точности чисел с плавающей точкой (IEEE 754), некоторые значения выпадают реже других. Для диапазона 0-1 это незаметно, но для больших диапазонов (например, от 1 до 1 000 000) смещение становится статистически значимым.

Вывод: эта формула работает для небольших диапазонов, но не является «криптографически чистой» и может давать перекос в распределении.

Правильная формула от MDN

Mozilla Developer Network (MDN) рекомендует более корректный подход, который минимизирует ошибки округления:

// Хороший, универсальный способ
function getRandomIntInclusive(min, max) {
  const minCeiled = Math.ceil(min);
  const maxFloored = Math.floor(max);
  return Math.floor(Math.random() * (maxFloored - minCeiled + 1) + minCeiled);
}

Разница в том, что мы явно приводим границы к целым числам с помощью Math.ceil() и Math.floor(). Это гарантирует, что min и max будут обработаны корректно, даже если вы случайно передадите дробное число. Используйте этот метод как базовый стандарт в 2025 году.

Альтернативы: когда Math.random() недостаточно

Классический Math.random() — это псевдослучайный генератор (PRNG). Он детерминирован: если вы знаете начальное зерно (seed), вы можете предсказать всю последовательность. Для большинства веб-приложений (анимации, случайный контент) этого достаточно. Но есть сценарии, где нужна настоящая случайность:

  • Безопасность: генерация токенов, паролей, CSRF-токенов.
  • Криптография: шифрование данных на клиенте.
  • Азартные игры: честное распределение выигрышей.

В этих случаях на помощь приходит crypto.getRandomValues() — криптографически стойкий генератор (CSPRNG). Он использует системные источники энтропии и гораздо менее предсказуем.

Генерация целых чисел с crypto.getRandomValues()

Вот как выглядит современная реализация на 2025 год:

// Безопасная генерация случайного целого числа в диапазоне [min, max]
function getSecureRandomInt(min, max) {
  const range = max - min + 1;
  // Максимальное значение, кратное range (чтобы избежать смещения)
  const maxValid = Math.floor(0xFFFFFFFF / range) * range;
  const randomBuffer = new Uint32Array(1);
  
  let randomValue;
  do {
    crypto.getRandomValues(randomBuffer);
    randomValue = randomBuffer[0];
  } while (randomValue >= maxValid); // Отбрасываем значения, которые нарушают равномерность
  
  return min + (randomValue % range);
}

// Пример использования:
console.log(getSecureRandomInt(1, 100)); // Случайное число от 1 до 100

Этот код немного сложнее, но он решает проблему «смещения» (bias), которая возникает, когда 0xFFFFFFFF не делится нацело на range. Мы просто перегенерируем число, пока не получим подходящее. Для большинства приложений это происходит редко (вероятность < 0.5).

Современные тренды 2025: что изменилось?

JavaScript не стоит на месте. Вот три ключевых изменения, которые актуальны прямо сейчас:

  • BigInt и большие числа: Если вам нужно сгенерировать случайное число от 0 до 10^100, Math.random() не справится из-за ограничений точности. Используйте crypto.getRandomValues() с BigInt для работы с огромными диапазонами.
  • Web Crypto API стабильна: В 2025 году crypto.getRandomValues() поддерживается всеми современными браузерами (включая старые Edge и Safari). Забудьте про полифилы.
  • Оптимизация производительности: Для генерации одного числа crypto.getRandomValues() медленнее Math.random() примерно в 10-20 раз. Но если вам нужно 10 000 чисел, лучше сгенерировать большой буфер один раз и использовать его.

Генерация нескольких чисел за раз (производительность)

// Быстрая генерация массива случайных чисел
function getSecureRandomArray(min, max, count) {
  const range = max - min + 1;
  const buffer = new Uint32Array(count);
  crypto.getRandomValues(buffer);
  
  return Array.from(buffer, (value) => {
    const maxValid = Math.floor(0xFFFFFFFF / range) * range;
    // Простая обработка: для массовой генерации можно пожертвовать идеальной равномерностью
    return min + (value % range);
  });
}

console.log(getSecureRandomArray(10, 20, 5)); // [14, 18, 11, 20, 15]

Обратите внимание: в этом примере мы не делаем цикл с проверкой while, так как для массовой генерации перекос будет статистически мал, а производительность вырастет в разы.

Частые ошибки и как их избежать

Даже

Блоги

Рекомендаций по книгам