web-dev-qa-db-ja.com

Bash:$ RANDOMを使用してランダムな浮動小数点数を生成する方法

Integer Random Generator $ RANDOMを使用して、特定の精度で特定の範囲の実際の乱数を生成することは可能ですか?たとえば、0と1の間の4つの精度で実数を生成するにはどうすればよいですか?

0.1234
0.0309
0.9001
0.0000
1.0000

簡単な回避策:

printf "%d04.%d04\n" $RANDOM $RANDOM
5
sci9
_awk -v n=10 -v seed="$RANDOM" 'BEGIN { srand(seed); for (i=0; i<n; ++i) printf("%.4f\n", Rand()) }'
_

これにより、[0,1)の範囲の10進数4桁のn乱数(この例では10)が出力されます。 awkRand()関数を使用します(標準のawkではなく、最も一般的なawk実装によって実装されます)。その範囲のランダムな値を返します。乱数ジェネレーターは、シェルの_$RANDOM_変数によってシードされます。

awkプログラムにBEGINブロックしかない(他のコードブロックがない)場合、awkは標準入力ストリームから入力を読み取ろうとしません。

OpenBSDシステム(または、同じ jotユーティリティ が元の4.2BSDにあるシステム)では、次のように指定したとおりに10の乱数が生成されます。

_jot -p 4 -r 10 0 1
_
9
Kusalananda

別の答えで指摘されているように、乱数を生成するために使用できる他のユーティリティがあります。この回答では、リソースを_$RANDOM_といくつかの基本的な算術関数に制限しています。

浮動小数点数については、次のようなことを試してください

_printf '%s\n' $(echo "scale=8; $RANDOM/32768" | bc )
_

_$RANDOM_は0〜32767の数値のみを生成するため(32767を含む!)、最高の精度が得られます。しかし、bcを呼び出すことにより、基本的な算術関数の使用に関する私の規則も破りました。

しかし次に進む前に、2つの問題precisionrange浮動小数点数の場合。その後、整数の範囲の生成について見ていきます(整数を生成できる場合は、後でそれを除算して10進数にすることができます。これを実現したいユーティリティを使用したい場合)。

精度

_$RANDOM/32768_のアプローチを採用すると、_$RANDOM_は0から32767までの値を生成するため、_$RANDOM/32768_の結果も同様に有限の値になります。言い換えれば、それはまだ離散確率変数です(そして、コンピューターでは、その事実から逃れることはできません)。これを念頭に置いて、printfを使用することで、ある程度の精度を達成できます。

間隔をより細かくカバーしたい場合は、ベース32768から考え始めることができます。したがって、理論的には、_$RANDOM + $RANDOM*32768_は0から1,073,741,823までの均一な分布を与えるはずです。しかし、コマンドラインがその精度を非常にうまく処理できるかどうかは疑問です。この特定のケースに関するいくつかのポイント:

  • 2つの独立した均一に分布した確率変数の合計は、一般的に均一ではありません。この場合、少なくとも理論的には(3番目のポイントを参照)、そうです。
  • $RANDOM + $RANDOM*32768 = $RANDOM * ( 1 + 32768 )を簡略化できるとは思わないでください。 _$RANDOM_の2つの発生は、実際には2つの異なるイベントです。
  • このように2回呼び出すと本当に2つの独立したランダムイベントが生成されるかどうかを知るには、_$RANDOM_がどのように生成されるのか十分にわかりません。

範囲

_$RANDOM/32768_だけを考えてみましょう。範囲内の数値が必要な場合は、_[a,b)_と言います。

_$RANDOM/32768*(b-a) + a
_

目的の範囲に着陸します。

整数値の生成

まず、_[0,b)_の間で乱数を生成することを検討してください。ここで、bは_32768_より小さいです。製品_q*b_について考えてみます。ここで、qは_32768/b_の整数部分です。次に、0から32767までの乱数を生成することができますが、_q*b_以上の乱数を破棄します。このように生成された番号をGに呼び出します。次に、Gは0から_q*b_の範囲になり、その分布は均一になります。次に、モジュラー演算を適用して、この値を目的の範囲に絞り込みます。

_G % b
_

次のようにランダムに数値を生成することに注意してください

_$RANDOM % b
_

bが_32768_の除数の1つである場合を除いて、均一分布は作成されません。

このためのbashスクリプトを作成

上記のように_q*b_を計算するのは面倒に聞こえます。しかし、実際にはそうではありません。次のようにして入手できます。

_q*b = 32768 - ( 32768 % b )
_

バッシュでは、これを得ることができます

_$((32768 - $((32768 % b)) ))
_

次のコードは、_0..b_(bを含まない)の範囲の乱数を生成します。 _b=$1_

