Uzyskaj losową liczbę skoncentrowaną na środku


238

Czy można uzyskać losową liczbę od 1 do 100 i utrzymać wyniki głównie w przedziale 40–60? Chodzi mi o to, że rzadko wychodzi poza ten zakres, ale chcę, aby był głównie w tym zakresie ... Czy jest to możliwe w JavaScript / jQuery?

Teraz używam tylko podstawowego Math.random() * 100 + 1.





20
Lubię, dokąd zmierza to pytanie, ale myślę, że powinno być bardziej szczegółowe. Czy chcesz rozkład Z (krzywa dzwonowa), rozkład trójkątów lub jakiś rozkład piłokształtnych? Moim zdaniem istnieje wiele możliwości odpowiedzi na to pytanie.
Patrick Roberts

12
Można to zrobić w javascript, ale na pewno nie ma to nic wspólnego z jQuery ... :)
A. Wolff

Odpowiedzi:


397

Najprostszym sposobem byłoby wygenerowanie dwóch liczb losowych od 0-50 i dodanie ich razem.

Daje to rozkład nastawiony na 50, w ten sam sposób rzucając dwie tendencje kostek w kierunku 7.

W rzeczywistości, używając większej liczby „kostek” (jak sugeruje @Falco) , możesz przybliżyć się do krzywej dzwonowej:

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

Weighted random numbers

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


12
Jest to łatwe i szybkie rozwiązanie, które można łatwo wyważyć, dodając więcej liczb, np. 4 x (0–25), i da ci ładną krzywą dzwonkową dla rozkładu!
Falco

8
To fantastyczny kawałek kodu. Myślę, że się w tym zakochałem. Prosty, szybki, wydajny; świetna odpowiedź. Dziękujemy za opublikowanie tego.
ctwheels

14
Świetna odpowiedź, ale jeśli ktoś zamierza użyć tego do wygenerowania rozkładu normalnego, jest dość nieefektywny (i trzeba go przekształcić, aby uzyskać pożądaną średnią i odchylenie standardowe). Bardziej wydajną opcją byłaby transformacja Boxa-Mullera, która jest dość łatwa do wdrożenia i zrozumienia, jeśli znasz trochę matematyki.
Brendon

1
@RaziShaban To dość intuicyjne: jest tylko jedna kombinacja rzutów kostką, która sumuje się do 2 (tylko wężowe oczy), ale istnieje 6 różnych kombinacji, które składają się na 7 (6-1, 5-2, 4-3, 3- 4, 2-5, 1-6). Jeśli uogólnisz na kostki N-stronne, szczyt zawsze wynosi N + 1.
Barmar

2
@RaziShaban Badanie zmiennych losowych jest centralną częścią statystyki. Fakt, że wraz ze wzrostem kostek zbliżamy się do rozkładu normalnego, jest znanym centralnym twierdzeniem o granicy .
BlueRaja - Danny Pflughoeft

48

Masz tutaj kilka dobrych odpowiedzi, które dają konkretne rozwiązania; Pozwól, że opiszę ci ogólne rozwiązanie. Problemem jest:

  • Mam źródło mniej więcej równomiernie rozmieszczonych liczb losowych od 0 do 1.
  • Chciałbym stworzyć sekwencję liczb losowych o innym rozkładzie.

Ogólnym rozwiązaniem tego problemu jest wypracowanie funkcji kwantylowej pożądanego rozkładu, a następnie zastosowanie funkcji kwantyli do wyjścia jednolitego źródła.

Funkcja kwantyl jest odwrotny od całki od żądanej funkcji rozkładu . Funkcja rozkładu to funkcja, w której pole pod częścią krzywej jest równe prawdopodobieństwu, że losowo wybrany element będzie w tej części.

Daję przykład, jak to zrobić tutaj:

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

Kod tam jest w C #, ale zasady dotyczą każdego języka; dostosowanie rozwiązania do JavaScript powinno być proste.


2
Lubię to podejście. Może warto dodać, że istnieje biblioteka javascript, która generuje Gaussowskie (i inne niestandardowe) dystrybucje: simjs.com/random.html
Floris

36

Odczytywanie tablic liczb itp. Nie jest wydajne. Powinieneś wziąć mapowanie, które przyjmuje losową liczbę od 0 do 100 i mapuje do rozkładu, którego potrzebujesz. W twoim przypadku możesz wziąć rozkład z największą liczbą wartości w środku zakresu.f(x)=-(1/25)x2+4x

Dystrybucja


