web-dev-qa-db-ja.com

中心に焦点を当てた乱数を取得する

1〜100の乱数を取得し、結果を主に40〜60の範囲内に収めることは可能ですか?つまり、めったにその範囲外に出ることはありませんが、主にその範囲内に収まるようにしたいのです... JavaScript/jQueryで可能ですか?

今は、基本的なMath.random() * 100 + 1を使用しています。

237
Darryl Huffman

最も簡単な方法は、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;
}

Weighted random numbers

JSFiddle: http://jsfiddle.net/797qhcza/1/

特定の解決策を提供するいくつかの良い答えがあります。一般的な解決策を説明します。問題は:

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

この問題の一般的な解決策は、目的の分布の変位値関数を計算し、均一なソースの出力に変位値関数を適用することです。

分位関数は、inverseintegralの希望分布関数。分布関数は、曲線の一部の下の面積が、ランダムに選択されたアイテムがその部分にある確率に等しい関数です。

ここでその方法の例を示します。

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

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

48
Eric Lippert

数値の配列などを取得するのは効率的ではありません。 0から100の間の乱数を取り、必要な分布にマップするマッピングを取る必要があります。したがって、あなたの場合、f(x)=-(1/25)x2+4xを使用して、範囲の中央で最も値が大きい分布を取得できます。

Distribution

36
iCaramba

番号が「範囲外」になることを許可する「チャンス」を設定するようなことをするかもしれません。この例では、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/

17

私は数年前にこの問題を解決する必要があり、私の解決策は他のどの答えよりも簡単でした。

境界間に3つのランダムを生成し、それらを平均しました。これにより、結果は中央に向かって引き寄せられますが、四肢に到達することは完全に可能です。

15
Lefty

それlooksバカですが、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;
}
14
max890

確かにそれは可能です。ランダムな1-100を作ります。数値が30未満の場合、範囲40〜60で生成されない場合は1〜100の範囲で数値を生成します。

11
Luka Krajnc

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

合計する数値が多いほど、中心に向かって偏りが大きくなります。 1つの乱数のsumを使用することはあなたの質問ですでに提案されていますが、気づいたように範囲の中心に向かって偏っていません。他の回答では、 2つの乱数の合計 または つの乱数の合計 を使用することを提案しています。

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

このアプローチにより、必要なバイアスを微調整できます。

11
kasperd

このようなものを使用するのはどうですか:

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を使用!!!最高の素晴らしい

8
ctwheels

[0, 1)から[1, 100]までのランダムな値を重みに応じてマッピングする関数を作成できます。この例を考えてみましょう:

0.0-1.0 to 1-100 by percentage weight

ここでは、値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>
8
Salman A

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>
7
wolfhammer

ベータ分布 を使用して0〜1の数値を生成し、それをスケールアップすることをお勧めします。非常に柔軟で、さまざまな形の分布を作成できます。

以下は、手早く汚れたサンプラーです。

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))
}
6
Neal Fultz

さて、最後の答えとここでのほとんどの答えは、ベルカーブ型の結果を得るためのある種の半統計的な方法を使用していると感じたため、別の答えを追加することにしました。以下に提供するコードは、サイコロを振ったときと同じように機能します。したがって、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>
6
ctwheels
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);
}
5
Olaide Oyekoya

このまさに問題を対象とする最良の解決策は BlueRaja-Danny Pflughoeft によって提案されたものですが、やや高速でより一般的な解決策も言及する価値があると思います。


の2つの要件を満たす乱数(文字列、座標ペアなど)を生成する必要がある場合

  1. 結果セットは非常に小さいです。 (16Kの数字以下)
  2. 結果セットは控えめです。 (整数のみなど)

私は通常、要件を満たす数字の配列(文字列、座標ペアなど)を作成することから始め(あなたの場合:より可能性の高い数字を複数回含む数字の配列)、その配列のランダムなアイテムを選択します。この方法では、アイテムごとに一度だけ高価なランダム関数を呼び出す必要があります。

5
mg30rg

分布

 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;
}
4
Khaled.K

ヘルパー乱数を使用して、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);
}
4
Amir Saniyan

gaussian関数を使用できる場合は、それを使用します。この関数は、average 0およびsigma 1を含む通常の数値を返します。

この数の95%はaverage +/- 2*sigma内にあります。あなたのaverage = 50、およびsigma = 5

randomNumber = 50 + 5*gaussian()

この答えは本当に良いです 。しかし、さまざまな状況での実装手順(私はJavaScriptには興味がありませんので、理解していただければ幸いです)を投稿したいと思います。


すべての範囲に範囲と重みがあると仮定します。

ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}

初期静的情報、キャッシュ可能:

  1. すべての重量の合計(サンプルでは108)
  2. 範囲選択の境界。基本的には、この式:Boundary[n] = Boundary[n - 1] + weigh[n - 1]およびBoundary[0] = 0。サンプルにはBoundary = {0, 1, 3, 103, 108}があります

番号の生成:

  1. 範囲[0、すべての重みの合計)から乱数Nを生成します。
  2. for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
  3. ithの範囲を取り、その範囲で乱数を生成します。

パフォーマンスの最適化に関する追加の注意。範囲は昇順でも降順でも順序付けする必要はありません。したがって、より高速な範囲検索では、最大の重みを持つ範囲が最初に、最小の重みを持つ範囲が最後になります。

3
ST3

これを行う最良の方法は、特定の数のセットに均等に分布する乱数を生成し、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]の範囲にあり、エッジよりも中心に近い可能性がはるかに高くなります。

3
arik