Отримайте випадкове число, орієнтоване на центр


238

Чи можливо отримати випадкове число між 1-100 і зберегти результати переважно в межах 40-60? Я маю на увазі, він вийде з цього діапазону рідко, але я хочу, щоб він був в основному в межах цього діапазону ... Чи можливо це за допомогою JavaScript / jQuery?

Зараз я просто використовую базовий Math.random() * 100 + 1.




1
можливо дублікат: stackoverflow.com/questions/1527803 / ...
Mahedi Sabuj

20
Мені подобається, куди йде це питання, але я думаю, що воно повинно бути більш конкретним. Ви хочете розподілу Z (крива дзвіночка), трикутника чи якогось розподілу пилоподібних? На мою думку, можна відповісти на це питання.
Патрік Робертс

12
Це можна зробити в JavaScript, але точно не має нічого спільного з jQuery ... :)
А. Вольф

Відповіді:


397

Найпростішим способом було б генерувати два випадкових числа від 0-50 і додавати їх разом.

Це дає нахил розподілу у напрямку до 50, таким же чином котячи два ухили в кістки до 7.

Насправді, використовуючи більшу кількість "кісток" (як пропонує @Falco) , ви можете зробити більш наближене до кривої дзвону:

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

Зважені випадкові числа

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


12
Це просте і швидке рішення, яке можна легко зважити більше, додавши більше цифр, наприклад, 4 x (0-25) і дасть вам гарну дзвінку для розподілу!
Фалько

8
Це фантастичний біт коду. Я думаю, що я закоханий у це. Простий, швидкий, ефективний; чудова відповідь. Дякуємо, що опублікували це.
ctwheels

14
Чудова відповідь, але якщо хтось має намір використовувати це для створення нормального розподілу, він є досить неефективним (і вам потрібно перетворити його, щоб отримати бажану середню і стандартну відхилення). Більш ефективним варіантом було б перетворення Box-Muller, яке досить легко здійснити і зрозуміти, якщо ви знаєте трохи математики.
Брендон

1
@RaziShaban Це досить інтуїтивно: є лише одна комбінація кидків, що додає до 2 (лише зміїні очі), але є 6 різних комбінацій, які складають до 7 (6-1, 5-2, 4-3, 3- 4, 2-5, 1-6). Якщо узагальнити N-однобічні кубики, пік завжди N + 1.
Бармар

2
@RaziShaban Вивчення випадкових змінних є центральною частиною статистики. Той факт, що, коли ми збільшуємо кістки, ми підходимо до нормального розподілу - відома теорема центрального ліміту .
BlueRaja - Danny Pflughoeft

48

Ви маєте кілька хороших відповідей, які дають конкретні рішення; дозвольте описати для вас загальне рішення. Проблема полягає в наступному:

  • У мене є джерело більш-менш рівномірно розподілених випадкових чисел між 0 і 1.
  • Я хочу створити послідовність випадкових чисел, які слідують за різним розподілом.

Загальне рішення цієї проблеми - розробити квантильну функцію потрібного розподілу, а потім застосувати квантильну функцію до виводу вашого рівномірного джерела.

Функція квантиля є зворотній з інтеграла від потрібної функції розподілу . Функція розподілу - це функція, де площа під частиною кривої дорівнює ймовірності того, що випадково обраний елемент опиниться у цій частині.

Я наводжу приклад, як це зробити тут:

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

Код в C # є, але принципи застосовуються до будь-якої мови; слід адаптувати рішення до JavaScript.


2
Мені подобається такий підхід. Можливо, хочеться додати, що існує бібліотека javascript, яка генерує гауссові (та інші ненормальні) дистрибуції: simjs.com/random.html
Флоріс,

36

Прийом масивів чисел тощо не є ефективним. Ви повинні зробити відображення, яке займає випадкове число від 0 до 100, і відображає потрібний вам розподіл. Тож у вашому випадку ви можете взяти для отримання розподілу з найбільшою кількістю значень у середині діапазону.f(x)=-(1/25)x2+4x

Поширення


2
Ми насправді не знаємо, який розподіл потрібен. "В основному 40-60" означає для мене криву дзвону.
Lefty

так, ти маєш рацію, можливо, тобі потрібні кращі карти, але це банально
iCaramba

3
Я візьму на це ваше слово, тому що це не моя область знань. Чи можете ви відрегулювати функцію та відобразити нову криву?
Lefty

