1. ホーム
  2. java

[解決済み] なぜこの乱数値は50/50ではなく、25/75の分布になるのでしょうか?

2022-06-03 06:05:51

質問

編集してください。 つまり、基本的に私が書こうとしているのは、1ビットのハッシュで double .

をマッピングしたいのですが doubletrue または false を50/50の確率で表示します。そのために、私はいくつかの乱数を選ぶコードを書きました。 (例として、私はこれを規則性のあるデータで使いたいのですが、それでも50/50の結果を得ることができます) を選び、その最後のビットをチェックし、インクリメントします。 y を増分し、それが1であれば n である。

しかし、このコードでは常に25%の y と75%の n . なぜ50/50ではないのでしょうか?そして、なぜこのような奇妙な、しかし直球勝負の(1/3)配分なのでしょうか?

public class DoubleToBoolean {
    @Test
    public void test() {

        int y = 0;
        int n = 0;
        Random r = new Random();
        for (int i = 0; i < 1000000; i++) {
            double randomValue = r.nextDouble();
            long lastBit = Double.doubleToLongBits(randomValue) & 1;
            if (lastBit == 1) {
                y++;
            } else {
                n++;
            }
        }
        System.out.println(y + " " + n);
    }
}

出力例です。

250167 749833

どのように解決するのですか?

nextDoubleは次のように動作するため、( ソース )

public double nextDouble()
{
    return (((long) next(26) << 27) + next(27)) / (double) (1L << 53);
}

next(x) 作る x ランダムビットになります。

さて、なぜこれが問題になるのでしょうか?なぜなら、最初の部分(除算前)で生成された数値の約半分は 1L << 52 よりも小さいため、それらのシグニフィカンドは埋められる 53 ビットを完全に埋められず、シグニフィカンドの最下位ビットが常に 0 であることを意味します。


この件に注目が集まっているため、ここで、がどのようなものであるかについて補足説明します。 double がどのようなものなのか、そしてなぜそれがこの質問で重要なのかを説明します。

基本的に double は次のようになります: ( ソース )

この画像では見えない非常に重要な詳細は、数字が "正規化されていることです。 1 53 ビットの分数が 1 で始まるように (そのように指数を選択することにより)、その 1 が省略されます。このため、図では分数 (シグニフィカンド) が 52 ビットになっていますが、実際には 53 ビットになっています。

この正規化により、もしコード内の nextDouble のコードで 53 番目のビットが設定されている場合、そのビットは暗黙の先行 1 であり、それは消え、他の 52 ビットは結果としての double . しかし、そのビットが設定されていない場合、残りのビットはそれが設定されるまで左にシフトされなければなりません。

平均して、生成された数値の半分は、シグニフィカントが ではない というケースに分類され、残りの半分は少なくとも 1 ずつシフトされている (または完全に 0 である) ため、最下位ビットは常に 0 になります。

1: 常にというわけではなく、最高値の 1 を持たない 0 にはできないことは明らかです。これらの数は非正規数または準正規数と呼ばれます。 wikipedia:非正規数 .