2
Nie wiemy, jaka dystrybucja jest potrzebna. „Głównie 40–60” oznacza dla mnie krzywą dzwonową.
Lefty

tak, masz rację, może potrzebujesz lepszego mapowania, ale to banalne
iCaramba

3
Wierzę na to, bo to nie jest moja specjalizacja. Czy możesz dostosować funkcję i wyświetlić nową krzywą?
Lefty

1
@ Lewy - Uproszczona krzywa dzwonowa xod 0 do 100 (wzięte z tego pytania ):y = (Math.sin(2 * Math.PI * (x/100 - 1/4)) + 1) / 2
Sphinxxx

@Sphinxxx To nie jest krzywa dzwonowa, to krzywa grzechu. Krzywa dzwonowa nigdy nie dotyka osi x.
BlueRaja - Danny Pflughoeft

17

Mógłbym zrobić coś takiego jak ustawienie „szansy”, aby liczba mogła przekroczyć granicę. W tym przykładzie szansa na 20% będzie wynosić 1-100, w przeciwnym razie 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>

skrzypce: http://jsfiddle.net/kbv39s9w/


5
Może ktoś z bardziej szczegółowymi danymi statystycznymi może mnie poprawić i chociaż osiąga to, czego szuka PO (głosowałem), ale tak naprawdę nie wybrałoby # poza zakresem 20% czasu, prawda? W tym rozwiązaniu w 20% przypadków miałbyś okazję wybrać numer od 1 do 100, który obejmuje 40–60. Czy tak naprawdę nie byłoby (0,2 * 0,8) 16%, aby wybrać # poza zakresem, czy coś mi brakuje?
Josh

Nie, masz rację. To tylko moje sformułowanie. Poprawię to. Dziękuję Ci!
Kreacja bitowa

1
@Josh - To całkiem niezłe miejsce. Oto prosty dowód tego, jak to wygląda jsfiddle.net/v51z8sd5 . Pokaże procent liczb wykrytych poza granicami i unosi się wokół 0,16 (16%).
Travis J

15

Musiałem rozwiązać ten problem kilka lat temu, a moje rozwiązanie było łatwiejsze niż jakiekolwiek inne odpowiedzi.

Wygenerowałem 3 losy między granicami i uśredniłem je. To przyciąga wynik do środka, ale pozostawia całkowicie możliwość dotarcia do kończyn.


7
Czym różni się ta odpowiedź od odpowiedzi BlueRaja? Tam bierze sumę (2,3, ... dowolnej liczby) liczb losowych i przyjmuje średnią. Rezultat jest identyczny z twoim, gdy użyjesz BellFactor3.
Floris

@floris dobrze, nie koduję w rodzinie języków c, więc odpowiedź nawet nie wyglądała tak, jakby robiła to samo, co moja odpowiedź, dopóki nie przeczytałem jej teraz. Swoją metodę stworzyłem metodą prób i błędów i stwierdziłem, że 3 losowe liczby były poprawne. Ponadto moje można wykonać w jednej linii i nadal łatwo je zrozumieć.
Lefty

2
Naprawdę? Nie sądzisz, że istnieje podobieństwo między JS i C? OK, cóż, powiedzmy, że nie umiem ŻADNEGO z tych języków ani Javy, które dla mnie są podobne w porównaniu do języków, które znam.
Lefty

1
Szczerze mówiąc, tak naprawdę przyciągnął mnie sam tytuł, ponieważ sam to rozwiązałem i byłem dość dumny ze sposobu, w jaki to zrobiłem. Znowu nie zdawałem sobie sprawy, że to pytanie js, dopóki tego nie powiedziałeś. Na szczęście naprawdę, ponieważ moja technika nie zależy od języka i niektórzy ludzie uważają, że to przydatna odpowiedź.
Lefty

5
JavaScript jest właściwie językiem rodziny C. ... ale cóż.
Joren

14

To wygląda głupio, ale można użyć rand dwukrotnie:

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

Jasne, że to możliwe. Zrób losowy 1-100. Jeśli liczba wynosi <30, wygeneruj liczbę z zakresu 1-100, jeśli nie, wygeneruj ją w zakresie 40-60.


11

Istnieje wiele różnych sposobów generowania takich liczb losowych. Jednym ze sposobów jest obliczenie sumy wielu równomiernie losowych liczb. Ile liczb losowych zsumujesz i jaki jest ich zasięg, określi, jak będzie wyglądał ostateczny rozkład.

