JavaScript、乱数の範囲や重複を指定〜Math.random使い方。





こんにちは、「ふ」です。
JavaScriptでランダムな数値を取得する場合にはMath.random( )メソッドを使用します。ただしそれ単体では「0以上1未満の乱数」しか返ってきません。
整数値や指定した範囲の乱数が欲しいときには、そこからまた手を加える必要があります。

そこで今回は、目的に応じたMath.randomメソッドの使い方について紹介していこうと思います。
⬇︎には、各処理のsyntax(構文)にジャンプする「クイック目次」も用意しました。ピンポイントで知りたい方はご利用ください。



クイック目次
(目的のsyntaxまで飛びます)

乱数にはMath.random()。





ランダムな数値を返す。


Math.random();

JavaScriptで乱数を生成するには、Math.random( )メソッドを使います。
実行すると、0以上1未満のランダムな数値が返されます。

例。

試しに5回行ってconsoleに出力してみます。

JavaScript

for(i=0;i<5;i++) { console.log(「「1Math.random()」」); }

▶︎ console

0.45237172804129444 0.7440626001973654 0.9763285165036872 0.1251404767451707 0.16411709225645899

ものすごい詳細度で数値が返ってきました。軽く億を超えています。

この「0以上1未満」の数値を加工することで、目的に応じた「ランダムな数の抽出」に利用します。

気を付けたいのが「0以上1未満」が返される、ということ。
「0」は含まれますが、「1」が返されることはありません。この点を意識して加工しないと、思った結果が得られない場合があります。

ランダムな整数を取得。





1〜nの整数に丸める。


Math.ceil(Math.random()*「「1n」」);



Math.random( )でそのまま返ってくるのは「0以上1未満の小数」です。
これを1からnの整数に丸めたいとき。

① Math.random( )で返された値をn倍する
→ 0からn未満の整数もしくは小数が得られます。

② 小数点以下を切り上げ
Math.ceil( )は、小数点以下を切り上げて整数に丸めるメソッドです。
0からn未満の小数点以下を切り上げると、1からnまでの整数が得られます。



例。

1〜5の整数をランダムに取得する、というのをやってみましょう。

for(i=0;i<5;i++) { let random = Math.random( ); console.log(random「「1*5」」); }

① 先づMath.random( )の帰り値に「5」を掛け合わせます。これが構文の「n」にあたる部分ですね。

▶︎ console

4.824717324676763 3.555600670128495 2.1807659758899 0.7854187153365677 1.9421816730907504

「0以上1未満」の乱数に「5」を掛けたので、「0以上5未満」の乱数が得られます。



for(i=0;i<5;i++) { let random = Math.random( ); console.log(random*5); console.log(「「1Math.ceil」」(random*5)); }

その上でMath.ceilを使って小数点以下を切り上げます。

▶︎ console

5 4 3 1 2

ランダムな整数を取り出すことができました。





0〜(n-1)のランダムな整数。


Math.floor((Math.random()*「「1n」」));

Math.floorメソッドは、Math.ceilとは逆に小数点以下を切り捨てて整数に丸めます。
すると0〜n-1までの整数をランダムに発生させることができます。

n個の要素を持つ配列や親要素から中身をランダムに取り出したいとき。
その場合は「0以上、(n-1)以下」が返ってきたほうが都合いいですね。





0〜nの整数に丸める。


Math.floor((Math.random()*「「1(n+1)」」));

最小値0も、最大値nも、結果に含めたいとき。これは先程のsyntaxを少し変形させれば得ることができます。
Math.randomに(n+1)を掛け合わせて、Math.floorで切り捨て。

例えば「0〜5」のランダム整数が欲しいときには5+1、つまり「6」を掛けてやればOKです。

乱数の範囲指定。


つぎは指定した範囲で乱数を取得する方法です。
必要とする乱数が、いつも0や1からとは限りません。取得範囲の最小値をm、最大値をnとしたときの、乱数取得について紹介します。




m以上n未満の整数。


Math.floor(Math.random()*(「「1n」」-「「1m」」)+「「1m」」);

基本的な仕組みはこう⬇︎なっています。

「mからnの間の乱数」ということは、全ての返り値が最小値でのm以上であるということが言えます。
返される数値はmとnの「差」の部分でだけ、でランダムが発生しているということですね。