1
@Lefty - спрощена крива дзвону xвід 0 до 100 (взято з цього питання ):y = (Math.sin(2 * Math.PI * (x/100 - 1/4)) + 1) / 2
Sphinxxx

@Sphinxxx Це не крива дзвону, це крива гріха. Крива дзвоника ніколи не торкається осі x.
BlueRaja - Danny Pflughoeft

17

Я можу зробити щось на кшталт встановлення "шансу" на те, щоб число було дозволено "вийти за межі". У цьому прикладі 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/


5
Можливо, хтось із більш статистичною деталізацією може мене виправити, і хоча це досягає того, що шукає ОП (тому я проголосував), але це не справді вибере # поза межами 20% часу, правильно? У цьому рішенні 20% часу ви мали б можливість вибрати номер 1-100, який включає 40-60. Невже це не буде (0,2 * 0,8) 16%, щоб вибрати # поза межами, чи я щось пропускаю?
Джош

Ні, ти маєш рацію. Це лише моє формулювання. Я виправлю це. Дякую!
Побітове креативне

1
@Josh - Це досить місце на. Ось простий доказ того, що схоже на jsfiddle.net/v51z8sd5 . Він покаже відсоток цифр, виявлених поза межами, і коливається в межах 0,16 (16%).
Травіс J

15

Мені потрібно було вирішити цю проблему кілька років тому, і моє рішення було простіше, ніж будь-яка з інших відповідей.

Я створив 3 рандеми між межами і усереднював їх. Це тягне результат до центру, але залишає цілком можливим дістатися до кінцівок.


7
Чим це краще / відрізняється від відповіді BlueRaja? Там він бере суму (2,3, ... будь-яке число, яку ви хочете) випадкових чисел і бере середнє. Результат ідентичний вашому, коли ви використовуєте BellFactor3.
Флоріс,

@floris добре, я не кодую в сімействі мов c, щоб ця відповідь навіть не виглядала так, ніби вона робила те саме, що і моя відповідь, поки я просто не перечитав її зараз. Я створив свій метод невеликою кількістю проб і помилок і виявив, що 3 випадки - це правильне число. Крім того, шахту можна виконати в один рядок і все ще легко зрозуміти.
Lefty

2
Дійсно? Ви не думаєте, що між JS та C є подібність? Гаразд, давайте просто скажу, що я не можу говорити ВСЕ з цих мов, ні на Java, які, на мене, схожі в порівнянні з мовами, якими я знайомий.
Lefty

1
Справедливий момент, мене насправді приваблював лише титул як щось, що я вирішив сам, і був дуже гордий тим, як це зробив. Знову ж таки, я не знав, що це питання js, поки ви просто цього не сказали. Пощастило, адже моя техніка не залежить від мови, і деякі люди, здається, вважають це корисною відповіддю.
Lefty

5
JavaScript насправді є сімейною мовою C ... але ну добре.
Жорен

14

Це виглядає нерозумно, але ви можете використовувати 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;
}

11

Звичайно, це можливо. Зробіть випадковий 1-100. Якщо число <30, то генерують число в діапазоні 1-100, якщо не генерують в діапазоні 40-60.


11

Існує маса різних способів генерування таких випадкових чисел. Один із способів зробити це - обчислити суму декількох однаково випадкових чисел. Скільки випадкових чисел ви підсумовуєте та який їх діапазон, визначатиме, як виглядатиме кінцевий розподіл.

Чим більше цифр ви підсумуєте, тим більше воно буде упереджене до центру. Використання суми 1 випадкового числа було запропоновано у вашому запитанні, але, як ви помітили, воно не є упередженим у напрямку до діапазону. Інші відповіді пропонують використовувати суму 2 випадкових чисел або суму 3 випадкових чисел .

Ви можете отримати ще більше ухилу до центру діапазону, взявши суму більше випадкових чисел. В крайньому випадку, ви можете взяти суму 99 випадкових чисел, кожне з яких було 0 або 1. Це було б біноміальним розподілом. (Біноміальні розподіли в певному сенсі можна розглядати як дискретний варіант нормальних розподілів). Теоретично це все ще може охопити весь діапазон, але він має стільки упередженості щодо центру, що ніколи не слід очікувати, що він досягне кінцевих точок.

Такий підхід означає, що ви можете налаштувати лише те, наскільки ви хочете,


