Подтвердить что ты не робот

Как генерировать случайные числа, смещенные в сторону одного значения в диапазоне?

Скажем, если бы я хотел создать непредвзятое случайное число между min и max, я бы сделал:

var rand = function(min, max) {
    return Math.floor(Math.random() * (max - min + 1)) + min;
};

Но что, если я хочу генерировать случайное число между min и max, но более смещен к значению N между min и max до степени D? Лучше всего проиллюстрировать его кривой вероятности:

enter image description here

4b9b3361

Ответ 1

Вот один из способов:

  • Получить случайное число в диапазоне min-max
  • Получить произвольное нормированное значение смешивания
  • Смешать случайное с предубеждением на основе случайного микса

Ie., в псевдо:

Variables:
  min = 0
  max = 100
  bias = 67      (N)
  influence = 1  (D) [0.0, 1.0]

Formula:
  rnd = random() x (max - min) + min
  mix = random() x influence
  value = rnd x (1 - mix) + bias x mix

Коэффициент смешивания может быть уменьшен с помощью вторичного фактора, чтобы установить, насколько он должен влиять (т.е. mix * factor, где коэффициент равен [0, 1]).

Demo

Это приведет к смещению произвольного диапазона. Верхняя полоса имеет 1 влияние, влияние нижней части 0,75. Смещение здесь установлено равным 2/3 позиции в диапазоне. Нижняя полоса без (преднамеренного) смещения для сравнения.

var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "red"; ctx.fillRect(399,0,2,110);  // draw bias target
ctx.fillStyle = "rgba(0,0,0,0.07)";

function getRndBias(min, max, bias, influence) {
    var rnd = Math.random() * (max - min) + min,   // random in range
        mix = Math.random() * influence;           // random mixer
    return rnd * (1 - mix) + bias * mix;           // mix full range and bias
}

// plot biased result
(function loop() {
  for(var i = 0; i < 5; i++) {  // just sub-frames (speedier plot)
    ctx.fillRect( getRndBias(0, 600, 400, 1.00),  4, 2, 50);
    ctx.fillRect( getRndBias(0, 600, 400, 0.75), 55, 2, 50);
    ctx.fillRect( Math.random() * 600          ,115, 2, 35);
  }
  requestAnimationFrame(loop);
})();
<canvas width=600></canvas>

Ответ 2

Говорите, когда вы используете Math.floor(Math.random() * (max - min + 1)) + min;, вы на самом деле создаете равномерный дистрибутив. Чтобы получить распределение данных в вашем графике, вам нужно распределение с ненулевой асимметрией.

Существуют разные методы получения таких распределений. Вот пример бета-дистрибутива, найденный в stackoverflow.


Вот пример, приведенный по ссылке:

unif = Math.random()  // The original uniform distribution.

И мы можем перенести его в бета-дистрибутив, выполнив

beta = sin(unif*pi/2)^2 // The standard beta distribution

Чтобы получить асимметрию, показанную на диаграмме,

beta_right = (beta > 0.5) ? 2*beta-1 : 2*(1-beta)-1;

Вы можете изменить значение 1 на любое другое, чтобы оно исказилось до другого значения.

Ответ 3

Просто для удовольствия, здесь версия, которая опирается на гауссовую функцию, как упоминается в комментарии SpiderPig к вашему вопросу. Функция Гаусса применяется к случайному числу от 1 до 100, где высота колокола указывает, насколько близко конечное значение будет N. Я интерпретировал степень D для обозначения того, насколько вероятным конечное значение должно быть близко к N, и поэтому D соответствует ширине колокола - чем меньше D, тем меньше вероятность смещения. Очевидно, что пример может быть далее откалиброван.

(Я скопировал метод холста Кен Файрстенберга, чтобы продемонстрировать функцию.)

function randBias(min, max, N, D) {
  var a = 1,
      b = 50,
      c = D;

  var influence = Math.floor(Math.random() * (101)),
    x = Math.floor(Math.random() * (max - min + 1)) + min;

  return x > N 
         ? x + Math.floor(gauss(influence) * (N - x)) 
         : x - Math.floor(gauss(influence) * (x - N));

  function gauss(x) {
    return a * Math.exp(-(x - b) * (x - b) / (2 * c * c));
  }
}

var ctx = document.querySelector("canvas").getContext("2d");
ctx.fillStyle = "red";
ctx.fillRect(399, 0, 2, 110);
ctx.fillStyle = "rgba(0,0,0,0.07)";

(function loop() {
  for (var i = 0; i < 5; i++) {
    ctx.fillRect(randBias(0, 600, 400, 50), 4, 2, 50);
    ctx.fillRect(randBias(0, 600, 400, 10), 55, 2, 50);
    ctx.fillRect(Math.random() * 600, 115, 2, 35);
  }
  requestAnimationFrame(loop);
})();
<canvas width=600></canvas>

Ответ 4

Fun: используйте изображение как функцию плотности. Образец случайных пикселей, пока вы не получите черный, затем возьмите координату x.

enter image description here

код:

getPixels = require("get-pixels"); // npm install get-pixels

getPixels("distribution.png", function(err, pixels) {
  var height, r, s, width, x, y;
  if (err) {
    return;
  }
  width = pixels.shape[0];
  height = pixels.shape[1];
  while (pixels.get(x, y, 0) !== 0) {
    r = Math.random();
    s = Math.random();
    x = Math.floor(r * width);
    y = Math.floor(s * height);
  }
  return console.log(r);
});

Пример вывода:

0.7892316638026386
0.8595335511490703
0.5459279934875667
0.9044852438382804
0.35129814594984055
0.5352215224411339
0.8271261665504426
0.4871773284394294
0.8202084102667868
0.39301465335302055

Масштаб по вкусу.