1〜100の乱数を取得して、結果を主に40〜60の範囲内に収めることは可能ですか?つまり、めったにその範囲外になることはありませんが、主にその範囲内にしたいのです... JavaScript / jQueryで可能ですか?
現在、私は基本的なだけを使用していMath.random() * 100 + 1
ます。
1〜100の乱数を取得して、結果を主に40〜60の範囲内に収めることは可能ですか?つまり、めったにその範囲外になることはありませんが、主にその範囲内にしたいのです... JavaScript / jQueryで可能ですか?
現在、私は基本的なだけを使用していMath.random() * 100 + 1
ます。
回答:
最も簡単な方法は、0〜50の2つの乱数を生成し、それらを加算することです。
これにより、2つのダイスバイアスを7に向けてロールするのと同じように、50に向けてバイアスされた分布が得られます。
実際、(@ Falcoが示唆するように)より多くの「ダイス」を使用することにより、ベルカーブにさらに近づけることができます。
function weightedRandom(max, numDice) {
var num = 0;
for (var i = 0; i < numDice; i++) {
num += Math.random() * (max/numDice);
}
return num;
}
JSFiddle:http ://jsfiddle.net/797qhcza/1/
特定の解決策を与えるいくつかの良い答えがあります。一般的な解決策を説明します。問題は:
この問題の一般的な解決策は、変位値関数を計算することですご所望の分布の、そしてあなたの均一源の出力にクォンタイル関数を適用します。
分位数関数は、目的の分布関数の積分の逆です。分布関数は、曲線の一部の下の面積が、ランダムに選択されたアイテムがその部分に含まれる確率に等しい関数です。
ここでその方法の例を示します。
http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/
そこのコードはC#にありますが、原則はすべての言語に適用されます。ソリューションをJavaScriptに適合させるのは簡単です。
数値の配列などを取るのは効率的ではありません。0から100までの乱数を取り、必要な分布にマップするマッピングを行う必要があります。したがって、あなたのケースでは、範囲の中央に最も多くの値を持つ分布を取得することができます。f(x)=-(1/25)x2+4x
私は、「範囲外」に行くことを許可される数のために「チャンス」を設定するようなことをするかもしれません。この例では、20%の確率で数値は1〜100になり、それ以外の場合は40〜60になります。
$(function () {
$('button').click(function () {
var outOfBoundsChance = .2;
var num = 0;
if (Math.random() <= outOfBoundsChance) {
num = getRandomInt(1, 100);
} else {
num = getRandomInt(40, 60);
}
$('#out').text(num);
});
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button>Generate</button>
<div id="out"></div>
フィドル:http : //jsfiddle.net/kbv39s9w/
私は数年前にこの問題を解決する必要があり、私の解決策は他のどの回答よりも簡単でした。
境界の間に3つのランダムを生成し、それらを平均しました。これは結果を中央に引き寄せますが、端に到達することは完全に可能です。
BellFactor
。3.の
このような乱数を生成する方法はたくさんあります。これを行う1つの方法は、複数の一様乱数の合計を計算することです。合計する乱数の数とその範囲は、最終的な分布がどのようになるかを決定します。
合計する数値が多いほど、中心に向かってバイアスがかかります。1つの乱数の合計を使用することは既に質問で提案されていますが、気づくように範囲の中心に偏っていません。他の回答では、2つの乱数の合計または3つの乱数の合計を使用することを提案しています。
より多くの乱数の合計を取ることにより、範囲の中心に向かってさらにバイアスをかけることができます。極端な場合、それぞれが0または1の99個の乱数の合計を取ることができます。これは二項分布になります。(二項分布は、ある意味では、正規分布の離散バージョンと見なすことができます)。これは理論的には全範囲をカバーできますが、エンドポイントに到達することを期待してはならないほど中心に向かって偏っています。
このアプローチは、必要なバイアスを調整できることを意味します。
このようなものを使用するのはどうですか:
var loops = 10;
var tries = 10;
var div = $("#results").html(random());
function random() {
var values = "";
for(var i=0; i < loops; i++) {
var numTries = tries;
do {
var num = Math.floor((Math.random() * 100) + 1);
numTries--;
}
while((num < 40 || num >60) && numTries > 1)
values += num + "<br/>";
}
return values;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
私がコード化した方法では、いくつかの変数を設定できます:
ループ = 試行結果の数
回数=関数がwhileループの実行を停止する前に、関数が40〜60の数値を取得しようとする回数
追加ボーナス:do whileを使用します!!! 最高の素晴らしさ
重み[0, 1)
に[1, 100]
応じてからまでのランダムな値をマッピングする関数を記述できます。この例を考えてみましょう:
ここでは、値0.95
はの間の値にマッピングされます[61, 100]
。
実際.05 / .1 = 0.5
、にマッピングされている[61, 100]
場合、81
。
これが関数です:
/*
* Function that returns a function that maps random number to value according to map of probability
*/
function createDistributionFunction(data) {
// cache data + some pre-calculations
var cache = [];
var i;
for (i = 0; i < data.length; i++) {
cache[i] = {};
cache[i].valueMin = data[i].values[0];
cache[i].valueMax = data[i].values[1];
cache[i].rangeMin = i === 0 ? 0 : cache[i - 1].rangeMax;
cache[i].rangeMax = cache[i].rangeMin + data[i].weight;
}
return function(random) {
var value;
for (i = 0; i < cache.length; i++) {
// this maps random number to the bracket and the value inside that bracket
if (cache[i].rangeMin <= random && random < cache[i].rangeMax) {
value = (random - cache[i].rangeMin) / (cache[i].rangeMax - cache[i].rangeMin);
value *= cache[i].valueMax - cache[i].valueMin + 1;
value += cache[i].valueMin;
return Math.floor(value);
}
}
};
}
/*
* Example usage
*/
var distributionFunction = createDistributionFunction([
{ weight: 0.1, values: [1, 40] },
{ weight: 0.8, values: [41, 60] },
{ weight: 0.1, values: [61, 100] }
]);
/*
* Test the example and draw results using Google charts API
*/
function testAndDrawResult() {
var counts = [];
var i;
var value;
// run the function in a loop and count the number of occurrences of each value
for (i = 0; i < 10000; i++) {
value = distributionFunction(Math.random());
counts[value] = (counts[value] || 0) + 1;
}
// convert results to datatable and display
var data = new google.visualization.DataTable();
data.addColumn("number", "Value");
data.addColumn("number", "Count");
for (value = 0; value < counts.length; value++) {
if (counts[value] !== undefined) {
data.addRow([value, counts[value]]);
}
}
var chart = new google.visualization.ColumnChart(document.getElementById("chart"));
chart.draw(data);
}
google.load("visualization", "1", { packages: ["corechart"] });
google.setOnLoadCallback(testAndDrawResult);
<script src="https://www.google.com/jsapi"></script>
<div id="chart"></div>
これは、3/4 40-60とその範囲外の1/4の加重ソリューションです。
function weighted() {
var w = 4;
// number 1 to w
var r = Math.floor(Math.random() * w) + 1;
if (r === 1) { // 1/w goes to outside 40-60
var n = Math.floor(Math.random() * 80) + 1;
if (n >= 40 && n <= 60) n += 40;
return n
}
// w-1/w goes to 40-60 range.
return Math.floor(Math.random() * 21) + 40;
}
function test() {
var counts = [];
for (var i = 0; i < 2000; i++) {
var n = weighted();
if (!counts[n]) counts[n] = 0;
counts[n] ++;
}
var output = document.getElementById('output');
var o = "";
for (var i = 1; i <= 100; i++) {
o += i + " - " + (counts[i] | 0) + "\n";
}
output.innerHTML = o;
}
test();
<pre id="output"></pre>
わかりました。最後の回答のように感じたので、別の回答を追加することにしました。ここでのほとんどの回答と同様に、ベルカーブタイプの結果のリターンを取得する何らかの統計的方法を使用します。以下に提供するコードは、サイコロを振るときと同じように機能します。したがって、1または99を取得するのは最も困難ですが、50を取得するのが最も簡単です。
var loops = 10; //Number of numbers generated
var min = 1,
max = 50;
var div = $("#results").html(random());
function random() {
var values = "";
for (var i = 0; i < loops; i++) {
var one = generate();
var two = generate();
var ans = one + two - 1;
var num = values += ans + "<br/>";
}
return values;
}
function generate() {
return Math.floor((Math.random() * (max - min + 1)) + min);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="results"></div>
ベータ版の使用をお勧めしますをの数値を生成し、それを拡大することをします。これは非常に柔軟で、さまざまな形の分布を作成できます。
以下は、素早く汚れたサンプラーです。
rbeta = function(alpha, beta) {
var a = 0
for(var i = 0; i < alpha; i++)
a -= Math.log(Math.random())
var b = 0
for(var i = 0; i < beta; i++)
b -= Math.log(Math.random())
return Math.ceil(100 * a / (a+b))
}
var randNum;
// generate random number from 1-5
var freq = Math.floor(Math.random() * (6 - 1) + 1);
// focus on 40-60 if the number is odd (1,3, or 5)
// this should happen %60 of the time
if (freq % 2){
randNum = Math.floor(Math.random() * (60 - 40) + 40);
}
else {
randNum = Math.floor(Math.random() * (100 - 1) + 1);
}
この非常に問題を対象とする最良のソリューションは、BlueRaja-Danny Pflughoeftによって提案されたものですが、やや高速でより一般的なソリューションも言及する価値があります。
の2つの要件を満たす乱数(文字列、座標ペアなど)を生成する必要がある場合
私は通常、要件を満たす数値の配列(文字列、座標ペアなど)を作成することから始めます(あなたの場合:可能性の高いものを複数回含む数値の配列)。次に、その配列のランダムな項目を選択します。この方法では、高価なランダム関数をアイテムごとに1回呼び出すだけで済みます。
分布
5% for [ 0,39]
90% for [40,59]
5% for [60,99]
解決
var f = Math.random();
if (f < 0.05) return random(0,39);
else if (f < 0.95) return random(40,59);
else return random(60,99);
一般的なソリューション
random_choose([series(0,39),series(40,59),series(60,99)],[0.05,0.90,0.05]);
function random_choose (collections,probabilities)
{
var acc = 0.00;
var r1 = Math.random();
var r2 = Math.random();
for (var i = 0; i < probabilities.length; i++)
{
acc += probabilities[i];
if (r1 < acc)
return collections[i][Math.floor(r2*collections[i].length)];
}
return (-1);
}
function series(min,max)
{
var i = min; var s = [];
while (s[s.length-1] < max) s[s.length]=i++;
return s;
}
ヘルパー乱数を使用して、40〜60または1〜100の乱数を生成できます。
// 90% of random numbers should be between 40 to 60.
var weight_percentage = 90;
var focuse_on_center = ( (Math.random() * 100) < weight_percentage );
if(focuse_on_center)
{
// generate a random number within the 40-60 range.
alert (40 + Math.random() * 20 + 1);
}
else
{
// generate a random number within the 1-100 range.
alert (Math.random() * 100 + 1);
}
gaussian
関数を使用できる場合は、それを使用してください。この関数は、average 0
およびで通常の数を返しますsigma 1
。
この数の95%は以内average +/- 2*sigma
です。あなたのaverage = 50
、そしてsigma = 5
そう
randomNumber = 50 + 5*gaussian()
これを行う最良の方法は、特定の数のセットに均等に分布する乱数を生成し、投影が必要な数にヒットする可能性が高い0から100までのセットに投影関数を適用することです。
通常、これを達成する数学的な方法は、必要な数の確率関数をプロットすることです。ベルカーブを使用することもできますが、計算を簡単にするために、フリップされた放物線を使用するだけです。
それをゆがめることなく、根が0と100になるように放物線を作成しましょう。次の方程式が得られます。
f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x
これで、0から100までの曲線の下のすべての領域が、数値を生成する最初のセットを表しています。そこでは、生成は完全にランダムです。したがって、最初のセットの境界を見つけるだけです。
もちろん、下限は0です。上限は、100での関数の積分です。
F(x) = -x^3/3 + 50x^2
F(100) = 500,000/3 = 166,666.66666 (let's just use 166,666, because rounding up would make the target out of bounds)
したがって、0から166,666までの数を生成する必要があることがわかります。次に、その数値を取得して、2番目のセット(0〜100)に投影するだけです。
生成した乱数は、0〜100の入力xを持つ放物線の積分であることがわかっています。つまり、乱数はF(x)の結果であると仮定し、xを解く必要があります。
この場合、F(x)は3次方程式であり、の形式F(x) = ax^3 + bx^2 + cx + d = 0
では、次のステートメントが当てはまります。
a = -1/3
b = 50
c = 0
d = -1 * (your random number)
これをxについて解くと、探している実際の乱数が得られます。これは、[0、100]の範囲にあることが保証され、エッジよりも中心に近い可能性がはるかに高くなります。
この答えは本当に良いです。しかし、私はさまざまな状況で実装手順を投稿したいと思います(私はJavaScriptが好きではないので、理解していただければ幸いです)。
すべての範囲に範囲と重みがあると仮定します。
ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}
初期の静的情報、キャッシュできます:
Boundary[n] = Boundary[n - 1] + weigh[n - 1]
とBoundary[0] = 0
。サンプルはBoundary = {0, 1, 3, 103, 108}
番号生成:
N
範囲[0、すべての重みの合計]から乱数を生成します。for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
i
範囲を取り、その範囲で乱数を生成します。パフォーマンスの最適化に関する追加の注意。範囲を昇順でも降順でも並べる必要はないため、範囲の検索を高速化するには、重みが最も高い範囲を最初に、重みが最も低い範囲を最後に指定する必要があります。