Math.random()とRandom.nextInt(int)の比較


135

どのような違いがあるMath.random() * nRandom.nextInt(n)どこnの整数がありますか?


数学はわかりませんが、使用するとFindBugsが文句を言うことを知っていますMath.random()
finnw

3
Randomには静的メソッドがないため、(new Random())。nextInt(n))を使用してください。Mathで同様の整数を生成するには、以下を使用します。Math.floor((Math.random()* n)+1);
Dimitri Dewaele 2014年

回答:


169

以下は GiliがリンクしているSunフォーラムの投稿からの「Random.nextInt(n)より効率的で偏りの少ない理由」の詳細な説明ですMath.random() * n

Math.random()は内部的にRandom.nextDouble()を使用します。

Random.nextDouble()はRandom.next()を2回使用して、仮数部にほぼ均一に分散されたビットを持つdoubleを生成するため、0〜1-(2 ^ -53)の範囲で均一に分散されます。

Random.nextInt(n)は、平均して2回未満のRandom.next()を使用します-1回使用します。取得した値がMAX_INT未満のnの最大倍数を超える場合は、再試行します。それ以外の場合は、nを法とする値(これを返します) MAX_INTを下回るnの最大倍数を超える値が分布を歪曲するのを防ぎます)。そのため、0からn-1の範囲で均一に分布する値を返します。

6倍にスケーリングする前は、Math.random()の出力は、一様分布から得られた2 ^ 53の可能な値の1つです。

6によるスケーリングは可能な値の数を変更せず、intにキャストすると、これらの値は6つの「バケット」(0、1、2、3、4、5)のいずれかに強制され、各バケットはいずれかを含む範囲に対応します1501199875790165または1501199875790166(6は2 ^ 53のアドバイザーではないため)これは、十分な数のダイスロール(または十分に多数のサイドを持つダイス)の場合、ダイスが大きなバケットに向かってバイアスされていることを示します。

この効果が現れるまで、非常に長い時間ダイスを振るのを待ちます。

Math.random()も約2倍の処理を必要とし、同期の影響を受けます。


3
Random.nextIntとnextDoubleも同期の対象です。
adrianos 2014年

この文脈で、「偏りが少ない」とはどういう意味ですか?
ΦXocę 웃 Пepeúpaツ

2
@ΦXocę웃Пepeúpaツこれは単に、特定の数字が他の数字よりも描かれる可能性が高いことを意味します。他の数よりもいくつかの数を選ぶ傾向があるため(完全にランダムではなく、十分な大きさのサンプルサイズが与えられているわけではありません)
フォードパーフェクト

1
そのスレッドへの最後のコメントは次のように報告していることに注意してください。「記述されたバイアスは2 ^ 53の一部ですが、使用されるPRNGの最大サイクル長は2 ^ 48のみです。したがって、アプリケーションで表示されるのはデータです。バイアスではなく、基礎となるPRNGの分布。」これは2つの方法が同等であるという事実を指しているでしょう
デジタル錯覚

1
@ΦXocę웃Пepeúpaツダイスキューブ65に置き換えます。「5バイアス」になります。サイコロに問題があることに気付く前に、サイコロを数回投げることができます。ランダムジェネレーターに問題があることに気付く前に、非常に高度な綿密な調査を行わなければなりません。
デヴィッド・Horvathの

27

別の重要な点は、同じシードで2つのRandomオブジェクトを作成できるため、Random.nextInt(n)は繰り返し可能であることです。これはMath.random()では不可能です。



0

この例によればRandom.nextInt(n)、Math.random()* nよりも予測可能な出力が少なくなります。[ソートされていない配列よりもソートされた配列の方が速い] [1]によると、Random.nextInt(n)は予測難しいと言えます。

usingRandomClass:時間:328マイル秒。

usingMathsRandom:時間:187マイル秒。

package javaFuction;
import java.util.Random;
public class RandomFuction 
{
    static int array[] = new int[9999];
    static long sum = 0;
    public static void usingMathsRandom() {
        for (int i = 0; i < 9999; i++) {
         array[i] = (int) (Math.random() * 256);
       }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }
        }
    }

    public static void usingRandomClass() {
        Random random = new Random();
        for (int i = 0; i < 9999; i++) {
            array[i] = random.nextInt(256);
        }

        for (int i = 0; i < 9999; i++) {
            for (int j = 0; j < 9999; j++) {
                if (array[j] >= 128) {
                    sum += array[j];
                }
            }

        }

    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        usingRandomClass();
        long end = System.currentTimeMillis();
        System.out.println("usingRandomClass " + (end - start));
        start = System.currentTimeMillis();
        usingMathsRandom();
        end = System.currentTimeMillis();
        System.out.println("usingMathsRandom " + (end - start));

    }

}

1
2番目のループでは、> = 50を確認します。これは、50%を超える場合に当てはまります。これにより、ほとんどの場合、このifステートメントがtrueになり、より予測しやすくなります。したがって、あなたの結果はあなたの答えを支持して偏っています
Neuron '24年

それはタイプミスです... 2番目の例で128にすると、同じ結果が得られます。
jatin Goyal 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.