awk(gawk)には乱数を生成する組み込み関数としてrand
が用意されているのですが、たとえば以下のようなソースコードで検証ができるように、実行のたびに同じような乱数列を生成してしまいます。
for _ in $(seq 5); do
awk 'BEGIN{print(rand(), rand(), rand())}'
done
# 0.237788 0.291066 0.845814
# 0.237788 0.291066 0.845814
# 0.237788 0.291066 0.845814
# 0.237788 0.291066 0.845814
# 0.237788 0.291066 0.845814
awkの仕様なのか、gawkの実装がそうなのか、正直なところよくわからないのですが、乱数生成のシード値の初期値が一定になっているらしく、そのために実行のたびに同じような乱数列が生成されてしまうようです。したがって実行のたびに異なった乱数列を生成したいという場合はsrand
を使って、シード値を変更すればよい――と思って書いたのが以下のコードになります。
for _ in $(seq 5); do
awk 'BEGIN{srand(); print(rand(), rand(), rand())}'
done
# 0.589234 0.904348 0.441211
# 0.589234 0.904348 0.441211
# 0.589234 0.904348 0.441211
# 0.50575 0.499608 0.730847
# 0.50575 0.499608 0.730847
結果から述べると、想定通りに動作しませんでした。確かにsrand
がシード値を書き換えてはくれているようですが、まだまだ同じような乱数列を生成してしまう。この原因をいろいろ調べたところ、srand
は引数を省略したとき、実行時の時刻をシード値に設定するのですが、この「時刻」の単位が「秒」。つまり1秒以内にsrand
を呼び出すと、同じシード値が設定されてしまい、結果として同じ乱数列が生成されてしまいます。
よってsrand
の引数は省略せず、自前で用意したシード値を設定する必要がありそうです。そしてそのシード値を自前で用意する方法ですが、awkの外部で生成した乱数を利用することとします。たとえばbashの組み込み変数RANDOM
で生成した乱数を-vオプションを使って、awk内部に引き渡し、これをsrand
の引数(=乱数のシード値)としたところ、想定通り動作しました(´・ω・`)
for _ in $(seq 5); do
awk -v seed="${RANDOM}" 'BEGIN{srand(seed); print(rand(), rand(), rand())}'
done
# 0.975518 0.212486 0.0285477
# 0.838674 0.664669 0.070124
# 0.314944 0.212045 0.00891201
# 0.725379 0.64815 0.805276
# 0.109255 0.713515 0.799796
-vオプションの利用が難しい場合は「外部コマンドで乱数を生成、それをgetline
で拾って、シード値とする」という方法でも大丈夫そうです(´・ω・`)
for _ in $(seq 5); do
awk 'BEGIN{
"seq 32767 | shuf -n 1" | getline seed
srand(seed);
print(rand(), rand(), rand())
}'
done
# 0.711337 0.206352 0.0366282
# 0.614835 0.52362 0.556768
# 0.666671 0.18371 0.66057
# 0.964642 0.860411 0.500877
# 0.95293 0.590551 0.826792