Im więcej liczb zsumujesz, tym bardziej będzie on tendencyjny do środka. Wykorzystanie sumy 1 liczb losowych zostało już zaproponowane w twoim pytaniu, ale jak zauważysz, nie jest tendencyjne w kierunku środka zakresu. Inne odpowiedzi sugerują użycie sumy 2 liczb losowych lub sumy 3 liczb losowych .

Możesz uzyskać jeszcze więcej odchylenia w kierunku środka zakresu, biorąc sumę więcej liczb losowych. W skrajności można przyjąć sumę 99 liczb losowych, z których każda ma wartość 0 lub 1. To byłby rozkład dwumianowy. (Rozkłady dwumianowe można w pewnym sensie postrzegać jako dyskretną wersję rozkładów normalnych). Teoretycznie może to obejmować cały zakres, ale ma tak dużą tendencję do centrum, że nigdy nie należy oczekiwać, że osiągnie punkty końcowe.

Takie podejście oznacza, że ​​możesz dostosować, ile chcesz stronniczości.


8

Co z użyciem czegoś takiego:

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>

Sposób, w jaki go kodowałem, pozwala ustawić kilka zmiennych:
pętle = liczba wyników
prób = liczba przypadków, gdy funkcja spróbuje uzyskać liczbę między 40-60, zanim przestanie działać przez pętlę while

Dodano bonus: Używa zrobić do czasu !!! Awesomeness w najlepszym wydaniu


8

Można napisać funkcję, która mapuje wartości losowych między [0, 1)aby [1, 100]w zależności od wagi. Rozważ ten przykład:

0,0-1,0 do 1-100 procentowo masy

Tutaj wartość jest 0.95odwzorowywana na wartość między [61, 100].
W rzeczywistości mamy .05 / .1 = 0.5, co po mapowaniu [61, 100]daje 81.

Oto funkcja:

