web-dev-qa-db-ja.com

インクルーシブ範囲でランダム浮動小数点double

Math.random()(およびほとんどの疑似乱数ジェネレータ、AFAIK)が生成するため、以下にリストされている関数を使用して、目的の範囲[X,Y)(Xは包括的で、Yは排他的であることに注意)内のランダムな浮動小数点数を簡単に取得できます。 [0,1)の数字:

function randomInRange(min, max) {
  return Math.random() * (max-min) + min;
}
// Notice that we can get "min" exactly but never "max".

どうすれば、両方の境界に望ましい範囲を含むの乱数、つまり[X,Y]を取得できますか?

IEE-754浮動小数点倍精度 のビットを「ローリング」して、可能な最大値を1.0にすることにより、Math.random()(または同等のもの)から値を「インクリメント」できると思います正確ですが、特にビット操作にあまり適していない言語では、それを正しく行うのは面倒なようです。もっと簡単な方法はありますか?

(余談ですが、なぜ乱数ジェネレータは[0,1)ではなく[0,1]で数値を生成するのですか?)

[編集]これには必要がないことに注意してください。教訓的です。好奇心旺盛で興味深い答えを期待しています。この質問が不適切な場合は、自由に投票して終了してください。

26
maerics

私はもっ​​と良い決断があると信じていますが、これはうまくいくはずです:)

function randomInRange(min, max) {
  return Math.random() < 0.5 ? ((1-Math.random()) * (max-min) + min) : (Math.random() * (max-min) + min);
}
13
Alex L

この問題に対する私の解決策は、常に上限の代わりに以下を使用することでした。

Math.nextAfter(upperBound,upperBound+1)

または

upperBound + Double.MIN_VALUE

したがって、コードは次のようになります。

double myRandomNum = Math.random() * Math.nextAfter(upperBound,upperBound+1) + lowerBound;

または

double myRandomNum = Math.random() * (upperBound + Double.MIN_VALUE) + lowerBound;

これは単に、上限を最小のdouble(Double.MIN_VALUE)だけインクリメントするため、上限はランダム計算の可能性として含まれます。

これは、1つの数値を優先して確率を歪めないため、これを回避するのに適した方法です。

これが機能しない唯一のケースは、上限がDouble.MAX_VALUEに等しい場合です。

5
MetaMilo

まず、コードに問題があります:randomInRange(0,5e-324)を試すか、ブラウザのJavaScriptコンソールにMath.random()*5e-324と入力してください。

オーバーフロー/アンダーフロー/デノルムがなくても、浮動小数点演算について確実に推論することは困難です。少し掘り下げた後、私は反例を見つけることができます:

_>>> a=1.0
>>> b=2**-54
>>> Rand=a-2*b
>>> a
1.0
>>> b
5.551115123125783e-17
>>> Rand
0.9999999999999999
>>> (a-b)*Rand+b
1.0
_

これがa = 2で発生する理由を説明する方が簡単です53 およびb = 0.5:253-1は、次に表現可能な数です。デフォルトの丸めモード(「最も近い偶数に丸める」)は2を丸めます53-0.5アップ(2のため53 「偶数」[LSB = 0]および253-1は「奇数」[LSB = 1])なので、bを減算して2を取得します53、乗算して2を取得53-1、bを追加して2を取得53 再び。


2番目の質問に答えるには、基になるPRNGは、ほとんどの場合、間隔[0,2-1]、つまりランダムなビットを生成します。適切なn(浮動小数点表現の精度のビット)を選択して2で除算するのは非常に簡単です 予測可能な分布を取得します。 _[0,1)_にはいくつかの数値があることに注意してくださいneverこのメソッドを使用して生成します((0,2-53)IEEEダブルスを使用)。

また、オーバーフローを心配せずにa[Math.floor(Math.random()*a.length)]を実行できることも意味します(宿題:IEEEバイナリ浮動小数点では、_b < 1_が正の整数aに対して_a*b < a_を意味することを証明します)。

もう1つのいい点は、各ランダム出力xを間隔[x、x + 2-53)(あまり良くないのは、返される平均値が0.5をわずかに下回ることです)。 [0,1]に戻った場合、他のすべてと同じ確率でエンドポイントを返しますか、それとも他のすべての場合と同じように間隔の半分しか表さないため、エンドポイントは半分の確率しかありませんか?

