取一个集中在中心的随机数

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

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

18049 次浏览

当然这是可能的。随机抽取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;
}

我可能会做一些事情,比如设置一个“机会”,让数字被允许“出界”。在这个例子中,数字为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>

小提琴: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>

我的编码方式允许你设置几个变量:
循环 = number of results
. 循环 = number of results
. 循环 = number of results Tries =函数在停止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>

好的,所以我决定添加另一个答案,因为我觉得我的最后一个答案,以及这里的大多数答案,使用某种半统计的方式来获得钟形曲线类型的结果回报。下面我提供的代码与掷骰子的工作方式相同。因此,得到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)x2+4x来得到一个分布,在你的范围的中间值最多。

Distribution

生成这样的随机数有很多不同的方法。一种方法是计算多个均匀随机数的和。你和多少个随机数以及它们的范围将决定最终分布的样子。

你加起来的数字越多,它就越向中心倾斜。使用1个随机数的和已经在你的问题中提出了,但正如你注意到的那样,它并不偏向于范围的中心。其他答案建议使用两个随机数的和3个随机数的和

通过取更多随机数的和,你可以得到更偏向范围中心的结果。在极端情况下,你可以取99个随机数字的和,每个数字都是0或1。这是一个二项分布。(二项分布在某种意义上可以被看作是正态分布的离散版本)。理论上,这仍然可以覆盖整个范围,但它有很大的偏向中心,你永远不会期望看到它到达端点。

这种方法意味着你可以调整你想要的偏差。

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

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

  • 我有一个或多或少统一分布随机数的源,在0到1之间。
  • 我希望产生一个遵循不同分布的随机数序列。

这个问题的一般解决方案是计算出你想要的分布的分位数函数,然后将分位数函数应用到你的统一源的输出。

分位数函数是你想要的分布函数积分。分布函数是这样的函数,曲线的一部分下面的面积等于随机选择的项目将在该部分的概率。

我在这里给出一个如何做到这一点的例子:

http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/ < a href = " http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/ " > < / >

其中的代码是c#编写的,但原则适用于任何语言;它应该很容易适应JavaScript的解决方案。

针对这个问题的最佳解决方案是由BlueRaja - Danny Pflughoeft提出的,但我认为一个更快、更通用的解决方案也值得一提。


当我必须生成随机数(字符串,坐标对等)时,满足的两个要求

  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到166666之间的数字。然后,我们只需要把这个数字投影到我们的第二个集合,它在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]范围内,并且更接近中心而不是边缘。

我建议使用贝塔分布来生成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函数,就使用它。此函数返回带有average 0sigma 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范围并在该范围内生成随机数。

性能优化的附加说明。范围不需要按升序或降序排列,所以为了更快的范围查找,权重最高的范围应该排在前面,权重最低的范围应该排在最后。

分布

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