
🎲 Как сгенерировать случайное целое число в диапазоне на JavaScript
Случайное число нужно везде. ID заказа в интернет-магазине, код сброса пароля, бросок кубика в браузерной игре, выбор победителя розыгрыша. И почти всегда разработчик пишет одну и ту же строчку: Math.random().
Но между «получить случайное дробное от 0 до 1» и «получить случайное целое от 5 до 20», пропасть из трёх операций, в которой новички регулярно ошибаются: Math.floor и Math.ceil, плюс один или не плюс, путаница, которая даёт либо выход за диапазон, либо неравномерное распределение.
Разберём формулу до последней скобки, напишем рабочий генератор на чистом HTML+CSS+JS и заглянем туда, где Math.random() уже не справляется.
💡 Быстрый обзор:
- Разберите формулу Math.floor(Math.random() * (max - min + 1)) + min, поймите каждую операцию.
- Узнайте, почему только Math.floor, а не Math.ceil или Math.round, на примерах искажений диапазона.
- Скопируйте готовый HTML-генератор: два поля, кнопка, вывод результата, чистый JS, без фреймворков.
- Освойте Crypto.getRandomValues, когда Math.random не годится и нужна криптостойкая альтернатива.
Как работает Math.random()
Math.random(), статический метод встроенного объекта Math. Он не принимает аргументов и возвращает псевдослучайное число с плавающей точкой в диапазоне [0, 1), согласно спецификации MDN. Квадратная скобка у нуля означает «включительно», круглая у единицы, «исключая». То есть вы можете получить, к примеру 0.0, значение около 0.372 или близкое к 0.999, но никогда не получите ровно 1.0.
Распределение, приблизительно равномерное. Реализация зашита в движок (V8, SpiderMonkey, JavaScriptCore), и конкретный алгоритм (обычно xorshift128+) разработчик не выбирает и не сбрасывает, сид задаётся движком при старте.
Сам по себе Math.random() для прикладных задач бесполезен. Играм нужны целые числа, лотереям, целые в диапазоне, криптографии, криптостойкая энтропия, которой у Math.random() нет. Поэтому поверх него всегда наворачивают формулу приведения к диапазону.
Формула: случайное целое в диапазоне [min, max]
Базовая формула, которая покрывает подавляющее большинство случаев в веб-разработке:
1 function randomInt(min, max) { 2 return Math.floor(Math.random() * (max - min + 1)) + min; 3 }
Разберём на учебном примере с min = 5 и max = 10, опираясь на документацию Math.random:
- Допустим,
Math.random()вернул значениеRв диапазоне [0, 1). max - min + 1=10 - 5 + 1=6, количество возможных целых исходов: 5, 6, 7, 8, 9, 10.R * 6, масштабировали случайное дробное на ширину диапазона.Math.floor(R * 6), отбросили дробную часть вниз. Теперь у нас индекс от 0 до 5.индекс + min, сдвинули наmin. Готово: случайное целое от 5 до 10 включительно.
Та же формула в однострочнике:
1 const roll = Math.floor(Math.random() * (max - min + 1)) + min;
Почему Math.floor, а не Math.ceil или Math.round
Выбор метода округления критически влияет на распределение. Покажем на учебном примере с диапазоном от 1 до 3 (все значения, кроме границ интервалов, расчётные). Подход описан в каноническом ответе на StackOverflow.
С Math.floor и формулой (max - min + 1):
- Если
Math.random()попал в первую треть диапазона [0, 1): умножение на 3 даст значение от 0 до почти 1,Math.floorотбросит дробную часть, получится индекс 0, плюсmin, выпадет 1 (ровно треть случаев). - Если
Math.random()в средней трети: умножение на 3 даст от 1 до почти 2,Math.floor→ индекс 1, плюсmin→ 2 (ещё треть). - Если
Math.random()в последней трети: умножение на 3 даст от 2 до почти 3,Math.floor→ индекс 2, плюсmin→ 3 (последняя треть).
Равномерно.
Что будет с Math.ceil? Math.ceil(Math.random() * 3) даст 1, 2, 3, но у 1 шанс будет ничтожно мал (только при Math.random() ровно 0, что почти не случается). Крайние значения искажены.
Math.round тоже неравномерен: крайние значения получают половинный шанс относительно средних. Поэтому в генерации случайных целых используют исключительно Math.floor.
Полный пример: генератор на HTML, CSS и JavaScript
Рабочая страница, которую можно сохранить как .html и открыть в браузере. Два поля ввода, кнопка, результат на зелёной подложке.
1 <!DOCTYPE html> 2 <html lang="ru"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Генератор случайных чисел</title> 6 <style> 7 body { 8 font-family: system-ui, sans-serif; 9 max-width: 400px; 10 margin: 50px auto; 11 padding: 0 16px; 12 } 13 label { 14 display: block; 15 margin-top: 12px; 16 font-weight: 600; 17 } 18 input { 19 width: 100%; 20 padding: 8px; 21 margin-top: 4px; 22 font-size: 16px; 23 box-sizing: border-box; 24 } 25 button { 26 margin-top: 16px; 27 padding: 10px 24px; 28 font-size: 16px; 29 cursor: pointer; 30 } 31 .result { 32 margin-top: 20px; 33 font-size: 24px; 34 font-weight: 700; 35 color: #2e7d32; 36 } 37 </style> 38 </head> 39 <body> 40 41 <h1>Случайное число в диапазоне</h1> 42 43 <label for="minInput">От:</label> 44 <input type="number" id="minInput" value="1"> 45 46 <label for="maxInput">До:</label> 47 <input type="number" id="maxInput" value="100"> 48 49 <button id="generateBtn">Сгенерировать</button> 50 51 <div class="result" id="resultDisplay"></div> 52 53 <script> 54 function randomInt(min, max) { 55 return Math.floor(Math.random() * (max - min + 1)) + min; 56 } 57 58 const minInput = document.getElementById('minInput'); 59 const maxInput = document.getElementById('maxInput'); 60 const generateBtn = document.getElementById('generateBtn'); 61 const resultDisplay = document.getElementById('resultDisplay'); 62 63 generateBtn.addEventListener('click', () => { 64 const min = parseInt(minInput.value, 10); 65 const max = parseInt(maxInput.value, 10); 66 67 if (isNaN(min) || isNaN(max)) { 68 resultDisplay.textContent = 'Введите оба числа'; 69 return; 70 } 71 if (min > max) { 72 resultDisplay.textContent = '«От» не может быть больше «До»'; 73 return; 74 } 75 76 const result = randomInt(min, max); 77 resultDisplay.textContent = `Результат: ${result}`; 78 }); 79 </script> 80 81 </body> 82 </html>
Код намеренно написан на чистом JavaScript, без фреймворков и сборщиков, так его можно скопировать и запустить мгновенно, без npm install. Функция randomInt принимает min и max, возвращает целое включительно по обоим концам. Обработчик кнопки читает значения из полей, валидирует и выводит результат.
Когда Math.random() недостаточно: Crypto.getRandomValues()
Math.random(), псевдослучайный генератор. Его достаточно для игр, анимаций, A/B-тестов и выбора случайного элемента массива. Но для задач, где предсказуемость означает уязвимость, он не годится: токены сброса пароля, ключи API, криптографические nonce, генераторы одноразовых ссылок.
Представьте: вы отправляете пользователю ссылку для сброса пароля с токеном, сгенерированным через Math.random(). Зная сид и алгоритм (а они стандартны для движка), злоумышленник может восстановить последовательность и подобрать токен. Именно поэтому любой код, касающийся безопасности, должен использовать криптостойкий источник случайности.
Для таких сценариев существует Web Crypto API, crypto.getRandomValues(). Он использует криптостойкий источник энтропии операционной системы и заполняет переданный типизированный массив случайными значениями.
Генерация криптостойкого случайного целого в диапазоне:
1 function secureRandomInt(min, max) { 2 const range = max - min + 1; 3 const maxSafeVal = Math.floor(256 ** 4 / range) * range; 4 const buffer = new Uint32Array(1); 5 6 do { 7 crypto.getRandomValues(buffer); 8 } while (buffer[0] >= maxSafeVal); 9 10 return min + (buffer[0] % range); 11 }
Здесь используется rejection sampling: функция запрашивает случайное 32-битное значение и отвергает те, что выходят за пределы равномерно делимого диапазона, это исключает смещение (modulo bias), когда одни значения выпадают чаще других.
На практике для большинства веб-приложений хватает Math.random(). Но если вы пишете генератор одноразовых ссылок или кодов подтверждения, берите crypto.getRandomValues().
Видео: генерация случайного числа в JavaScript
Короткая демонстрация от канала dcode, разбор Math.random() и формулы диапазона с живым примером в консоли браузера:
⁉️🤔 Частые вопросы
Включает ли формула Math.floor(Math.random() * (max - min + 1)) + min верхнюю границу?
Да, включает.
max - min + 1даёт количество возможных целых исходов, аMath.floorв комбинации с+ minобеспечивает, чтоmaxдостижим. Например, приmin=1, max=3вы получите 1, 2 или 3, все три значения возможны.
Можно ли использовать Math.ceil вместо Math.floor?
Технически да, но распределение станет неравномерным. При
Math.ceil(Math.random() * range)значениеminбудет выпадать только когдаMath.random()вернёт ровно 0, это исчезающе редкое событие.Math.floorдаёт каждому целому в диапазоне равный шанс, поэтому в стандартной формуле используют именно его.
Math.random(), это действительно случайное число?
Нет, это псевдослучайное число. Движок JavaScript использует детерминированный алгоритм (обычно xorshift128+), который выдаёт статистически равномерную последовательность, но зная сид, можно предсказать все последующие значения. Для криптографии это неприемлемо, используйте
crypto.getRandomValues().
Как сгенерировать случайное число с плавающей точкой в диапазоне?
Уберите
Math.floorи+ 1:Math.random() * (max - min) + min. Это даст дробное отminвключительно доmaxисключая. Если нужен и верхний конец включительно, заменитеmax - minнаmax - min + Number.EPSILON, но на практике дробные диапазоны редко требуют включительности верхней границы.
Что быстрее:Math.random() или crypto.getRandomValues()?
Math.random()на порядки быстрее. В бенчмарках на V8 разница достигает стократного и большего разрыва, потому чтоcrypto.getRandomValues()делает системный вызов к/dev/urandom(Linux) или аналогу, аMath.random(), чисто внутрипроцессный PRNG. Для генерации тысяч значений в цикле беритеMath.random(), для одного токена безопасности,crypto.getRandomValues().
Случайное число за 30 секунд: что запомнить
Рабочая лошадка, Math.floor(Math.random() * (max - min + 1)) + min. Запомните три правила: всегда Math.floor (не ceil, не round), всегда + 1 в множителе (чтобы max был достижим), всегда + min в конце (чтобы сдвинуть диапазон). Эти три операции покрывают всё, от броска кубика до выбора случайного победителя.
Для кода сброса паролей и токенов, crypto.getRandomValues(). Медленнее, зато криптостойко.
Если нужно просто попробовать, скопируйте HTML-пример выше, сохраните как файл и откройте в браузере. Никаких зависимостей, никаких сборщиков. Работает везде, где есть <script>.



