Skip to content

Kõik WordPressist, veebiarendusest — ja mitte ainult

🎲 Kuidas genereerida JavaScriptiga juhuslik täisarv vahemikus

🎲 Kuidas genereerida JavaScriptiga juhuslik täisarv vahemikus

Juhuslikke numbreid on vaja igal pool. Tellimuse ID-d veebipoodides, parooli lähtestuskoodid, täringuvisked brauserimängudes, loosivõitja valimine. Ja peaaegu alati kirjutavad arendajad sama rea: Math.random().

Kuid „saa juhuslik kümnendmurd vahemikus 0 kuni 1" ja „saa juhuslik täisarv vahemikus 5 kuni 20" vahel on kolmest operatsioonist koosnev kuristik, kus algajad regulaarselt vigu teevad: Math.floor ja Math.ceil, pluss üks või mitte pluss, segadus, mis viib kas vahemiku ületamise või ebaühtlase jaotuseni.

Lahutame valemi viimase suluni lahti, kirjutame puhtas HTML+CSS+JS-is töötava generaatori ja vaatame, kus Math.random() puudulikuks jääb.

💡 Kiirülevaade:

  • Lahuta valem Math.floor(Math.random() * (max - min + 1)) + min osadeks, mõista iga operatsiooni.
  • Õpi, miks ainult Math.floor töötab, mitte Math.ceil või Math.round, koos vahemiku moonutuste näidetega.
  • Kopeeri valmis HTML-generaator: kaks välja, nupp, tulemuse väljund, puhas JS, ilma raamistikuteta.
  • Valda Crypto.getRandomValues, kui Math.random ei sobi ja vajad krüptograafiliselt turvalist alternatiivi.

Kuidas Math.random() töötab

Math.random() on sisseehitatud Math objekti staatiline meetod. See ei võta argumente ja tagastab pseudo-juhusliku ujukomaarvu vahemikus [0, 1), vastavalt MDN spetsifikatsioonile. Nurksulg nulli juures tähendab „kaasa arvatud", ümarsulg ühe juures tähendab „välistav". Seega võid saada 0.0, väärtuse umbes 0.372 või midagi 0.999 lähedast, kuid täpselt 1.0 ei saa sa kunagi.

Jaotus on ligikaudu ühtlane. Implementatsioon on mootorisse sisse ehitatud (V8, SpiderMonkey, JavaScriptCore) ja arendaja ei saa konkreetset algoritmi (tavaliselt xorshift128+) valida ega lähtestada; seeme määratakse mootori poolt käivitamisel.

Iseenesest on Math.random() praktiliste ülesannete jaoks kasutu. Mängud vajavad täisarve, loteriid vajavad täisarve vahemikus, krüptograafia vajab krüptograafiliselt turvalist entroopiat, mis Math.random()-il puudub. Seepärast mähitakse selle ümber alati vahemiku teisendamise valem.

Valem: juhuslik täisarv vahemikus [min, max]

Põhivalem, mis katab valdava enamuse veebiarenduse juhtudest:

1function randomInt(min, max) {
2 return Math.floor(Math.random() * (max - min + 1)) + min;
3}

Lahutame selle õppenäitega, kus min = 5 ja max = 10, tuginedes Math.random dokumentatsioonile:

  • Oletame, et Math.random() tagastas väärtuse R vahemikus [0, 1).
  • max - min + 1 = 10 - 5 + 1 = 6, võimalike täisarvuliste tulemuste arv: 5, 6, 7, 8, 9, 10.
  • R * 6 skaleeris juhusliku kümnendmurru vahemiku laiusele.
  • Math.floor(R * 6) ümardas alla, jättes murdosa kõrvale. Nüüd on meil indeks 0 kuni 5.
  • index + min nihutas min võrra. Valmis: juhuslik täisarv 5 kuni 10 (kaasa arvatud).

Sama valem ühe reana:

1const roll = Math.floor(Math.random() * (max - min + 1)) + min;

Miks Math.floor, mitte Math.ceil või Math.round

Ümardamismeetodi valik mõjutab jaotust kriitiliselt. Demonstreerime seda õppenäitega, kasutades vahemikku 1 kuni 3 (kõik väärtused peale intervalli piiride on arvutatud). Lähenemist kirjeldatakse kanoonilises StackOverflow vastuses.

Math.floor ja valemiga (max - min + 1):

  • Kui Math.random() langeb vahemiku [0, 1) esimesse kolmandikku: 3-ga korrutamine annab väärtuse 0 kuni peaaegu 1, Math.floor jätab murdosa kõrvale, andes indeksi 0, pluss min annab 1 (täpselt üks kolmandik juhtudest).
  • Kui Math.random() on keskmises kolmandikus: 3-ga korrutamine annab 1 kuni peaaegu 2, Math.floor → indeks 1, pluss min2 (teine kolmandik).
  • Kui Math.random() on viimases kolmandikus: 3-ga korrutamine annab 2 kuni peaaegu 3, Math.floor → indeks 2, pluss min3 (viimane kolmandik).

Ühtlane jaotus.

Mis juhtub Math.ceil-iga? Math.ceil(Math.random() * 3) annab 1, 2, 3, kuid 1-l on olematu tõenäosus (ainult siis, kui Math.random() on täpselt 0, mida peaaegu kunagi ei juhtu). Äärmuslikud väärtused on moonutatud.

Math.round on samuti ebaühtlane: äärmuslikud väärtused saavad poole väiksema tõenäosuse võrreldes keskmistega. Seepärast kasutatakse juhusliku täisarvu genereerimisel eranditult Math.floor.

Täielik näide: generaator HTML-is, CSS-is ja JavaScriptis