8

Як щодо використання чогось подібного:

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>

Те, як я його закодував, дозволяє встановити пару змінних:
loops = кількість спроб результатів
= кількість разів, коли функція спробує отримати число між 40-60, перш ніж вона перестане працювати через цикл while

Доданий бонус: Він використовує доки !!! Дивовижність у кращих випадках


8

Ви можете написати функцію , яка відображає випадкові значення між [0, 1)в [1, 100]залежності від ваги. Розглянемо цей приклад:

0,0-1,0 до 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>


7

Ось зважений розчин на 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>


6

Гаразд, тому я вирішив додати ще одну відповідь, тому що я відчував, що як моя остання відповідь, як і більшість відповідей тут, використовують якийсь напівстатистичний спосіб отримання повернення результату кривої дзвіночки. Код, який я надаю нижче, працює так само, як і коли ви котите кістки. Тому найважче отримати 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

Я рекомендую використовувати бета-версію щоб генерувати число від 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))
}

5
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

Найкращим рішенням, спрямованим на цю саму проблему, є рішення, запропоноване BlueRaja - Danny Pflughoeft, але, я думаю, варто також згадати дещо швидше та більш загальне рішення.


Коли мені доведеться генерувати випадкові числа (рядки, пари координат тощо), що відповідають двом вимогам

  1. Набір результатів зовсім невеликий. (не більше 16К чисел)
  2. Набір результатів дискретний. (як-от цілі числа)

Зазвичай я починаю зі створення масиву чисел (рядків, пар координат тощо), що виконує вимогу (У вашому випадку: масив чисел, що містить найбільш ймовірні кілька разів). Потім вибираю випадковий елемент цього масиву. Таким чином, вам потрібно лише один раз викликати дорогу випадкову функцію за кожний предмет.


1
Якщо ви збираєтеся заздалегідь заповнити масив варіантів, ви можете також перемістити їх згодом. Тоді ви можете просто захопити їх по порядку, поки не закінчите. Перемішайте знову, якщо / коли ви потрапили в кінець списку.
Геобіць

@Geobits Переміщення списку набагато більш ресурсомістке завдання, ніж випадковий вибір одного з його елементів. Це лише хороший вибір, якщо список повинен бути передбачуваним.
mg30rg

1
Але ви робите це лише один раз за цикл списку, а не кожен раз. Якщо ви попередньо обробляєте це (оскільки у вас все-таки є крок попередньої обробки, я припускаю, що це нормально), то дуже швидко отримати кожне число згодом. Ви можете змінити розміщення кожного разу, коли у вас є час простою, або знаєте, що вам не знадобиться випадкове число на деякий час. Просто пропонуючи це як альтернативу, обидва мають (не) переваги.
Геобіць

@Geobits Якщо ви зробите це так, цифри "єдиної ймовірності" будуть "випадати", і до переназначення вони не зможуть з'явитися в результаті. (тобто, якщо ви імітуєте метання двох кубиків, у вас не буде найменшого шансу отримати номер 2 більше ніж удвічі.)
mg30rg

1
Це набагато краща причина не використовувати його, за винятком рідкісних додатків, де це нормально;)
Geobits

4

Поширення

 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

Ви можете використовувати довідкове випадкове число, щоб генерувати випадкові числа в 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

Якщо ви можете використовувати gaussianфункцію, використовуйте її. Ця функція повертає нормальне число за допомогою average 0та sigma 1.

95% цієї кількості знаходяться в межах average +/- 2*sigma. Твоє average = 50, і sigma = 5так

randomNumber = 50 + 5*gaussian()

3

Найкращий спосіб зробити це - генерувати випадкове число, яке розподіляється порівну в певному наборі чисел, а потім застосувати функцію проекції до множини між 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] і набагато більша ймовірність бути близьким до центру, ніж ребра.


3

Ця відповідь справді гарна . Але я хотів би розмістити інструкції щодо впровадження (я не в 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. Утворити випадкове число Nз діапазону [0, сума всіх ваг).
  2. for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
  3. Візьміть iдіапазон і генеруйте випадкове число в цьому діапазоні.

Додаткова примітка для оптимізації продуктивності. Діапазони не повинні бути впорядковані ні по порядку, ні по порядку зменшення, тому для швидшого діапазону пошуку з найвищою вагою слід пройти першим, а той, що має найменшу вагу, повинен бути останнім.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.