[0,1]に数値を返すという簡単な質問に答えるために、以下のメソッドは整数[0,2]を効果的に生成します]([0,2n + 1-1]そして、それが大きすぎる場合は捨て、2で割る

_function randominclusive() {
  // Generate a random "top bit". Is it set?
  while (Math.random() >= 0.5) {
    // Generate the rest of the random bits. Are they zero?
    // If so, then we've generated 2^n, and dividing by 2^n gives us 1.
    if (Math.random() == 0) { return 1.0; }
    // If not, generate a new random number.
  }
  // If the top bits are not set, just divide by 2^n.
  return Math.random();
}
_

コメントはベース2を意味しますが、私はthinkであると想定されています。

  • 0と1はおそらく返されるはずです(つまり、Math.random()は0に近い浮動小数点数の近い間隔を利用しません)。
  • Math.random()> = 0.5、確率は1/2(底は偶数)
  • 基礎となるPRNGは、これを実行できるほど十分です。

乱数は常にペアで生成されることに注意してください。while(a)内の乱数の後には常にif内の乱数または末尾(b)内の乱数が続きます。 0または0.5を返すPRNGを検討することで、それが賢明であることを確認するのはかなり簡単です。

  • _a=0   b=0  _:0を返します
  • _a=0   b=0.5_:0.5を返します
  • _a=0.5 b=0  _:1を返す
  • _a=0.5 b=0.5_:ループ

問題:

  • 仮定が正しくない場合があります。特に、一般的なPRNGは、48ビットLCGの上位32ビットを取得することです(FirefoxおよびJavaこれを実行します)。doubleを生成するには、 2つの連続する出力から53ビットを取り、2で除算します53、ただし、一部の出力は不可能です(2を生成できません)53 48ビットの状態の出力!)そのうちのいくつかは決して0を返さないと思います(シングルスレッドアクセスを想定しています)が、Javaの実装を今のところチェックしたくありません。
  • Math.random()は、余分なビットを取得する必要がある結果としてpotential出力ごとに2回ですが、これはPRNGにさらに制約を課します(上記のLCGの4つの連続した出力に関する理由)。
  • Math.random()は平均して、出力ごとに約four回呼び出されます。少し遅い。
  • 結果は確定的に破棄され(シングルスレッドアクセスを想定)、出力スペースの削減がほぼ保証されます。
5
tc.

選択した閉じた間隔がサブセットになるように、半分開いた間隔をわずかに大きくするだけです。次に、ランダムな変数を生成して、それが上記の閉じた間隔になるまで続けます。

例:[3,8]で均一なものが必要な場合は、[3,8]に到達するまで、[3,9)で均一なランダム変数を繰り返し再生成します。

function randomInRangeInclusive(min,max) {
 var ret;
 for (;;) {
  ret = min + ( Math.random() * (max-min) * 1.1 );
  if ( ret <= max ) { break; }
 }
 return ret;
}

注:ハーフオープンR.Vを生成する回数。はランダムで無限の可能性がありますが、予想される呼び出し回数はそれ以外の場合は1に近づけることができます。また、無限に何度も呼び出さない可能性のあるソリューションは存在しないと思います。

4
Berry

0と1の間の値の「非常に大きい」数を考えると、それは本当に重要ですか? 実際にが1に当たる可能性は非常に低いため、実行していることに対して大きな違いが生じる可能性はほとんどありません。

1
Jon Skeet

私はかなり経験が少ないので、解決策も探しています。

これは私の大まかな考えです:

乱数ジェネレータは、[0,1]ではなく[0,1)で数値を生成します。

[0,1)は、[1,2)などが続くユニット長で、重複することなく...

Random [x、y]の場合、これを行うことができます:

float randomInclusive(x, y){

    float MIN = smallest_value_above_zero;
    float result;
    do{
        result = random(x, (y + MIN));
    } while(result > y);
    return result;
}

[x、y]のすべての値が選択される可能性が同じで、uがyに到達できる場合。

これが機能しない場合、または潜在的な問題がある場合はお知らせください。

ありがとう〜

0
Starringeye
private static double random(double min, double max) {
    final double r = Math.random();
    return (r >= 0.5d ? 1.5d - r : r) * (max - min) + min;
}
0
ivan.a.bovin

Math.round()は、バインドされた値を含めるのに役立ちます。 _0 <= value < 1_(1は排他的)がある場合、Math.round(value * 100) / 100は_0 <= value <= 1_(1は包括的)を返します。ここでの注意は、値の小数点以下の桁数が2桁になっていることです。 3桁が必要な場合は、Math.round(value * 1000) / 1000などを試します。次の関数にはもう1つのパラメーターがあります。つまり、小数点以下の桁数です。私はprecisionと呼びます。

_function randomInRange(min, max, precision) {
    return Math.round(Math.random() * Math.pow(10, precision)) /
            Math.pow(10, precision) * (max - min) + min;
}
_
0
Thach Van

質問は尋ねるのと似ています1.0の直前の浮動小数点数は何ですか?そのような浮動小数点数がありますが、これは2 ^ 24分の1です(IEEE floatの場合) )または2 ^ 53分の1(doubleの場合)。

違いは実際には無視できます。

0
wallyk

これはどう?

function randomInRange(min, max){
    var n = Math.random() * (max - min + 0.1) + min;
    return n > max ? randomInRange(min, max) : n;
}

これでスタックオーバーフローが発生した場合、プレゼントを購入します。

-編集:現在のことを気にしないでください。私は野生になりました:

randomInRange(0, 0.0000000000000000001)

スタックオーバーフローが発生しました。

0

上限を含めるために浮動小数点値が必要になる状況は何でしょうか?整数については理解できますが、浮動小数点については、包含と排他の違いは1.0e-32のようなものです。

0
Mike Schachter

このように考えてください。浮動小数点数が任意の精度であると想像すると、正確にminを取得する可能性はゼロです。 maxを取得する可能性もあります。私はあなたにあなた自身の結論を出させます。

この「問題」は、0と1の間の実線上のランダムな点を取得することと同等です。「包括的」と「排他的」はありません。

0
Kendall Frey