そのため「差」の範囲で乱数を発生させ、後から最小値を足します。そして最後にMath.floorまたはMath.ceilで整数に丸めます。

例。

「6以上、10未満のランダムな整数」でやってみましょう。5回繰り返します。

for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*(「「110-6」」)); }

はじめにMath.randomの返り値に「最大値 - 最小値」を掛ける。

▶︎ console

1.6209758412966098 0.07181268730240564 3.6172163573123104 2.40201686813249 1.552105481614972

「10 - 6 = 4」につき、0以上4未満の乱数が返ってきます。




for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*(10-6)); console.log(random*(「「110-6」」)+「「16」」); }

そこに最小値を足します。

▶︎ console

7.6209758412966098 6.07181268730240564 9.6172163573123104 8.40201686813249 7.552105481614972

0以上4未満の乱数に「6」を足したので、6以上10未満の乱数になりました。




for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*(10-6)); console.log(random*(10-6)+6); console.log(「「1Math.floor」」(random*(10-6)+6)); }

そしてMath.floor( )で小数点以下を切り捨て。

▶︎ console

7 6 9 8 7

「6以上10未満」のランダムな整数が取得できました。

「最小値以上、最大値未満」の形を紹介しましたが、他のケースもみてみましょう。




最小値を含めない、最大値以下


Math.ceil(Math.random()*(「「1n」」-「「1m」」)+「「1m」」);

「最小値以上最大値未満」の逆なので、さきほどの「切り捨て」処理を「切り上げ」処理に変更するだけです。




最大値も最小値も含む


Math.floor(Math.random()*(「「1n」」-「「1m」」+1)+「「1m」」);

ランダム部分である「n - m」に1を足してMath.floorで切り捨てています。
例で使った「6以上10未満」で考えると、「6以上11未満」の処理をしていることに成増。




最大値も最小値も含まない


Math.ceil(Math.random(「「1n」」-「「1m」」-1)+「「1m」」);

今度はランダム部分から1を引いてMath.ceilで切り上げ。
2つ前のシンタックスでやった「最小値を含まない、最大値以下」の変形です。
「6を含まない10以下」を「6を含まない9以下」に加工しています。

小数点。


整数ばかりやっていたので、小数点以下を出力する方法を紹介します。





0からnまでの、小数点以下o桁の乱数


Math.floor(Math.random()*「「1n」」*10**「「4o」」)/10**「「4o」」

「*(アスタリスク)」の2連は「べき乗」を表します。
目的の小数点以下を返すには、Math.floorの引数内でrandomの返り値を10のo乗させ、floorの外側を10のo乗で割ります。

例。

0以上5未満、小数点以下2桁のランダムをやってみます。

for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*「「15」」*10**「「42」」); }

Math.randomの返り値に5を掛けて、さらに10の2乗を掛けます。

▶︎ console

175.49110799286848 242.3542793168003 481.8882838501146 452.4635926416928 35.40569618415568

randomの帰り値が5X100倍されました。




for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*5*10**2); console.log(「「1Math.floor」」(random*5*10**2)); }

Math.floorで小数点以下を切り捨てます。

▶︎ console

175 242 481 452 35

基本的に3桁の整数になりました。
1番下の「35」は最初のMath.random( )で返ったものがかなり小さかったのでしょう。でも問題ありません。




for(i=0;i<5;i++) { let random = Math.random(); cosole.log(random*5*10**2); console.log(Math.floor(random*5*10**2)); console.log(Math.floor(random*5*10**2)/10**「「42」」); }

さいごに10の2乗で割ってやると....

▶︎ console

1.75 2.42 4.81 4.52 0.35

「0以上5未満」の、小数点以下2桁のランダム数が得られました。

重複を避ける。


ランダムかつ、同じものを返したくないというケースはよくあると思います。
例えば、

A〜Eと書かれた5枚のカードがあり、2名にそれぞれ1枚ずつ配るという状況。
このとき「5枚の中から1枚ずつ取る」ので、取り出された2枚の内容が重複してはいけません。「1人目のカードはA、もう1人のカードもA」という事態はあってはならない。

Math.random( )を5つの整数に収束させるとすぐに結果がカブってしまいそうです。ここはwhile文を使って、「重複しなくなるまでカードを配り直す」ことにしました。




重複しない2つを取り出す。


