Bashシェルスクリプトでランダムな数値(乱数)を生成する
はじめに
意外にも、初歩的な「乱数」に関するエントリを書いたことがなかった( _過去にサラリと触れていぐらいた_ )ようなので、今回ネタにすることにしました。
「ランダム」というキーワードを取り上げたエントリも参考に紹介します。
- Bash シェルスクリプトでランダムな文字列を生成する
- Bash シェルスクリプトで 1〜100 までの数からランダムに 10 個を重複なしで取り出す方法
- シェルで shuf コマンドを使って入力情報をいろんな方法でシャッフルしてみる(組み合わせにも使える)
検証環境
$ uname -moi
x86_64 x86_64 GNU/Linux
$ head -n 2 /etc/os-release
NAME="Ubuntu"
VERSION="21.04 (Hirsute Hippo)"
$ bash -version | head -n 1
GNU bash, バージョン 5.1.4(1)-release (x86_64-pc-linux-gnu)
乱数を取得するための組み込み変数「RANDOM」を使う
Bash には、乱数を取得するための組み込み変数として RANDOM というものが用意されています。
試しに $RANDOM
を出力してみましょう。
$ echo $RANDOM
458
$ echo $RANDOM
11854
echo $RANDOM
を実行するたびに、出力される数値が異なります。
"RANDOM" 変数の最大値は?最小値は?
RANDOM 変数は参照するたびにランダムな値を返しますが、値の範囲はどうなっているでしょうか?
試しに $RANDOM
を 1,000,000 回 出力した結果を集計してみます。
# 1,000,000回 ループして、 RANDOM 変数を参照する。
# 結果を ソート → 重複除去 してファイルに書き出す。
$ for i in {0..1000000}; do echo ${RANDOM}; done | sort -n | uniq > result.txt
# ファイルの行数は 32,768 行
$ wc -l result.txt
32768 result.txt
# 先頭5行を確認
$ head -n 5 result.txt
0
1
2
3
4
# 末尾5行を確認
$ tail -n 5 result.txt
32763
32764
32765
32766
32767
このように、 "RANDOM" 変数は 0 〜 32767 の間でランダムな数値を返却します 。
( 2 の 15 乗分の数値がランダムに算出されます。 )
# 2の15乗 を算出
$ echo $((2**15))
32768
任意の範囲の乱数を求める方法( ex: 0〜5 )
任意の範囲の乱数を求めたい場合にはどうしたらよいでしょう?
例えば、 サイコロの出目として 6 つの数値( 0〜5 )をランダムに取得したい場合 はどうしたら良いでしょう?
この場合も RANDOM
変数を利用することができます。
# $RAMDOM % 出目の数 を算出する
$ echo $((RANDOM % 6))
1
$ echo $((RANDOM % 6))
3
10,000 回 出力した結果を集計してみます。
# 10,000回 0〜5 までの数値を出力し、、数を数える
$ for i in {0..10000}; do echo $((RANDOM % 6)); done | sort -n | uniq -c > result2.txt
# 0〜5 の数値がそれぞれ、おおよそ 1667回 出力されている
$ cat result2.txt
1690 0
1669 1
1701 2
1620 3
1669 4
1652 5
コインの出目を求めたい場合には 2 で割ったあまり を算出します。
$ echo $((RANDOM % 2))
0
$ echo $((RANDOM % 2))
0
$ echo $((RANDOM % 2))
1
【2022-10-16 追記】6 つの数値(0〜5)をランダムに取得する際の注意
Twitter にてアドバイスを頂きました!
この方法だと、ごく僅かではありますが偏りが発生します ( 0 と 1 だけ、他の出目より 1/32767 だけ多くでる )。
これは RANDOM
変数が 0 〜 32767 ( 20-1 〜 215-1 )の間でランダムな数値を返却するためです。
RANDOM
変数の値を 3 つ加算 ( 0 〜 98303 ) し、範囲内に登場する数値の数が 6 で割り切れるようにすることでこの問題を解消できます。
$ echo $(((RANDOM + RANDOM + RANDOM) % 6))
0
$ echo $(((RANDOM + RANDOM + RANDOM) % 6))
2
"32767" 以上の乱数を算出したい場合は?
"32767" 以上の乱数を求めたい場合にはどうしたら良いでしょう?
RANDOM
変数を複数回加算する
Twitter にてアドバイスを頂きました!
RANDOM
変数を複数回足し合わせることで 32767 の上限を引き上げることができます。
RANDOM + RANDOM
で 0 〜 65535 、 RANDOM + RANDOM + RANDOM
で 0 〜 98303 のランダム値を求めることができます。
# 0 〜 65535 のランダム値を算出
$ echo $((RANDOM + RANDOM))
26306
$ echo $((RANDOM + RANDOM))
35702
# 0 〜 98303 のランダム値を算出
$ echo $((RANDOM + RANDOM + RANDOM))
21338
$ echo $((RANDOM + RANDOM + RANDOM))
70054
shuf
コマンドを使う
shuf
コマンドを使って 「1 から 100,000」 までの乱数を算出してみます。
$ shuf -i 1-100000 -n 1
92326
$ shuf -i 1-100000 -n 1
8180
$ shuf -i 1-100000 -n 1
28623
shuf
コマンドがない場合は?
shuf
コマンドがない場合は、以下のように seq
、 sort
、 head
を組み合わせて頑張りましょう。
# sort -R で標準入力をランダムに入れ替える
$ seq 1 100000 | sort -R | head -n 1
89938
$ seq 1 100000 | sort -R | head -n 1
11193
$ seq 1 100000 | sort -R | head -n 1
4539
ただ、複数コマンドを組み合わせていることもあり、 遅い です。
ひとこと
使えるなら RANDOM
で実現するのがよいでしょう。
ディスカッション
コメント一覧
まだ、コメントがありません