$ RANDOMを使用すると、結果が不均一に広がるのはなぜですか?


14

ウィキペディアでRNGを読み、TLDPで$RANDOM機能しますが、実際にはこの結果を説明していません。

$ max=$((6*3600))
$ for f in {1..100000}; do echo $(($RANDOM%max/3600)); done | sort | uniq -c
  21787 0
  22114 1
  21933 2
  12157 3
  10938 4
  11071 5

約2倍以上の値が3、4、5よりも0、1、2になる傾向があるのに、最大モジュロを変更すると、10個すべての値にほぼ均等に広がるのはなぜですか?

$ max=$((9*3600))
$ for f in {1..100000}; do echo $(($RANDOM%max/3600)); done | sort | uniq -c
  11940 0
  11199 1
  10898 2
  10945 3
  11239 4
  10928 5
  10875 6
  10759 7
  11217 8

9
これに対する通常の答えは、RANDOMの最大値とモジュロに均等に分割できる最大値との間にある場合、リロール(受信した番号を破棄して別のものを選択)することです。これは通常のランダムではなく、すべての言語/ツール/その他で通常使用されるモジュロから制限されるRNGドメインです。そのタイプのRNGを実装します。
チャールズダフィー

7
あなたはそれを取得どのように悪いのいくつかの素敵なグラフたい場合は、このバイアスの元に私の2013年の記事を参照してください。ericlippert.com/2013/12/16/...を
エリックリペット

1
「乱数の生成はあまりにも重要なので、偶然に任せることはできません」-ロバート・コベユー。
参考

@Eric Lippertありがとう、私は喜んでそれを読みます!
cprn

1
モジュロバイアスによる問題が発生して$RANDOMいて、変数は内部で適切なPRNGを使用しないことに注意してください。
フォレスト

回答:


36

モジュロバイアスのトピックを展開するための式は次のとおりです。

max=$((6*3600))
$(($RANDOM%max/3600))

また、この式で$RANDOMは、0〜32767の範囲のランダムな値です。

   RANDOM Each time this parameter is referenced, a random integer between
          0 and 32767 is generated.

これが可能な値にどのようにマッピングされるかを視覚化するのに役立ちます。

0 = 0-3599
1 = 3600-7199
2 = 7200-10799
3 = 10800-14399
4 = 14400-17999
5 = 18000-21599
0 = 21600-25199
1 = 25200-28799
2 = 28800-32399
3 = 32400-32767

したがって、式では、0、1、2の確率は4、5の確率の2倍になります。また、3の確率は4、5よりもわずかに高くなります。したがって、0、1、2が勝者、4、5が敗者の結果です。

に変更すると9*3600、次のようになります。

0 = 0-3599
1 = 3600-7199
2 = 7200-10799
3 = 10800-14399
4 = 14400-17999
5 = 18000-21599
6 = 21600-25199
7 = 25200-28799
8 = 28800-32399
0 = 32400-32767

1〜8の確率は同じですが、0にはわずかな偏りがあるため、テストでは100'000回の反復で0が勝者でした。

モジュロバイアスを修正するには、最初に式を単純化する必要があります(0〜5のみが必要な場合、モジュロは3600ではなく6であり、クレイジーな数でもありません)。この単純化だけで、バイアスが大幅に削減されます(32766は0にマップされ、32767は1にマップされ、これら2つの数値に小さなバイアスが与えられます)。

バイアスを完全になくすには、(たとえば)$RANDOMがを下回る場合32768 % 6(利用可能なランダムな範囲に完全にマッピングされない状態を排除する)に再ロールする必要があります。

max=6
for f in {1..100000}
do
    r=$RANDOM
    while [ $r -lt $((32768 % $max)) ]; do r=$RANDOM; done
    echo $(($r%max))
done | sort | uniq -c | sort -n

テスト結果:

  16425 5
  16515 1
  16720 0
  16769 2
  16776 4
  16795 3

別の方法は、注目に値するバイアスを持たない別のランダムソースを使用することです(32768の可能な値よりも桁違いに大きい)。ただし、とにかく再ロールロジックを実装しても問題はありません(たとえたぶん実現しなかったとしても)。


「$ RANDOMが32768%6未満の場合に再ロールする必要がある」を除いて、答えはおおむね正しいです。実際には「floor((RANDMAX + 1)/ 6)* 6」以上(つまり32766) )、およびその下の関連するシェルコードを修正します。
ナユキ

@Nayuki特定のエラー(特定のコンテキスト内で適用される)を指摘できる場合、それを修正させていただきます。私の解決策はほんの一例であり、さまざまな方法があります。開始範囲、終了範囲、または中間のいずれかからバイアスを削除できますが、違いはありません。あなたはそれをより良く計算することができます(そして、すべての繰り返しでモジュロをしません)。任意のモジュロやrandmax値などの特殊なケースを処理できます。また、RANDMAX + 1が存在しないRANDMAX = INTMAXを処理できますが、ここでは焦点ではありませんでした。
frostschutz

返信は投稿よりも著しく悪いです。まず、あなたのどのフレーズが事実上間違っているかを具体的に指摘しました。"32768%6" == 2なので、$ RANDOM <2になるたびに再ロールすることに注意してください。範囲の開始/終了/中間のバイアスについては、投稿全体が範囲の終了時のバイアスを除去することであり、私の回答はまさにそれにも対応しています。3番目に、RANDMAX = INTMAXの処理について説明しますが、回答の中で値32768(= 32767 + 1)を何度も言及しました。これは、RANDMAX + 1の計算に慣れていることを意味します。
ナユキ

1
@Nayuki私のコードは0と1を削除し、あなたのコードは32766と32767を削除します。詳細を教えてください。どのような違いがありますか?私は人間であり、間違いを犯しますが、あなたがこれまでに言ったのは、理由を説明したり示したりせずに「間違っている」だけです。ありがとうございました。
frostschutz

1
気にしないで、それを理解しました。誤報については申し訳ありません。
ナユキ

23

これはモジュロバイアスです。あればRANDOMよく構成され、0から32767の間の各値が等しい確率で生成されます。モジュロを使用する場合、確率を変更します。モジュロより上のすべての値の確率は、マッピング先の値に追加されます。

あなたの例では、6×3600は値の範囲の約3分の2です。したがって、上位3分の1の確率が下位3分の1の確率に加算されます。つまり、0〜2(およそ)の値は、3〜5の値の2倍の確率で生成されます。モジュロバイアスははるかに小さく、32400〜32767の値にのみ影響します。

あなたの主な質問に答えるために、少なくともBashでは、シードがわかっていればランダムシーケンスは完全に予測可能です。を参照intrand32してくださいvariables.c

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.