4

これは、プログラミングよりも数学的な問題かもしれません。JS では、1 ~ 6 の間隔でランダムな整数を返す関数が必要でした。これが私が見つけたものです。

// Returns a random integer between min and max
// Using Math.round() will give you a non-uniform distribution!
function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

これをコピーしてコードに貼り付けると、罪悪感を覚えます。私はこれを理解していません: 最大から最小を減算し、1 を加算し、答えに Math.random() を掛けてから最小を加算する理由。紙に手書きでいくつかの数字を書くのにうんざりしていましたが、うまくいきました!しかし、私は理由を理解していません!

4

4 に答える 4

6

と の動作をすでに理解していると仮定するとMath.floorMath.random残りの手順は次のとおりです。

  • Math.random()0↝ (包括的) と1(排他的)の間の乱数
  • Math.random() * max0↝ (包括的) とmax(排他的)の間の乱数
  • Math.floor(Math.random() * max)↝ (含む) と(含まない)の間のランダムな整数0max
  • Math.floor(Math.random() * (max - min)) + minmin(含む) とmax(含まない) の間のランダムな整数
  • Math.floor(Math.random() * ((max + 1) - min)) + minmin(incl.) とmax+1(excl.) の間のランダムな整数 ( OR betweenminmax両方を含む)
于 2013-07-07T22:05:43.530 に答える
3

Math.random()0 から 1 (1.0 を除く) までの「実数」を返します。

それは素晴らしいことですが、1 から 2 までの「実数」が必要な場合はどうすればよいでしょうか。

答え: [0,1) を [1,2) に「変換」します。

実際には、結果に 1 を加算することを意味します。

試してみてください -- Math.random()+11 から 2 までの数字が表示されます。

数学では、これは「マッピング」として知られています。つまり、[0,1) のすべての可能な実数について、その実数を [1,2) の別の実数に「マッピング」する方法を見つけます。つまり、[0,1) の間の実数を与えた場合、その数をマッピングできるはずです。[1,2) の間の数を返す関数にその数を適用します。

この場合、関数 f(x) = x+1 です。

[1,2) の間の乱数がどのように得られるかわかりましたか? 隣り合う 2 つの間隔を視覚化し、[0,1) のすべての点から [1,2) の対応するマップに向かう線を想像してください。次に、[0,1) 上のランダムな点を選び、線をたどります。[1,2) のランダムな点まで線をたどります!

[0,1) から [1,2) へのすべての完全な 1 対 1 マップは、[0,1) の間の乱数を [1,2) の間の乱数に変換します...しかし、すべてではありません。 [1,2) の間で均等に分散された乱数が得られます。均等に分散された結果を与えるマップの背後にある数学は少し複雑ですが、要するに、マップが定数による加算、減算、乗算、および除算のみを含む場合、結果も均等に分散されるという意味で「合法」です。

これで、[0,1) を [1,2) に変換する方法がわかりました。

[0,1) を [0,2) にマップしたい場合はどうすればよいですか? もう数字の足し算はできません...

すべてを 2 倍したらどうですか。

これは機能するはずです -- 関数 f(x) = x*2 は実際に [0,1) 上のすべての点を [0,2) 上の点にマッピングします --- そして定数 (2) による乗算のみを含むため、これは分布を維持するマップです。

これはうまくいきます! Math.random()*20 から 2 の間の乱数が返されます。

さて、もう少し複雑なこと... [0,1) を [1,3) に変換します。

2 を掛けてもうまくいきません... 0*2 = 0 であり、それは目標範囲内ではありません。1 を追加しても機能しません... 0+1 が目標範囲内にあり、1+1 も目標範囲内にあるとしても、3 に到達する方法はありません。

[0,1) を [1,3) に変換できない場合は、別のものを [1,3) に変換できるかどうか試してみましょう。

[0,2) はどうですか? はい、これを行うことができます... 関数 f(x) = x+1 は [0,2) を [1,3) に完全にマップします。+範囲を「シフト」すると考えることができます。

したがって、ここでの解決策は明らかです。最初に [0,1) を [0,2) に変更し、次に [0,2) を [1,3) に変更します。

1 つ目 (f(x) = x*2) は既にわかっており、2 つ目 (f(x) = x+1) もわかっています。したがって、「結合された」変換/マップは f(x) = (x*2)+1 です。

つまりMath.random()*2 + 1、0 から 3 までの数値が返されます。

最後のトリックとして、[0,1) を任意の範囲 [min,max) にマッピングします。

ここでの秘訣は、これを [min,min+range) のように書き直すことです。ここで、range = max-min.

ここで、範囲 [0,range) を [min,min+range) に変換するのは簡単であることがわかります。これに「min」を追加するだけです。したがって、範囲 [0,range) があり、[min,min+range) を取得したい場合、f(x) = x+min を使用します。

では、 [0,1) から [0,range) にするにはどうすればよいでしょうか?

範囲で乗算!

f(x) = (x*範囲) + 最小

range = max-min を使用して、元の用語に書き戻します。

f(x) = (x*(最大-最小)) + 最小

[0,1) の実数を [min,max) の実数に変換します。

残りは(便利な整数に変換して)あなたに任せます

于 2013-07-07T22:13:22.433 に答える
1

コードの説明は次のとおりです。

  • Math.random()0 から 1 (1 を含まない) の間の乱数を生成します。
  • 必要な数値の範囲に基づいて、その値をスケーリングする必要があります。あなたの範囲は、あなたのmin希望する数からあなたの希望する数までの距離maxですmax - min
  • max生成された数値の範囲に値を含めたい場合は、次を使用しますmax - min + 1
  • 0次に、乱数を追加するのではなく、正しい基数から開始するようにする必要がありますmin
  • 次に、それを整数にしたい場合は、呼び出しMath.floor()て、次に小さい整数に切り捨てます。

したがって、これがあった場合:

Math.floor(Math.random())

あなたはいつもゼロになるでしょう。0からMath.floor()1 の間 (1 を含まない) の float 値は常に 0 に切り捨てられます。

次に、次のように範囲を拡張すると:

Math.floor(Math.random() * (max - min + 1))

max - minこれで、0 から大きい方の値までの乱数が得られます。

したがって、正しいベースから開始するには、次のminように追加します。

Math.floor(Math.random() * (max - min + 1)) + min
于 2013-07-07T22:03:58.730 に答える
1
0 <= Math.random() < 1  =>
0 <= Math.random() * 6 < 6 =>
0 <= Math.floor( Math.random() * 6 ) <= 5   

次に「min」を追加すると、次のようになります。

min <= Math.floor( Math.random() * 6 ) <= 5 + min

あなたの例では、min = 1 の場合、1 ~ 6 のすべての数字が含まれます。

これではっきりしたことを願っています。

于 2013-07-07T22:13:05.803 に答える