/*
 * 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

Oto ważone rozwiązanie dla 3/4 40-60 i 1/4 poza tym zakresem.

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

Ok, więc zdecydowałem się dodać kolejną odpowiedź, ponieważ czułem się tak, jakby moja ostatnia odpowiedź, a także większość odpowiedzi tutaj, wykorzystywały jakiś pół statystyczny sposób uzyskania wyniku typu krzywej dzwonowej. Podany poniżej kod działa tak samo, jak podczas rzutu kostką. Dlatego najtrudniej jest zdobyć 1 lub 99, ale najłatwiej zdobyć 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

Polecam użycie rozkładu beta do wygenerowania liczby między 0-1, a następnie przeskalowanie. Jest dość elastyczny i może tworzyć wiele różnych kształtów dystrybucji.

Oto szybki i brudny sampler:

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

Najlepszym rozwiązaniem tego problemu jest BlueRaja - Danny Pflughoeft, ale myślę, że warto wspomnieć o nieco szybszym i bardziej ogólnym rozwiązaniu.


Kiedy muszę wygenerować liczby losowe (ciągi, pary współrzędnych itp.) Spełniające dwa wymagania

  1. Zestaw wyników jest dość mały. (nie większe niż liczby 16K)
  2. Zestaw wyników jest dyskretny. (jak tylko liczby całkowite)

Zwykle zaczynam od utworzenia tablicy liczb (ciągów, par współrzędnych itp.) Spełniających wymagania (w twoim przypadku: tablicy liczb zawierającej liczby bardziej prawdopodobne wiele razy). Następnie wybieram losowy element tej tablicy. W ten sposób musisz wywołać kosztowną losową funkcję tylko raz na przedmiot.


1
Jeśli masz zamiar wypełnić wiele opcji, możesz je również przetasować później. Następnie możesz po prostu złapać je w kolejności, aż skończy się. Potasuj ponownie, jeśli / kiedy dotrzesz do końca listy.
Geobity

@Geobits Przetasowanie listy jest zadaniem o wiele bardziej wymagającym zasobów niż losowe wybranie jednego z jej elementów. To dobry wybór, jeśli lista musi być przewidywalna.
mg30rg

1
Ale robisz to tylko raz na cykl listy zamiast za każdym razem. Jeśli przetworzysz to wstępnie (ponieważ i tak masz krok wstępnego przetwarzania, zakładam, że jest w porządku), to bardzo szybko zdobędziesz każdą liczbę później. Możesz przetasować, gdy masz przestój, lub wiesz, że przez chwilę nie będziesz potrzebować losowej liczby. Po prostu jako alternatywa, oba mają (nie) zalety.
Geobits

@Geobits Jeśli zrobisz to po swojemu, liczby „pojedynczego prawdopodobieństwa” „wypadną” i do czasu przetasowania nie będą mogły powstać. (tj. jeśli symulujesz rzucanie dwiema kośćmi, nie będziesz miał najmniejszej szansy na uzyskanie liczby 2 więcej niż dwa razy.)
mg30rg

1
To o wiele lepszy powód, aby go nie używać, z wyjątkiem rzadkich aplikacji, w których jest to w porządku;)
Geobits

4

Dystrybucja

 5% for [ 0,39]
90% for [40,59]
 5% for [60,99]

Rozwiązanie

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

Ogólne rozwiązanie

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

Możesz użyć liczby losowej pomocnika, aby wygenerować liczby losowe w 40-60 lub 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

Jeśli możesz użyć tej gaussianfunkcji, użyj jej. Ta funkcja zwraca normalną liczbę za pomocą average 0isigma 1 .

95% tej liczby mieści się w zakresie average +/- 2*sigma. Twój average = 50i sigma = 5tak

randomNumber = 50 + 5*gaussian()

3

Najlepszym sposobem na to jest wygenerowanie liczby losowej, która jest równomiernie rozłożona na pewien zestaw liczb, a następnie zastosowanie funkcji projekcji do zbioru od 0 do 100, w którym prawdopodobieństwo, że projekcja uderzy w pożądane liczby.

Zazwyczaj matematycznym sposobem osiągnięcia tego jest wykreślenie funkcji prawdopodobieństwa liczb, które chcesz. Możemy użyć krzywej dzwonowej, ale dla łatwiejszego obliczenia wystarczy pracować z odwróconą parabolą.

Zróbmy parabolę w taki sposób, aby jej korzenie były równe 0 i 100 bez wypaczania. Otrzymujemy następujące równanie:

f(x) = -(x-0)(x-100) = -x * (x-100) = -x^2 + 100x

Teraz cały obszar pod krzywą od 0 do 100 jest reprezentatywny dla naszego pierwszego zestawu, w którym chcemy wygenerować liczby. Tam generacja jest całkowicie losowa. Wszystko, co musimy zrobić, to znaleźć granice naszego pierwszego zestawu.

Dolna granica to oczywiście 0. Górna granica jest całką naszej funkcji przy 100, czyli

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)

Wiemy więc, że musimy wygenerować liczbę pomiędzy 0 a 166 666. Następnie musimy po prostu wziąć tę liczbę i rzutować ją do naszego drugiego zestawu, który wynosi od 0 do 100.

Wiemy, że liczba losowa, którą wygenerowaliśmy, jest jakąś całką naszej paraboli z wejściem x od 0 do 100. Oznacza to, że po prostu musimy założyć, że liczba losowa jest wynikiem F (x) i rozwiązać dla x.

W tym przypadku F (x) jest równaniem sześciennym, aw postaci F(x) = ax^3 + bx^2 + cx + d = 0prawdziwe są następujące stwierdzenia:

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

Rozwiązanie tego dla x daje rzeczywistą liczbę losową, której szukasz, która z pewnością mieści się w przedziale [0, 100] i znacznie większe prawdopodobieństwo, że znajdzie się bliżej środka niż krawędzi.


3

Ta odpowiedź jest naprawdę dobra . Ale chciałbym opublikować instrukcje implementacyjne (nie jestem w JavaScript, więc mam nadzieję, że zrozumiesz) dla innej sytuacji.


Załóżmy, że masz zakresy i wagi dla każdego zakresu:

ranges - [1, 20], [21, 40], [41, 60], [61, 100]
weights - {1, 2, 100, 5}

Wstępne informacje statyczne mogą być buforowane:

  1. Suma wszystkich wag (w próbce 108)
  2. Granice wyboru zakresu. To w zasadzie ta formuła: Boundary[n] = Boundary[n - 1] + weigh[n - 1]i Boundary[0] = 0. Próbka maBoundary = {0, 1, 3, 103, 108}

Generowanie liczb:

  1. Wygeneruj losową liczbę Nz zakresu [0, Suma wszystkich wag).
  2. for (i = 0; i < size(Boundary) && N > Boundary[i + 1]; ++i)
  3. Wybierz izakres i wygeneruj losową liczbę w tym zakresie.

Dodatkowa uwaga na temat optymalizacji wydajności. Zakresy nie muszą być zamawiane ani w kolejności rosnącej, ani malejącej, dlatego w celu uzyskania większego zasięgu zasięg o największej wadze powinien być pierwszy, a najmniejszy - ostatni.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.