Skip to content

Все для WordPress, веб-розробки — і не тільки

🎲 Як згенерувати випадкове ціле число в діапазоні на JavaScript

🎲 Як згенерувати випадкове ціле число в діапазоні на 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]

Базова формула, яка покриває переважну більшість випадків у веброзробці:

1function 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 включно.

Та сама формула в однорядковому записі:

1const 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, плюс min2 (ще третина).
  • Якщо Math.random() в останній третині: множення на 3 дасть від 2 до майже 3, Math.floor → індекс 2, плюс min3 (остання третина).

Рівномірно.

Що буде з 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(). Він використовує криптостійке джерело ентропії операційної системи і заповнює переданий типізований масив випадковими значеннями.

Генерація криптостійкого випадкового цілого в діапазоні:

1function 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>.