[javascript] 中心に焦点を当てた乱数を得る


Answers

具体的な解決策を提示する良い答えがあります。 私にあなたのための一般的な解決策を説明させてください。 問題は:

  • 私は、0と1の間に多かれ少なかれ均一に分布した乱数のソースを持っています。
  • 私は、異なる分布に従う一連の乱数を生成したい。

この問題の一般的な解法は、あなたの望む分布の分位関数を計算してから、あなたの一様な源の出力に分位関数を適用することです。

分位関数は、あなたが望む分布関数の 積分逆数です。 分布関数は、曲線の一部の下の面積が、ランダムに選択された項目がその部分に入る確率と等しい関数である。

私はここでこれを行う方法の例を挙げています:

http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/

そこのコードはC#にありますが、原則はどの言語にも適用されます。 ソリューションをJavaScriptに適合させるのは簡単です。

Question

1-100の間の乱数を取得し、主に40-60の範囲内に結果を保持することは可能ですか? つまり、その範囲から外れることはまれですが、主にその範囲内にしたいと思っています... JavaScript / jQueryで可能ですか?

今は基本的なMath.random() * 100 + 1ます。




体重に応じて[0, 1) [1, 100][1, 100]間のランダムな値をマップする関数を書くことができます。 この例を考えてみましょう。

ここで、値0.95[61, 100]間の値にマップされます。
実際には.05 / .1 = 0.5であり、これは[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>




分布

 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;
}



私はセットアップのような何かを行う可能性があります "チャンス"は、番号の範囲外に行くことを許可する。 この例では、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/ : http://jsfiddle.net/kbv39s9w/




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);
}



それ馬鹿に見えるが、あなたはrandを2回使うことができる:

var choice = Math.random() * 3;
var result;

if (choice < 2){
    result = Math.random() * 20 + 40; //you have 2/3 chance to go there
}
else {
    result = Math.random() * 100 + 1;
}



私は最後の答え、ここでのほとんどの答えのように感じたので、私は別の答えを追加することを決めました、ベルカーブ型の結果のリターンを得る半統計的な方法を使用します。 下記のコードは、あなたがサイコロを振ったときと同じように動作します。 したがって、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>




gaussian関数を使用できる場合は、それを使用してください。 この関数は、 average 0sigma 1正規数を返します。

この数の95%はaverage +/- 2*sigma以内である。 average = 50sigma = 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)に投影するだけです。

私たちが生成した乱数は、入力xが0から100までの放物線の積分であることがわかります。つまり、乱数は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]の範囲にあることが保証されており、エッジよりも中心に近い可能性が高いです。




このような乱数を生成するには、さまざまな方法があります。 これを行う1つの方法は、複数の一様乱数の合計を計算することです。 合計した乱数の数とその範囲によって最終的な分布がどのように見えるかが決まります。

あなたが集計する数字が多いほど、中心に偏っていくでしょう。 1乱数合計を使用して、あなたの質問に既に提案されていますが、気づくように、範囲の中心に偏っていません。 他の答えは、2つの乱数の和または3つの乱数の合計を使用することを提案しています 。

より多くの乱数の合計をとることで、範囲の中心にさらに偏ったバイアスを得ることができます。 極端な場合、それぞれが0または1の99個の乱数の合計をとることができます。これは2項分布になります。 (二項分布は、ある意味では、正規分布の離散バージョンとみなすことができる)。 これは理論的にはまだ完全な範囲をカバーすることができますが、それはエンドポイントに到達することを期待してはいけない、中心に向かって非常に偏っています。

このアプローチは、あなたがどれくらいの偏りを微調整できるかを意味します。




Links