获取一个以中心为中心的随机数

Get a random number focused on center

是否可以得到一个1-100之间的随机数,并使结果主要在40-60范围内?我的意思是,它很少会超出该范围,但我希望它主要在该范围内...... JavaScript/jQuery 是否可能?

现在我只是在使用基本的 Math.random() * 100 + 1

当然可以。随机抽取 1-100。如果数字小于 30,则生成 1-100 范围内的数字,如果不在 40-60 范围内生成。

看起来愚蠢但是你可以使用 rand 两次:

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

我可能会做一些事情,比如设置一个 "chance" 以允许号码进入 "out of bounds"。在此示例中,数字为 1-100 的概率为 20%,否则为 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>

fiddle: http://jsfiddle.net/kbv39s9w/

使用这样的东西怎么样:

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!!!尽善尽美

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

这是 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>

好的,所以我决定添加另一个答案,因为我觉得我的最后一个答案以及这里的大多数答案都使用某种半统计方法来获得钟形曲线类型的结果 return .我在下面提供的代码与掷骰子时的工作方式相同。所以,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>

最简单的方法是生成两个 0-50 的随机数并将它们相加。

这给出了偏向 50 的分布,以同样的方式将两个骰子偏向 7。

事实上,通过使用更多的“骰子”(如@Falco 所建议的),您可以更接近钟形曲线:

function weightedRandom(max, numDice) {
    let num = 0;
    for (let i = 0; i < numDice; i++) {
        num += Math.random() * (max/numDice);
    }    
    return num;
}

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

几年前我需要解决这个问题,我的解决方案比其他任何答案都简单。

我在边界之间生成了 3 个随机数并将它们平均。这将结果拉向中心,但完全有可能到达四肢。

获取数字数组等效率不高。您应该采用一个映射,该映射采用 0 到 100 之间的随机数并映射到您需要的分布。因此,在您的情况下,您可以使用 f(x)=-(1/25)x<sup>2</sup>+4x 来获得最多的分布范围中间的值。

有很多不同的方法可以生成这样的随机数。一种方法是计算多个均匀随机数的总和。您求和的随机数数量及其范围将决定最终分布的外观。

加起来的数字越多,就越偏向中心。使用 1 个随机数的总和 已在您的问题中提出,但正如您所注意到的那样,它并不偏向范围的中心。其他答案建议使用 or the .

您可以通过对更多随机数求和来获得对范围中心的更大偏差。在极端情况下,您可以取 99 个随机数的总和,每个随机数不是 0 就是 1。这将是一个二项分布。 (二项分布在某种意义上可以看作是正态分布的离散版本)。这在理论上仍然可以覆盖整个范围,但它非常偏向中心,你永远不应该期望看到它到达端点。

这种方法意味着您可以调整所需的偏差。

您可以编写一个函数,根据权重映射 [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>

你这里有一些很好的答案,给出了具体的解决方案;让我为您描述一般的解决方案。问题是:

  • 我有一个或多或少均匀分布在0和1之间的随机数的来源。
  • 我希望生成一个遵循不同分布的随机数序列。

此问题的一般解决方案是计算出所需分布的分位数函数,然后将分位数函数应用于统一源的输出。

分位数函数是您想要的分布函数积分。分布函数是曲线一部分下的面积等于随机选择的项目出现在该部分的概率的函数。

我在这里举例说明如何这样做:

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

里面的代码是用C#写的,但是原理适用于任何语言;将解决方案调整为 JavaScript.

应该很简单

针对这个问题的最佳解决方案是 提出的解决方案,但我认为还值得一提的是速度更快、更通用的解决方案。


当我要生成满足

两个要求的随机数(字符串、坐标对等)时
  1. 结果集很小。 (不大于 16K 个数字)
  2. 结果集是谨慎的。 (仅限整数)

我通常首先创建一个满足要求的数字数组(字符串、坐标对等)(在你的例子中:一个数字数组包含多次出现的可能性更大的数字。),然后随机选择一个项目那个数组。这样,您只需为每个项目调用一次昂贵的随机函数。

最好的方法是生成一个随机数,该随机数在一组特定的数字中均匀分布,然后将投影函数应用于 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 之间的数字。然后,我们只需要获取该数字并将其投影到我们的第二组,即 0 到 100 之间。

我们知道我们生成的随机数是输入 x 在 0 到 100 之间的抛物线的某个积分。这意味着我们只需假设随机数是 F(x) 的结果,并且求解 x.

在这种情况下,F(x)是一个三次方程,并且在F(x) = ax^3 + bx^2 + cx + d = 0的形式下,下列陈述是正确的:

a = -1/3
b = 50
c = 0
d = -1 * (your random number)

求解 x 会得到您正在寻找的实际随机数,它保证在 [0, 100] 范围内,并且靠近中心的可能性比靠近边缘的可能性高得多。

我建议使用 beta distribution 生成 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))
}

您可以使用辅助随机数来生成 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功能就用吧。此函数 returns 正常数与 average 0sigma 1.

这个数字的 95% 在 average +/- 2*sigma 之内。你的average = 50sigma = 5所以

randomNumber = 50 + 5*gaussian()

。但我想 post 实现说明(我不喜欢 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. 取第 i 个范围并生成该范围内的随机数。

性能优化的补充说明。范围不必按升序或降序排序,因此对于更快的范围查找范围,具有最高权重的范围应该排在第一位,而具有最低权重的范围应该排在最后。

分布

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