Получить случайное число, сосредоточенное на центре
можно ли получить случайное число от 1-100 и сохранить результаты в основном в диапазоне 40-60? Я имею в виду, что он будет выходить из этого диапазона редко, но я хочу, чтобы он был в основном в этом диапазоне... Возможно ли это с помощью JavaScript / jQuery?
сейчас я просто использую basic Math.random() * 100 + 1.
20 ответов:
самым простым способом было бы сгенерировать два случайных числа из 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/
У вас есть несколько хороших ответов здесь, которые дают конкретные решения; позвольте мне описать для вас общее решение. Проблема заключается в следующем:
- у меня есть источник более или менее единообразно распределенные случайные числа между 0 и 1.
- Я хочу создать последовательность случайных чисел, которые следуют за другим распределением.
общее решение этой проблемы заключается в выработке функция квантиля желаемого распределение, а затем применить функцию квантиля к выходу вашего однородного источника.
функция квантиля-это инверсия на Интеграл нужные функции распределения. Функция распределения-это функция, в которой площадь под частью кривой равна вероятности того, что случайно выбранный элемент будет находиться в этой части.
Я приведу пример, как это сделать здесь:
http://ericlippert.com/2012/02/21/generating-random-non-uniform-data/
код там есть в C#, но принципы применимы к любому языку; он должен быть простым, чтобы адаптировать решение к JavaScript.
с массивами чисел и т. д. это не эффективно. Вы должны взять отображение, которое принимает случайное число от 0 до 100 и отображает необходимое вам распределение. Так что в вашем случае, вы могли бы взять
f(x)=-(1/25)x2+4xчтобы получить распределение с наибольшим количеством значений в середине диапазона.
я мог бы сделать что-то вроде установки "шанса" для числа, которое будет разрешено выйти "за пределы". В этом примере с вероятностью 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/
Мне нужно решить эту проблему несколько лет назад, и мое решение было проще, чем любые другие ответы.
я сгенерировал 3 рандома между границами и усреднил их. Это тянет результат к центру, но оставляет полностью возможным достичь конечностей.
Это выглядит глупо, но вы можете использовать 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 случайного числа уже было предложено в вашем вопросе, но, как вы заметили, не предвзято к центру диапазон. Другие ответы предлагают использовать сумма 2 случайных чисел или сумма 3 случайных чисел.
вы можете получить еще больше смещения к центру диапазона, взяв сумму более случайных чисел. В крайнем случае вы можете взять сумму 99 случайных чисел, каждое из которых было либо 0, либо 1. Это будет биномиальное распределение. (Биномиальные распределения можно в некотором смысле рассматривать как дискретную версию нормальных распределений). Это может еще теоретически он охватывает весь диапазон, но у него так много смещения к центру, что вы никогда не должны ожидать, что он достигнет конечных точек.
этот подход означает, что вы можете настроить, сколько смещения вы хотите.
Как насчет использования чего-то вроде этого:
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>то, как я его закодировал, позволяет вам установить пару переменных:
петли = количество результатов
нах = количество раз функция будет пытаться получить число между 40-60, прежде чем он перестанет работать через цикл whileдобавлен бонус: он использует do while!!! Удивительность в лучшем виде
вы можете написать функцию, которая отображает случайные значения между
[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>
вот взвешенное решение на 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-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)) }
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); }
лучшее решение, нацеленное на эту самую проблему, - это то, которое предлагает BlueRaja-Danny Pflughoeft но я думаю, что несколько более быстрое и более общее решение также стоит упомянуть.
когда мне нужно генерировать случайные числа (строки, пары координат и т. д.) удовлетворение двух требований
- результирующий набор довольно мал. (не более 16K чисел)
- результирующий набор является сдержанным. (как целые числа только)
обычно я начинаю с создания массива чисел (строк, пар координат и т. д.) выполнение требования (в вашем случае: массив чисел, содержащий более вероятные несколько раз.), затем выберите случайный элемент этого массива. Таким образом, вам нужно только вызвать дорогую случайную функцию один раз за элемент.
распределение
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; }
вы можете использовать случайное число помощника, чтобы генерировать случайные числа в 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 0иsigma 1.95% из этого числа находятся в пределах
average +/- 2*sigma. Вашaverage = 50иsigma = 5такrandomNumber = 50 + 5*gaussian()
лучший способ сделать это-создать случайное число, которое равномерно распределяется в определенном наборе чисел, а затем применить функцию проекции к набору между 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] и гораздо выше вероятность быть близко к центру, чем к краям.
этот ответ действительно хорош. Но я хотел бы опубликовать инструкции по реализации (я не в JavaScript, поэтому надеюсь, что вы поймете) для другой ситуации.
Предположим, у вас есть диапазоны и веса для каждого диапазона:
ranges - [1, 20], [21, 40], [41, 60], [61, 100] weights - {1, 2, 100, 5}
начальная статическая информация может быть кэширован:
- сумма всех Весов (108 образец)
- границы выбора. Это в основном такая формула:
Boundary[n] = Boundary[n - 1] + weigh[n - 1]иBoundary[0] = 0. Образец имеетBoundary = {0, 1, 3, 103, 108}поколение номер:
- генерация случайных чисел!--4--> из диапазона [0, сумма всех Весов).
for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)- взять
iбольшой ассортимент и генерировать случайное число в этом диапазоне.
дополнительное примечание для оптимизации производительности. Диапазоны не должны быть упорядочены ни по возрастанию, ни по убыванию, поэтому для более быстрого поиска диапазона диапазон, который имеет самый высокий вес должен идти первым, а тот, у кого самый низкий вес, должен идти последним.



Comments