_m=$((32768 - $((32768 % $1)) ))
a=$RANDOM
while (( $a > $m )); 
do
    a=$RANDOM
done
a=$(($a % $1))
printf "$a\n"
_

補遺

技術的には、作業する理由はほとんどありません

_m=$((32768 - $((32768 % $1)) ))
_

以下は同じことを達成します

_a=$RANDOM
while (( $a > $1 )); 
do
    a=$RANDOM
done
printf "$a\n"
_

それははるかに多くの作業ですが、コンピュータは高速です。

より大きな範囲で整数を生成する

これを理解させましょう。注意が必要であり、ある時点で、算術演算を処理する際のコンピュータのメモリ制限を考慮する必要があります。

最終メモ

受け入れられた回答は、0から1まで一様に乱数を作成しません。

これを確認するには、次を試してください

_$ for i in {1..1000}; do echo .$RANDOM; done | awk '{ a += $1 } END { print a }'
_

_[0,1)_の真に均一な分布の場合、平均は_0.500_に近いはずです。

しかし、上記のスニペットを実行するとわかるように、代わりに_314.432_または_322.619_のようなものが取得されます。 1000の数字なので、平均は_.322_です。この一連の生成された数値の真の平均は_.316362_です。

Perlスクリプトを使用して、この真の平均を取得できます

_  Perl -e '{ $i=0;  
             $s=0; 
             while ( $i<=32767 ) 
               { 
                 $j = sprintf "%.5f", ".$i"; 
                 $j =~ s/^0\.//; 
                 print "$j\n"; 
                 $s += $j; 
                 $i++ 
               }; 
             printf "%.5f\n", $s/32767; 
           }' 
_

ここに整数を追加して、_.$RANDOM_を使用するこのアプローチが、あなたが最も望んでいることを実行していないことを確認できるようにしています。言い換えれば、どの整数が生成され、どの整数が完全に失われるかを考えてください。かなりの数がスキップされます。かなりの数が倍増しています。

6
A.Ellett

シェルのprintfが%a形式(bash ksh zshなど)を理解できるため、内部ベース変更(hex-> dec)([0,1)の範囲で均一)を実行できるシステム0.00003から0.99997):

printf '%.5f\n' "$(printf '0x0.%04xp1' $RANDOM)"

$RANDOM(0.000000001から0.999999999へ)への呼び出しを組み合わせることにより、さらに多くの桁を使用することもできます

printf '%.9f\n'  "$(printf '0x0.%08xp2' $(( ($RANDOM<<15) + $RANDOM )))"

内部(シェルに対して)の「$ RANDOM」アルゴリズムは、線形フィードバックシフトレジスタ(LFSR)に基づいています。これらは、暗号的に安全な疑似乱数ジェネレータ(CSPRNG)ではありません。より適切なオプションは、/dev/urandomデバイスからのバイトを使用することです。そのためには、外部の8進または16進ダンプを呼び出す必要があります。

$ printf '%.19f\n' "0x0.$(od -N 8 -An -tx1 /dev/urandom | tr -d ' ')"
0.7532810412812978029

$ printf '%.19f\n' "0x0.$(hexdump -n 8 -v -e '"%02x"' /dev/urandom)"
0.9453460825607180595

フロートを取得するための非常に単純な(ただし、均一ではない)ソリューションは次のとおりです。

printf '0.%04d\n' $RANDOM

[0,1)(1を含まない)の範囲で均一にする方法:

while a=$RANDOM; ((a>29999)); do :; done; printf '0.%04d\n' "$((a%10000))"
6
Isaac

$(( ( RANDOM % N ) + MIN ))を使用します

NをMAX番号で置き換え、MINを生成する最小番号で置き換えます(MAXは排他的であるため、Nは_N+1_にして、MAXとMINの両方を含めます)。

または、代わりに$(shuf -i MIN-MAX -n 1)を使用できます。

から _man shuf_

_-i, --input-range=LO-HI
    treat each number LO through HI as an input line
-n, --head-count=COUNT 
    output at most COUNT lines
_

ここでshufの_-n 1_は、1つの乱数のみを生成することを意味します。

これは0〜9999の間の乱数を生成し、printfを使用して先頭にゼロを付けます(結果として、数値1は排他的です)。

_printf "0.%04d\n" $(( RANDOM % 1000 ))
0.0215
_
4
αғsнιη

バッシュで

bc -l <<< "scale=4 ; $((RANDOM % 10000 ))/10000"

どこ 1/10000はランダムな精度であり、4出力精度の桁数

1
Andrea993

zshには、_zsh/mathfunc_モジュール内にRand48()算術関数(erand48()標準関数のラッパー)があります。

_zmodload zsh/mathfunc
printf '%.4f\n' $((Rand48()))
_
0