web-dev-qa-db-ja.com

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

Wikipedia の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
15
cprn

モジュロバイアスのトピックを拡張するには、次の式を使用します。

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のみが必要な場合、モジュロは6でなく、6600でもクレイジー数でも構いません)。この単純化だけでバイアスが大幅に減少します(32766は0にマップされ、32767は1にマップされ、これら2つの数値に小さなバイアスが与えられます)。

バイアスを完全に取り除くには、(たとえば)$RANDOM32768 % 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の可能な値よりも桁違いに大きい)。ただし、とにかく再ロールロジックを実装しても問題はありません(たとえそれが成功しない場合でも)。

37
frostschutz

これはモジュロバイアスです。 RANDOMが適切に構成されている場合、0〜32767の各値が等しい確率で生成されます。モジュロを使用すると、確率が変更されます。モジュロより上のすべての値の確率は、それらがマップする値に追加されます。

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

主な質問に答えるために、少なくともBashでは、シードを知っていればランダムシーケンスは完全に予測可能です。見る intrand32 in variables.c

23
Stephen Kitt