
🎲 Jak wygenerować losową liczbę całkowitą z zakresu w JavaScript
Liczba losowa jest potrzebna wszędzie. ID zamówienia w sklepie internetowym, kod resetowania hasła, rzut kością w grze przeglądarkowej, wybór zwycięzcy konkursu. I prawie zawsze programista pisze tę samą linijkę: Math.random().
Ale między „uzyskać losowy ułamek od 0 do 1" a „uzyskać losową liczbę całkowitą od 5 do 20" jest przepaść złożona z trzech operacji, w której początkujący regularnie popełniają błędy: Math.floor i Math.ceil, plus jeden czy nie plus, zamieszanie, które daje albo wyjście poza zakres, albo nierównomierny rozkład.
Rozłożymy wzór na czynniki pierwsze, napiszemy działający generator w czystym HTML+CSS+JS i zajrzymy tam, gdzie Math.random() już nie wystarcza.
💡 Szybki przegląd:
- Proszę rozłożyć wzór Math.floor(Math.random() * (max - min + 1)) + min, zrozumieć każdą operację.
- Proszę dowiedzieć się, dlaczego tylko Math.floor, a nie Math.ceil czy Math.round, na przykładach zniekształceń zakresu.
- Proszę skopiować gotowy generator HTML: dwa pola, przycisk, wyświetlanie wyniku, czysty JS, bez frameworków.
- Proszę opanować Crypto.getRandomValues, gdy Math.random nie jest odpowiednie i potrzebna jest kryptograficznie bezpieczna alternatywa.
Jak działa Math.random()
Math.random(), statyczna metoda wbudowanego obiektu Math. Nie przyjmuje argumentów i zwraca pseudolosową liczbę zmiennoprzecinkową z zakresu [0, 1), zgodnie ze specyfikacją MDN. Nawias kwadratowy przy zerze oznacza „włącznie", okrągły przy jedynce „wyłączając". Oznacza to, że mogą Państwo otrzymać na przykład 0.0, wartość bliską 0.372 lub bliską 0.999, ale nigdy nie otrzymają Państwo dokładnie 1.0.
Rozkład jest w przybliżeniu równomierny. Implementacja jest wbudowana w silnik (V8, SpiderMonkey, JavaScriptCore), a konkretnego algorytmu (zazwyczaj xorshift128+) programista nie wybiera ani nie resetuje, ziarno (seed) jest ustawiane przez silnik podczas startu.
Samo Math.random() jest bezużyteczne w zadaniach praktycznych. Gry potrzebują liczb całkowitych, loterie, liczb całkowitych z zakresu, kryptografia, kryptograficznie bezpiecznej entropii, której Math.random() nie zapewnia. Dlatego zawsze nakłada się na nie wzór sprowadzający do zakresu.
Wzór: losowa liczba całkowita z zakresu [min, max]
Podstawowy wzór, który pokrywa zdecydowaną większość przypadków w web developmencie:
1 function randomInt(min, max) { 2 return Math.floor(Math.random() * (max - min + 1)) + min; 3 }
Rozłożymy to na przykładzie szkoleniowym z min = 5 i max = 10, opierając się na dokumentacji Math.random:
- Załóżmy, że
Math.random()zwrócił wartośćRz zakresu [0, 1). max - min + 1=10 - 5 + 1=6, liczba możliwych wyników całkowitych: 5, 6, 7, 8, 9, 10.R * 6, przeskalowaliśmy losowy ułamek przez szerokość zakresu.Math.floor(R * 6), odrzuciliśmy część ułamkową w dół. Teraz mamy indeks od 0 do 5.индекс + min, przesunęliśmy omin. Gotowe: losowa liczba całkowita od 5 do 10 włącznie.
Ten sam wzór w zapisie jednowierszowym:
1 const roll = Math.floor(Math.random() * (max - min + 1)) + min;
Dlaczego Math.floor, a nie Math.ceil czy Math.round
Wybór metody zaokrąglania ma krytyczny wpływ na rozkład. Pokażemy to na przykładzie szkoleniowym z zakresem od 1 do 3 (wszystkie wartości, poza granicami przedziałów, są obliczeniowe). Podejście opisano w kanonicznej odpowiedzi na StackOverflow.
Z Math.floor i wzorem (max - min + 1):
- Jeśli
Math.random()trafi w pierwszą jedną trzecią zakresu [0, 1): pomnożenie przez 3 da wartość od 0 do prawie 1,Math.floorodrzuci część ułamkową, wyjdzie indeks 0, plusmin, wypadnie 1 (dokładnie jedna trzecia przypadków). - Jeśli
Math.random()jest w środkowej jednej trzeciej: pomnożenie przez 3 da od 1 do prawie 2,Math.floor→ indeks 1, plusmin→ 2 (kolejna jedna trzecia). - Jeśli
Math.random()jest w ostatniej jednej trzeciej: pomnożenie przez 3 da od 2 do prawie 3,Math.floor→ indeks 2, plusmin→ 3 (ostatnia jedna trzecia).
Równomiernie.
Co będzie z Math.ceil? Math.ceil(Math.random() * 3) da 1, 2, 3, ale szansa na 1 będzie znikomo mała (tylko gdy Math.random() jest równe dokładnie 0, co prawie nigdy się nie zdarza). Skrajne wartości są zniekształcone.
Math.round również jest nierównomierny: skrajne wartości otrzymują połowę szansy w stosunku do środkowych. Dlatego w generowaniu losowych liczb całkowitych używa się wyłącznie Math.floor.
Pełny przykład: generator w HTML, CSS i JavaScript
Działająca strona, którą można zapisać jako .html i otworzyć w przeglądarce. Dwa pola wejściowe, przycisk, wynik na zielonym tle.
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>
Kod jest celowo napisany w czystym JavaScripcie, bez frameworków i narzędzi do budowania, dzięki czemu można go skopiować i uruchomić natychmiast, bez npm install. Funkcja randomInt przyjmuje min i max, zwraca liczbę całkowitą włącznie na obu końcach. Obsługa przycisku odczytuje wartości z pól, waliduje je i wyświetla wynik.
Kiedy Math.random() nie wystarcza: Crypto.getRandomValues()
Math.random(), generator pseudolosowy. Wystarcza do gier, animacji, testów A/B i wyboru losowego elementu tablicy. Ale do zadań, gdzie przewidywalność oznacza podatność na ataki, nie nadaje się: tokeny resetowania hasła, klucze API, kryptograficzne nonce, generatory jednorazowych linków.
Proszę sobie wyobrazić: wysyłają Państwo użytkownikowi link do resetowania hasła z tokenem wygenerowanym przez Math.random(). Znając ziarno i algorytm (a są one standardowe dla silnika), atakujący może odtworzyć sekwencję i odgadnąć token. Właśnie dlatego każdy kod związany z bezpieczeństwem musi używać kryptograficznie bezpiecznego źródła losowości.
Dla takich scenariuszy istnieje Web Crypto API, crypto.getRandomValues(). Wykorzystuje ono kryptograficznie bezpieczne źródło entropii systemu operacyjnego i wypełnia przekazaną tablicę typowaną losowymi wartościami.
Generowanie kryptograficznie bezpiecznej losowej liczby całkowitej z zakresu:
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 }
Tutaj stosuje się próbkowanie z odrzucaniem (rejection sampling): funkcja żąda losowej 32-bitowej wartości i odrzuca te, które wykraczają poza granice równomiernie podzielnego zakresu, co eliminuje obciążenie modulo (modulo bias), gdy jedne wartości wypadają częściej niż inne.
W praktyce dla większości aplikacji webowych wystarcza Math.random(). Ale jeśli piszą Państwo generator jednorazowych linków lub kodów weryfikacyjnych, proszę użyć crypto.getRandomValues().
Wideo: generowanie losowej liczby w JavaScripcie
Krótka demonstracja z kanału dcode, omówienie Math.random() i wzoru na zakres z żywym przykładem w konsoli przeglądarki:
⁉️🤔 Często zadawane pytania
Czy wzór Math.floor(Math.random() * (max - min + 1)) + min obejmuje górną granicę?
Tak, obejmuje.
max - min + 1daje liczbę możliwych wyników całkowitych, aMath.floorw połączeniu z+ minzapewnia, żemaxjest osiągalne. Na przykład przymin=1, max=3otrzymają Państwo 1, 2 lub 3, wszystkie trzy wartości są możliwe.
Czy można użyć Math.ceil zamiast Math.floor?
Technicznie tak, ale rozkład stanie się nierównomierny. Przy
Math.ceil(Math.random() * range)wartośćminwypadnie tylko wtedy, gdyMath.random()zwróci dokładnie 0, a to jest zdarzenie skrajnie rzadkie.Math.floordaje każdej liczbie całkowitej w zakresie równe szanse, dlatego w standardowym wzorze używa się właśnie jego.
Math.random(), czy to naprawdę losowa liczba?
Nie, to liczba pseudolosowa. Silnik JavaScript używa deterministycznego algorytmu (zazwyczaj xorshift128+), który generuje statystycznie równomierną sekwencję, ale znając ziarno, można przewidzieć wszystkie kolejne wartości. Dla kryptografii jest to niedopuszczalne, proszę używać
crypto.getRandomValues().
Jak wygenerować losową liczbę zmiennoprzecinkową z zakresu?
Proszę usunąć
Math.floori+ 1:Math.random() * (max - min) + min. Da to ułamek odminwłącznie domaxwyłączając. Jeśli potrzebny jest również górny koniec włącznie, proszę zastąpićmax - minprzezmax - min + Number.EPSILON, ale w praktyce zakresy ułamkowe rzadko wymagają włączania górnej granicy.
Co jest szybsze: Math.random() czy crypto.getRandomValues()?
Math.random()jest o rzędy wielkości szybsze. W benchmarkach na V8 różnica sięga stukrotności i więcej, ponieważcrypto.getRandomValues()wykonuje wywołanie systemowe do/dev/urandom(Linux) lub odpowiednika, aMath.random()to czysto wewnątrzprocesowy PRNG. Do generowania tysięcy wartości w pętli proszę użyćMath.random(), do pojedynczego tokena bezpieczeństwa,crypto.getRandomValues().
Losowa liczba w 30 sekund: co zapamiętać
Koń roboczy, Math.floor(Math.random() * (max - min + 1)) + min. Proszę zapamiętać trzy zasady: zawsze Math.floor (nie ceil, nie round), zawsze + 1 w mnożniku (aby max było osiągalne), zawsze + min na końcu (aby przesunąć zakres). Te trzy operacje pokrywają wszystko, od rzutu kością po wybór losowego zwycięzcy.
Dla kodu resetowania haseł i tokenów, crypto.getRandomValues(). Wolniejsze, ale kryptograficznie bezpieczne.
Jeśli chcą Państwo po prostu spróbować, proszę skopiować przykład HTML powyżej, zapisać jako plik i otworzyć w przeglądarce. Żadnych zależności, żadnych narzędzi do budowania. Działa wszędzie tam, gdzie jest <script>.



