【JavaScript】乱数とは?初心者にもわかりやすく具体例つきで解説
はじめに
皆さんは、プログラミングを始めてみたものの、「ランダムに数値を生成する方法がわからない」と困ってしまうことはないでしょうか。
JavaScriptでランダムな値を生成するためには、いくつかの方法があります。
最も有名なのは、Math.random()
を用いた手法です。
ただし、Math.random()
はあくまで擬似乱数を生成するものであり、セキュリティの高い乱数が必要な場合には別のアプローチが必要になります。
そこで本記事では、JavaScriptにおける乱数の基本から、さまざまな実装方法、実務での活用例までを丁寧に解説します。
プログラミング初心者の皆さんでも理解しやすいよう、具体的なコード例を交えながら紹介しますので、ぜひ参考にしてください。
この記事を読むとわかること
- JavaScriptで乱数を生成する基本的な方法
- 擬似乱数と暗号論的安全な乱数の違い
- 乱数を使った実務での活用シーン(ゲーム開発・UI演出・抽選など)
Math.random()
やcrypto.getRandomValues()
の具体的なコード例- 配列のシャッフルやランダム色の生成など、よくある実装の方法
JavaScriptにおける乱数の基礎
JavaScriptで乱数を扱う際、まず知っておきたいのが「擬似乱数」と「暗号論的安全な乱数」の概念です。
一般的に、プログラミング言語が標準で提供する乱数生成関数は、ほとんどが擬似乱数を返します。
擬似乱数とは、一定のアルゴリズムに基づいて生成される乱数であり、厳密には決定的な性質を持つという点がポイントです。
一方で暗号論的安全な乱数は、セキュリティの観点から、外部から値を予測されにくいよう工夫された仕組みを備えた乱数です。
JavaScriptでは、Math.random()
が擬似乱数を返し、crypto.getRandomValues()
などが暗号論的安全な乱数を得るための仕組みとして利用されることが多いです。
実務でも、「ユーザーの表示するアニメーションにちょっとしたランダム性を加える」といった目的なら Math.random()
で十分なケースがほとんどでしょう。
しかし、「ユーザーのパスワード生成」や「トークンの生成」のようなセキュリティが関わる場面では、暗号論的安全な乱数である crypto.getRandomValues()
を使う方が適切だと言えます。
こうした違いを理解しておくと、用途に応じて適切な乱数生成手法を選ぶことができるようになります。
初心者の方はまず Math.random()
を使いこなせるようになると、JavaScriptでのランダム処理の基礎を押さえられるはずです。
そのうえで、さらにセキュアな手法が必要なときに crypto
を使う、という流れで学習を進めるとスムーズです。
乱数が実務で使われるシーン
JavaScriptで乱数を使う場面は多岐にわたります。
Webサイトのちょっとした演出だけでなく、ゲームやアプリケーションの挙動にも欠かせません。
ここでは、いくつか代表的なシーンを挙げてみます。
ゲーム開発での乱数
サイコロを振る、カードを引く、宝箱の中身を決定するなど、ゲーム要素にはしばしばランダム性が必要です。
ブラウザゲームの場合は、クライアントサイドでJavaScriptが動作するため、Math.random()
で乱数を扱うケースが多いです。
ただし、対人ゲームのように不正利用を防ぎたい場合は、サーバーサイドでの処理が必要になる場合もあるでしょう。
WebページのUI/UX
画像やテキストをランダムで表示して、ユーザーに変化を与える場面があります。
たとえば、トップページの背景画像をランダムに変える、ボタンをクリックするとランダムにメッセージが切り替わるなど、単調になりがちな画面に意外性を取り入れることができます。
抽選システムやくじ引き
イベントやキャンペーンで、ランダムな抽選を行いたい場面で使われることがあります。
ただし、厳密な公平性を求める場合は、サーバーサイドでしっかりとロジックを組む必要があります。
セキュアなトークンやパスワード生成
ログイン周りの機能で一時トークンを発行したい場合や、仮パスワードを生成したいときなどは、crypto.getRandomValues()
のように暗号論的安全性を考慮した仕組みが不可欠です。
配列のシャッフル
配列の要素をランダムに並び替えて、順番を変更することはよくある操作です。
商品の表示順をランダムにしたり、クイズなどの問題順を変えたりする際に便利です。
このように、乱数はちょっとしたUI/UX上の演出から本格的なセキュリティ機能まで、幅広く利用されます。
擬似乱数と暗号論的安全な乱数
乱数生成の世界には大きくわけて2種類の乱数があります。
ここでは、それぞれの特徴をもう少し詳しく見ていきましょう。
擬似乱数とは
擬似乱数は、アルゴリズムによって数値が「見かけ上ランダム」になるように生成されたものです。
本当にランダムかというと、厳密には計算過程が存在するため、一度シード値がわかると後続の値を予測できる可能性があります。
多くの場合、ユーザーインターフェースの改善やゲームの挙動などであれば、この擬似乱数で十分なケースが多いです。
JavaScriptの場合、Math.random()
がこれにあたります。
暗号論的安全な乱数とは
暗号論的安全な乱数は、乱数からシードや次の値を推測されにくいように設計されています。
パスワード生成や認証トークンの作成では、悪意のある第三者に推測されると重大な問題が発生するため、この性質がとても重要になります。
JavaScriptでは、crypto.getRandomValues()
が代表的な手法です。
セキュリティを重視しない用途であれば、暗号論的安全な乱数を毎回使う必要はないかもしれません。
ただし、特に保護すべき情報を扱う場合には、暗号論的安全な乱数を利用するのが一般的です。
Math.random() を使った擬似乱数の生成
ここからは実際のコードを交えつつ、乱数生成について具体的に紹介していきます。
Math.random() の基本的な使い方
Math.random()
はJavaScriptの組み込みオブジェクト Math
が提供する関数で、戻り値は 0以上1未満 の数値です。
// 0以上1未満の乱数を取得 const randomValue = Math.random(); console.log(randomValue); // 例: 0.123456789...
このように、呼び出すたびに異なる値が返ってくるため、たとえば1回だけ呼べば 0.12345 が返り、別のタイミングで呼べば 0.67890 が返るなどの違いが出ます。
ただし毎回本物の乱数ではなく、「一定のアルゴリズムに沿って見かけ上ランダムな値が返る」ため、セキュリティが絡む場合は注意してください。
任意の範囲の整数を生成する
0から1未満の値が返されても、実務では「0から100までの整数をランダムに取りたい」といったケースの方が多いでしょう。
そんなときは、下記のような関数がよく使われます。
/** * min以上max以下のランダムな整数を返す * min, maxは整数であることを前提 */ function getRandomInt(min, max) { // Math.random()で0〜1未満の値を生成 // それに (max - min + 1) を掛けてから小数点以下を切り捨てる return Math.floor(Math.random() * (max - min + 1)) + min; } // 例: 1〜6までの整数をランダムに得る const diceRoll = getRandomInt(1, 6); console.log(diceRoll);
上記のコードでは、 Math.random() * (max - min + 1)
によって 0 ~ (max-min+1)
未満の範囲の小数を生成し、Math.floor()
で小数点以下を切り捨てます。
それを min
に足すことで、最終的に min ~ max
の整数値が得られるわけです。
この方法はゲームのサイコロや、ランダムなアイテムの出現など、幅広い場面で使われます。
実務での活用例:乱数を使ったアニメーション
たとえば、Webページ上の要素をランダムに動かす簡単なアニメーションを作る場合、Math.random()
で得た値をCSSの座標や角度に反映させるだけで、見た目に変化をもたせることができます。
const box = document.getElementById("box");
function randomMovement() {
const randomX = Math.random() * 500; // 0〜500pxの範囲
const randomY = Math.random() * 300; // 0〜300pxの範囲
box.style.transform = `translate(${randomX}px, ${randomY}px)`;
}
こういったシンプルな実装でも、単調に見えるUIを少し楽しげに演出できるかもしれません。
画面に動きや変化を与えたい場合には、まずは Math.random()
を活用するとよいでしょう。
crypto.getRandomValues() を使った暗号論的安全な乱数
Web開発ではそこまで多くないかもしれませんが、どうしてもセキュリティが必要なランダムが欲しいケースも存在します。
そういう場合に使えるのが、crypto.getRandomValues()
です。
crypto.getRandomValues() の使用例
crypto.getRandomValues()
では、指定したTypedArrayを暗号論的安全な乱数値で満たしてくれる という特徴があります。
コード例は下記のようになります。
// 乱数を入れる配列を作る(長さ16の Uint8Array) const array = new Uint8Array(16); // getRandomValuesで埋める crypto.getRandomValues(array); console.log(array);
実行すると、array
は 0〜255 のランダムな整数値で満たされます。
たとえば [123, 34, 90, ...]
のような結果になりますが、これらは後続の値を推測されにくいように生成されます。
パスワードやトークンの生成
もし一時的なパスワードやトークンを生成したい場合には、得られたバイト列を文字列に変換して利用したりします。
例として、以下のような関数で「ランダムな文字列」を生成してみましょう。
/** * 指定バイト数の暗号論的安全な乱数を * 文字列に変換して返す * lengthは生成したいバイト数 */ function generateRandomString(length) { const array = new Uint8Array(length); crypto.getRandomValues(array); // 0〜255を、a-z, A-Z, 0-9 の範囲で表現 // ただし、符号化の方法には注意 // ここでは簡単のため Base64 エンコード風にする const base64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; let result = ""; for (let i = 0; i < array.length; i++) { // 0〜63に収めるために 6ビット分だけ考慮 // ここではただの例として 0〜255の値を4等分したようなイメージで処理 const index = array[i] & 63; result += base64chars[index]; } return result; } const randomStr = generateRandomString(16); console.log(randomStr);
このようにして得られた文字列を、一時パスワードや認証トークンとして利用するわけです。
ただし、実運用の際は桁数や文字の制限、衝突のリスクなどに配慮する必要があります。
配列をシャッフルする方法
実務では、配列の要素をランダムに並び替える「シャッフル」が必要になることもしばしばあります。
たとえば、クイズの問題の順番をバラバラにしたい、抽選の名簿をランダムに取り扱いたい、といったシーンです。
フィッシャー–イェーツのシャッフルアルゴリズム
よく知られた手法がフィッシャー–イェーツのシャッフルというアルゴリズムです。
シャッフルを正しく行うために最も一般的なアルゴリズムの一つで、以下のように実装できます。
function shuffleArray(array) { // 配列を直接書き換えないように、コピーを作成 const newArr = array.slice(); for (let i = newArr.length - 1; i > 0; i--) { // 0 〜 iの範囲でランダムな整数を生成 const j = Math.floor(Math.random() * (i + 1)); // 要素を入れ替え [newArr[i], newArr[j]] = [newArr[j], newArr[i]]; } return newArr; } // 例: シャッフルの実行 const original = [1, 2, 3, 4, 5, 6]; const shuffled = shuffleArray(original); console.log(original); // [1, 2, 3, 4, 5, 6] のまま console.log(shuffled); // 例: [4, 2, 6, 5, 1, 3]
こうすることで、配列内の要素を「重複なく」「ほぼ均等な確率で」シャッフルできます。
UI上で商品の順番をランダムに並べたい場合にも、よく使われる方法です。
また、多くのライブラリやフレームワークで同様の関数が提供されていることもありますが、ロジック自体はシンプルなので、実装してしまうのも一つの手です。
ランダム色の生成
Webサイトやアプリケーションのデザイン上、「ランダムに色を変えたい」と思うことがあるかもしれません。
RGB形式でのランダム色
最も単純には、rgb( R, G, B )
の各成分を0〜255の範囲でランダムに選ぶだけです。
function getRandomColor() { const r = Math.floor(Math.random() * 256); const g = Math.floor(Math.random() * 256); const b = Math.floor(Math.random() * 256); return `rgb(${r}, ${g}, ${b})`; } const randomColor = getRandomColor(); console.log(randomColor); // 例: rgb(123, 45, 200)
背景色をランダムにしたい要素などに、上記の関数を適用するだけで様々なカラーに切り替わる演出ができます。
実務での注意点
たとえば、ボタンやテキストの色をランダムで設定すると可読性が落ちてしまうリスクがあります。
実務では、ある程度色合いに制限をもうけたり、配色の組み合わせをあらかじめ定義しておく方が望ましい場合も多いです。
また、ユーザーの操作を邪魔しないよう、ランダム色の使いどころは慎重に考える必要があるでしょう。
乱数を使った文字列操作とサンプルユースケース
乱数は数値だけでなく、「文字列」を扱うときにも登場します。
先ほど例として、暗号論的安全な乱数をBase64の文字列に変換する方法を紹介しました。
ここではもう少し身近な例として、ランダムな英数字を生成する関数を見てみましょう。
function generateRandomAlphaNumeric(length) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; let result = ""; for (let i = 0; i < length; i++) { // charsからランダムに1文字を選び出す const index = Math.floor(Math.random() * chars.length); result += chars[index]; } return result; } const randomString = generateRandomAlphaNumeric(10); console.log(randomString); // 例: Ab3Xf19zQw
これはあくまで擬似乱数なので、セキュアな目的で使うには不十分なことが多いです。
しかし、たとえば「ランダムなクーポンコードをUI上で表示」などの用途であれば十分役立ちます。
実務の現場では「ある程度予測しやすくても問題ない」部分と「絶対に予測されてはいけない」部分を区別して、実装を分けると良いでしょう。
乱数生成におけるパフォーマンス上の注意点
JavaScriptの乱数生成は、通常のWebアプリケーションであればそこまでパフォーマンスを気にする場面は多くありません。
しかし、リアルタイムで大量に乱数を扱うケース、たとえばデータ分析的な処理や、映像・音声の動的生成などを行う場合は、パフォーマンスへの配慮が必要になるかもしれません。
Math.random() の連続呼び出し
Math.random()
は比較的軽量ですが、極端に何万回も繰り返すような処理を行う場合は、全体のパフォーマンスに影響を与える可能性があります。
ただし普通のWebサイトで、ユーザーがボタンを押したときに1度だけ乱数を取得するといった場合には、大きな問題にならないでしょう。
crypto.getRandomValues() の負荷
crypto.getRandomValues()
は暗号論的安全性を担保するため、ある程度の計算コストやシステムリソースを必要とします。
頻繁に呼び出すとパフォーマンスが落ちる可能性があります。
大規模な乱数データを暗号論的安全な方法で生成するのであれば、クライアントサイドJavaScriptではなく、必要に応じてバックエンド側で処理を分担することも考えられます。
実務でのヒント
基本的には、乱数を頻繁に呼ぶ場合は「本当に暗号論的安全性が必要か」をまず判断するのが大切です。
それほど機密性を要しないタスクであれば、Math.random()
だけでも十分ですし、大量に呼び出す場合にはアルゴリズムの工夫も検討しましょう。
よくあるトラブルシューティング
乱数を使うときにありがちな疑問点やトラブルをいくつか挙げてみます。
なぜか同じ値ばかりが出る
ブラウザのキャッシュや、ループの書き方に問題がある可能性があります。
また、テスト時に何度も再実行しているときにコンソールが同じような値を表示しているだけかもしれません。
1度ごとに実行されるタイミングを確認してみてください。
意図した範囲外の整数が出力される
たとえば getRandomInt(1, 6)
のような関数を書いたのに、なぜか 0
や 7
が返ってくるケースは、多くの場合コードのロジックに誤りがあります。
Math.floor(Math.random() * (max - min + 1)) + min
の部分が崩れていないかをチェックしましょう。
セキュアにしたつもりが実は違う
Math.random()
ではなく crypto
を使っているつもりが、実際には一部のコードで Math.random()
を使い回していた、ということが起きるかもしれません。
暗号論的安全を確保したいなら、必ず該当箇所の実装が crypto.getRandomValues()
によるものになっているか確認するとよいでしょう。
セキュリティが要求される場面では、一部が擬似乱数に置き換わっていると大きな事故につながる可能性があります。
乱数とシードの概念
一部のプログラミング言語や数値解析系のライブラリでは、擬似乱数の元となるシードを指定できます。
シードとは、擬似乱数アルゴリズムの初期値のようなもので、同じシードを与えれば毎回同じ順番の乱数が得られます。
JavaScriptでのシード指定
JavaScriptの標準機能で、Math.random()
のシードを指定する方法は用意されていません。
そのため、再現性のある乱数列を得たい場合には、サードパーティのライブラリを利用するか、独自に擬似乱数生成器を実装する必要があります。
大半のWebアプリケーションでは、特にこの点が問題になることは多くないかもしれません。
ただ、ゲームやシミュレーションなどで「毎回同じシードから同じ結果を再現したい」というニーズがある場合には、注意が必要です。
乱数を使った実務上のヒント
ここまでで、乱数に関する基本的な手法や注意点を一通り見てきました。
もう少しだけ、実際に業務の場で遭遇しそうなシーンについて補足します。
ゲームロジック
たとえば、ゲーム内で敵が落とすアイテムのテーブルを設定し、「80%の確率でポーション、15%の確率で防具、5%の確率で超レアアイテム」といった処理をする場合があります。
こういうときは、あらかじめ確率の分岐を決めておいて、Math.random()
の値が 0 ~ 0.8
の間ならポーション、0.8 ~ 0.95
の間なら防具、0.95 ~ 1.0
の間ならレア、といった分岐を行うわけです。
抽選システム
イベントの当選・落選を判定するときにも乱数が使われます。
ただし、ユーザーが多数いる大規模システムの場合は、サーバーサイドで一括管理することが多いです。
クライアントサイドのみで抽選ロジックを実行すると不正操作の余地が生まれる可能性があるため、クライアント側はあくまで結果を表示するだけ、としておくケースが多いでしょう。
ログイン周りでのトークン
セッションIDや認証トークンをJavaScriptだけで完結させると、セキュリティリスクが生じることがあります。
多くのフレームワークでは、サーバーサイドでトークンを生成して、クライアントに渡す設計が一般的です。
とはいえ、クライアントで必要に応じて一時的にランダムな値を生成する場面もあり得るため、暗号論的安全な乱数の概念はしっかり押さえておきましょう。
セキュアなトークンが必要な場合は、なるべくサーバーサイドでの安全な生成を検討してください。
テストやデバッグで乱数を扱うときの工夫
乱数を含む処理は、テストやデバッグが難しくなる場合があります。
連続実行するたびに異なる結果が出るため、不具合の再現性を確保しにくいというデメリットがあるのです。
対応策の例
- 固定値を使う:特定のテストでは固定の値を入れて、結果を比較しやすくする。
- 抽象化:乱数生成を行う部分を関数として切り出し、テスト時にはモックを差し込むなどしてコントロールする。
とはいえ、普通の規模のアプリケーションでは、そこまでシビアに乱数テストを管理しないケースも多いかもしれません。
あまり複雑に考えすぎず、「問題となる箇所だけは固定値でテストする」と割り切るやり方もあります。
ブラウザや環境による差異
JavaScriptは、ブラウザ環境だけでなくNode.jsなどのサーバーサイド環境でも動作します。
しかし乱数生成に関しては、以下のように違いがあるかもしれません。
crypto モジュール
- ブラウザの
crypto
オブジェクトは、crypto.getRandomValues()
を使って暗号論的安全な乱数を生成します。 - Node.js では、
require('crypto')
またはimport crypto from 'crypto'
のようにして暗号関連の機能を利用します。crypto.randomBytes()
というメソッドがあるなど、多少仕様が異なります。
Math.random() の実装
Math.random()
はECMAScriptの仕様に沿って各実装が提供しますが、ごくまれに特定のブラウザバージョンで結果の偏りが報告されたりすることがあります。
しかし、基本的には多くの環境で十分にランダムに近い挙動をします。
普段はあまり気にする必要はないでしょうが、ミッションクリティカルな用途の場合には、環境やバージョンの情報を細かく追うケースもあるかもしれません。
まとめ
ここまで、JavaScriptで乱数を扱うための基礎から実践的な利用例、セキュリティ面でのポイントなどをひととおり解説してきました。
乱数は一見シンプルに見えますが、用途によっては非常に奥深いテーマです。
たとえば、単にUIを変化させるだけなら Math.random()
で十分なことが多い一方、パスワード生成や認証など、第三者による不正なアクセスが懸念される場面では crypto.getRandomValues()
のような暗号論的安全な手法が求められます。
また、ゲーム開発のように頻繁に乱数を呼ぶ場合にはアルゴリズムの効率を考慮する必要がありますし、テスト時に再現性を持たせるにはそもそもシード指定をどうするかなど、いろいろ検討ポイントが増えてきます。
ですが、初心者の皆さんがまず押さえておきたいのは、「JavaScriptの標準関数として Math.random()
が使える」「少し高度なセキュリティが絡むなら crypto
の利用を検討する」という大枠です。
実務でどちらを使うか悩む場合は、セキュリティ要件やパフォーマンス要件を上司やクライアントに確認してから実装するのがよいでしょう。
こうした基本を理解しておけば、Webアプリケーションの細部にランダム性を加える際や、簡易的なゲームや抽選機能を実装する場面でも、スムーズに対応できるようになるはずです。
ぜひ今回紹介した考え方やコード例を参考に、みなさんのJavaScript開発に役立ててみてください。