Töötav leht, mille saad salvestada .html failina ja brauseris avada. Kaks sisendvälja, nupp, tulemus rohelisel taustal.

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>

Kood on tahtlikult kirjutatud puhtas JavaScriptis, ilma raamistike või komplekteerijateta, et seda saaks kopeerida ja kohe ilma npm install-ita käivitada. Funktsioon randomInt võtab min ja max, tagastades täisarvu, mis on mõlemast otsast kaasa arvatud. Nupu töötleja loeb väärtused väljadelt, valideerib need ja kuvab tulemuse.

Kui Math.random() ei ole piisav: Crypto.getRandomValues()

Math.random() on pseudo-juhuslik generaator. See on piisav mängude, animatsioonide, A/B testide ja juhusliku massiivielemendi valimise jaoks. Kuid ülesannete jaoks, kus ennustatavus tähendab haavatavust, see ei sobi: parooli lähtestamise märgid, API võtmed, krüptograafilised nonce’id, ühekordsete linkide generaatorid.

Kujutle: saadad kasutajale parooli lähtestamise lingi märgiga, mis on genereeritud Math.random() abil. Teades seemet ja algoritmi (mis on mootori jaoks standardsed), saab ründaja jada rekonstrueerida ja märgi ära arvata. Just seepärast peab igasugune turvalisusega seotud kood kasutama krüptograafiliselt turvalist juhuslikkuse allikat.

Selliste stsenaariumide jaoks on olemas Web Crypto API: crypto.getRandomValues(). See kasutab operatsioonisüsteemi krüptograafiliselt turvalist entroopiaallikat ja täidab etteantud tüübitud massiivi juhuslike väärtustega.

Krüptograafiliselt turvalise juhusliku täisarvu genereerimine vahemikus:

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}

See kasutab tagasilükkamise valimit: funktsioon küsib juhuslikku 32-bitist väärtust ja lükkab tagasi need, mis jäävad väljapoole ühtlaselt jagatavat vahemikku. See välistab mooduli nihke, kui mõned väärtused esinevad sagedamini kui teised.

Praktikas on Math.random() enamiku veebirakenduste jaoks piisav. Kuid kui kirjutad generaatorit ühekordsete linkide või kinnituskoodide jaoks, kasuta crypto.getRandomValues().

Video: juhusliku arvu genereerimine JavaScriptis

Lühike demonstratsioon dcode kanalilt, mis lahutab Math.random() ja vahemiku valemi lahti elava näitega brauseri konsoolis:

⁉️🤔 Korduma kippuvad küsimused

Kas valem Math.floor(Math.random() * (max - min + 1)) + min hõlmab ülemist piiri?

Jah, hõlmab. max - min + 1 annab võimalike täisarvuliste tulemuste arvu ning Math.floor koos + min-iga tagab, et max on saavutatav. Näiteks min=1, max=3 korral saad 1, 2 või 3; kõik kolm väärtust on võimalikud.

Kas Math.ceil-i saab kasutada Math.floor-i asemel?

Tehniliselt jah, kuid jaotus muutub ebaühtlaseks. Math.ceil(Math.random() * range) korral esineb väärtus min ainult siis, kui Math.random() tagastab täpselt 0, mis on kaduvväike sündmus. Math.floor annab igale täisarvule vahemikus võrdse võimaluse, mistõttu standardvalem seda kasutab.

Kas Math.random() on tõeliselt juhuslik arv?

Ei, see on pseudo-juhuslik arv. JavaScripti mootor kasutab deterministlikku algoritmi (tavaliselt xorshift128+), mis toodab statistiliselt ühtlase jada, kuid seemne teadmine võimaldab kõiki järgnevaid väärtusi ennustada. Krüptograafia jaoks on see vastuvõetamatu; kasuta crypto.getRandomValues().

Kuidas genereerida juhuslikku ujukomaarvu vahemikus?

Eemalda Math.floor ja + 1: Math.random() * (max - min) + min. See annab kümnendmurru alates min (kaasa arvatud) kuni max (välja arvatud). Kui vajad ka ülemist otsa kaasa arvatuna, asenda max - min väärtusega max - min + Number.EPSILON, kuid praktikas vajavad kümnendmurru vahemikud harva ülemise piiri kaasamist.

Kumb on kiirem: Math.random() või crypto.getRandomValues()?

Math.random() on suurusjärkude võrra kiirem. V8 etalonides ulatub erinevus sajakordse vaheni või enamgi, sest crypto.getRandomValues() teeb süsteemikutse /dev/urandom-ile (Linux) või samaväärsele, samas kui Math.random() on puhtalt protsessisisene PRNG. Tuhandete väärtuste genereerimiseks tsüklis kasuta Math.random(); üksiku turvamärgi jaoks kasuta crypto.getRandomValues().

Juhuslik arv 30 sekundiga: mida meeles pidada

Tööloom on Math.floor(Math.random() * (max - min + 1)) + min. Jäta meelde kolm reeglit: alati Math.floor (mitte ceil, mitte round), alati + 1 kordajas (et max oleks saavutatav), alati + min lõpus (vahemiku nihutamiseks). Need kolm operatsiooni katavad kõik alates täringuvisetest kuni juhusliku võitja valimiseni.

Parooli lähtestamise koodide ja märkide jaoks kasuta crypto.getRandomValues(). Aeglasem, kuid krüptograafiliselt turvaline.

Kui soovid lihtsalt järele proovida, kopeeri ülaltoodud HTML näide, salvesta failina ja ava brauseris. Ei mingeid sõltuvusi, ei komplekteerijaid. Töötab igal pool, kus on <script>.