「「3//変数を宣言しておく」」 let m; let n; 「「3//2つが同じじゃなくなるまで繰り返せ」」 while(m == n) { m = Math.ceil(Math.random()*5); n = Math.ceil(Math.random()*5); }

変数n、mともに、1から5までのランダムな整数を代入しています。ただしwhile文で「mとnが同じ値なら、処理をやりなおしなさい」との縛りをかけているので、重複は許されません。

処理内容をfor文に入れて繰り返し、そのつど出力してみましょう(変数宣言もローカルにしてください)。

for(i=0;i<5;i++) { 「「3//変数を宣言しておく」」 let m; let n; 「「3//2つが同じじゃなくなるまで繰り返せ」」 while(m == n) { m = Math.ceil(Math.random()*5); n = Math.ceil(Math.random()*5); } 「「1console.log("mは" + String(m)+ ",nは" + String(n));」」 }

▶︎ console

mは1、nは3 mは5、nは3 mは4、nは5 mは3、nは4 mは4、nは5

重複しないランダム数字を取り出すことができました。

シャッフル(ランダムな並び替え)。


Math.random( )を使って、いくつかある要素の順番をランダムに並び替えることもできます。

やり方としては、各要素を配列にセットして順に選択していくのですが、これについても同じものを重複して選ばないようにしてくてはいけません。
とはいうものの。先ほどの「2枚選択」ならまだしも、要素全てに対してwhileの条件割り当てをするのもめんどくさい。

そこでとった作戦。

配列の中から1枚取り出したら..

その要素を配列から削除し、次回はその中から選択させる。
これで「重複しないで」並べ替えることができます。

例。

AからEの5枚のカード、これをランダムに並び替える、というのをやってみましょう。





ランダムに並び替える。


function narabekae() { 「「3//要素を配列に格納」」「「5 ..①」」 let arr = ["A","B","C","D","E"]; 「「3//最初の要素数を覚えておく」」「「5 ..②」」 let deflength = arr.length; 「「3//最初の要素数、くりかえし」」「「5 ..③」」 for(i=0;i<deflength;i++) { 「「3//現在の要素数から、添字をランダムに取り出す」」「「5 ..④」」 let index = Math.floor(Math.random()*arr.length); 「「3//出力」」「「5 ..⑤」」 console.log(arr[index]); 「「3//添字の要素を配列から削除」」「「5 ..⑥」」 「「1arr.splice(index,1);」」 } }

ここでは関数定義にしました。
配列をfunctionの中で宣言しておくことで、関数終了後に処理した配列の内容がリセットされるので便利です。

① 要素を配列に格納しています。

② ①で格納したデフォルトの要素数を覚えておきましょう。そして、

③ デフォルトの要素数②の数だけ、取り出し処理を行います。

④ 要素数は取り出しごとに減っていくのですが、現時点での要素数(arr.length)から、添字をランダムに取り出し。

⑤ consoleに出力しましょう。

⑥ 取り出した添字の要素を配列から削除。

splice( )関数を使用します。第1引数は添字、第2引数は「添字からいくつ分削除するか」を記述します。ここでは該当要素のみなので「1」ですね。

〜ではこのnarabikae( )を、3回ほど実行してみましょう。

for(b=0;b<3;b++) { 「「3//空白を入れる」」 console.log(""); narabekae(); }

▶︎ console

A B D E C C D E B A A D C E B

要素が重複することなく、「ランダム並び替え」ができています。

前の「重複しない2枚を取り出す」の発展系で、3枚や4枚を取り出す場合にも。このランダム並び替えを使うと、複雑な条件分けをせずに簡単に処理ができます。

等しい確率です。


今回はMath.random( )の使い方について長々とお話ししてきました。 最後までお読みくださり、ありがとうございました。

それぞれの処理について。
他にもやりかたは考えられるのですが、実用性を考えて、極力「等しい確率でランダムに返ってくる」方法をチョイスしました。

これらを使っておみくじや画像のランダム表示など、楽しいwebサイトを構築してください。ではまた〜 ♪



雨のアニメーションをSVGで作ろう。

2021.05.30
webページ上に雨を降らせます。

SVGでボタンアニメーション【Web Animations API】。

2020.12.19
event.targetからオブジェクトを動かす。











「ふ」です。

swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。