JS generuje losowe wartości logiczne

149

Proste pytanie, ale interesują mnie tutaj niuanse.

Generuję losowe wartości logiczne za pomocą następującej metody, którą sam wymyśliłem:

const rand = Boolean(Math.round(Math.random()));

Ilekroć się random()pojawia, wydaje się, że zawsze istnieje pułapka - nie jest naprawdę przypadkowa, jest zagrożona przez coś lub w inny sposób itp. Więc chciałbym wiedzieć:

a) Czy powyższe jest najlepszym sposobem, aby to zrobić?

b) Czy za dużo myślę?

c) Czy nie myślę o rzeczach?

d) Czy istnieje lepszy / szybszy / elegancki sposób, o którym nie wiem?

(Również trochę zainteresowany, jeśli B i C wykluczają się wzajemnie).

Aktualizacja

Jeśli to robi różnicę, używam tego do ruchu postaci AI.

Ben
źródło
31
const rand = Math.random() < 0.5jest równoważne i prostsze.
Hamms
3
Możesz osiągnąć tylko pseudolosowość , a nie prawdziwą przypadkowość.
Oriol
1
W rzeczywistości nic nie jest przypadkowe, celem jest zbliżenie się do losowości, jak to tylko możliwe.
Adam Buchanan Smith
A jeśli masz szansę 50/50, math.randompowinno być dużo. Po prostu użyj milisekund dla nasion.
Adam Buchanan Smith
Myślę, że to dość przypadkowe, kiedy ktoś odwiedza stronę internetową: D, więc wpadłem na ten pomysł ...Boolean(+Date.now()%2)
Roko C. Buljan

Odpowiedzi:

378

Technicznie rzecz biorąc, kod wygląda dobrze, ale jest trochę zbyt złożony. Można porównać Math.random()do 0.5bezpośrednio, jak zakresie Math.random()Is [0, 1)(to znaczy „w przedziale od 0 do 1, w tym 0, ale nie 1”). Możesz podzielić zakres na [0, 0.5)i [0.5, 1).

var random_boolean = Math.random() <= 0.5;

// Example
console.log(Math.random() <= 0.1) // %10 probability of getting "true"
console.log(Math.random() <= 0.4) // %40 probability of getting "true"
console.log(Math.random() <= 0.5) // %50 probability of getting "true"
console.log(Math.random() <= 0.8) // %80 probability of getting "true"
console.log(Math.random() <= 0.9) // %90 probability of getting "true"

kelwin
źródło
7
Podoba mi się to jedno rozwiązanie, ponieważ pozwala dostosować prawdopodobieństwo prawda / fałsz
Evanion,
jeśli na przykład zmienisz liczbę z 0,5 na 0,9, czy zwiększy to prawdopodobieństwo fałszu i jak?
Agent Zebra
jeśli zmienisz ją z 0,5 na 0,9. Wtedy prawdopodobieństwo prawdopodobnie ulegnie zmianie. Myślę, że możesz to wypróbować z dużą liczbą losowych pętli, na przykład 10000 iteracji.
Kelvin
Dla nowoczesnego JavaScript powinieneś użyć letnp.let randomBool = Math.random() >= 0.5;
Chris Halcrow
7
Dlaczego nie użyć po prostu <zamiast >=? Math.random() < 0.5nie obejmuje 0,5 dla dolnej połowy i nie obejmuje 1 dla górnej połowy - więc nadal jest to dokładnie 50% szansy. Poza tym jest krótszy. Moim zdaniem Math.random() < 0.1bardziej intuicyjne jest odczytanie jako „10% szansy na prawdę” niż Math.random() >= 0.9. Myślę, że to jednak dość wybredne. Niezła odpowiedź.
Aaron Plocharczyk
28

Jeśli Twój projekt ma lodash, możesz:

_.sample([true, false])

Alternatywnie możesz użyć własnej funkcji przykładowej ( źródło ):

const sample = arr => arr[Math.floor(Math.random() * arr.length)];

źródło
13

Aby uzyskać bardziej bezpieczną kryptograficznie wartość, możesz użyć crypto.getRandomValuesw nowoczesnych przeglądarkach.

Próba:

var randomBool = (function() {
  var a = new Uint8Array(1);
  return function() {
    crypto.getRandomValues(a);
    return a[0] > 127;
  };
})();

var trues = 0;
var falses = 0;
for (var i = 0; i < 255; i++) {
  if (randomBool()) {
    trues++;
  }
  else {
    falses++;
  }
}
document.body.innerText = 'true: ' + trues + ', false: ' + falses;

Zauważ, że cryptoobiekt jest API DOM, więc nie jest dostępny w Node, ale istnieje podobny interfejs API dla Node .

Alexander O'Mara
źródło
4
Math.random()jest notorycznie nieprzypadkowy na wiele sposobów, świetna alternatywna sugestia
Charles Harris
3
Dodam tutaj tylko małą korektę, ponieważ odkryłem po 50 000 000 przebiegów, że generuje średnio 0,78% lub więcej zer: zwraca a [0] <= 127; (
Inaczej
2
@AmundMidtskog Dobra rozmowa. Powinienem był wpisać:a[0] > 127
Alexander O'Mara,
1
Nawiasem mówiąc, zwykle możesz chcieć wygenerować znacznie większą liczbę próbek niż tylko 255. Zamiast tego, aby zmniejszyć szum w danych, około 100 000 - lub nawet dziesiątki milionów, jak zasugerowano w innym komentarzu, jeśli chcesz zobaczyć błędy tak małe, jak 0,78%.
krakaj
8
!Math.round(Math.random());

­­­­­­­­­­­­­­

user12066722
źródło
7
Sformatuj to bardziej pomocnie ( stackoverflow.com/editing-help ) i dodaj wyjaśnienie. Odpowiedzi zawierające tylko kod nie są zbyt mile widziane. Dodanie wyjaśnienia pomogłoby w walce z błędnym przekonaniem, że StackOverflow jest bezpłatną usługą pisania kodu.
Yunnosch
5

Będąc pod dużym wrażeniem odpowiedzi Kelvina, chciałbym zaproponować dość podobne, ale nieco ulepszone rozwiązanie.

var randomBoolean = Math.random() < 0.5;

To rozwiązanie jest nieco bardziej oczywiste do odczytania, ponieważ liczba po prawej stronie <mówi o prawdopodobieństwie uzyskania, truea nie otrzymania false, co jest bardziej naturalne do zrozumienia. <Jest również krótszy o jeden symbol niż >=;

Arthur Khazbs
źródło
1

Potencjalnie szybsze rozwiązania ...

Podejście operatora bitowego, o którym właśnie pomyślałem Math.random() + .5 >> 0lub ~~(Math.random() + .5). Oto test wydajności do samodzielnej oceny.

let randomBoolean = Math.random() + .5 >> 0;                 //chance of true
const randomBoolean = chance => Math.random() + chance >> 0; //chance of true

Operatory bitowe są w tym przypadku zasadniczo takie same jak użycie Math.trunc()lub Math.floor(), dlatego też jest to możliwe Math.trunc(Math.random() + .5).

let randomBoolean = Math.trunc(Math.random() + .5);
const randomBoolean = chance => Math.trunc(Math.random() + chance);

Inne, bardziej powszechne rozwiązania

Bardziej powszechnym sposobem uzyskania losowych wartości boolowskich jest prawdopodobnie podejście porównawcze, takie jak Math.random() >= .5z odpowiedzi Kelvina lub Math.random() < .5;z odpowiedzi Arthura Khazbsa , które w rzeczywistości wyświetlają wartość prawda i fałsz, a nie 1 i 0.

let randomBoolean = Math.random() >= .5;                 //chance of false
const randomBoolean = chance => Math.random() >= chance; //chance of false

let randomBoolean = Math.random()  < .5;                 //chance of true
const randomBoolean = chance => Math.random() < chance;  //chance of true

Jedynym powodem do zastosowania tego Math.round(Math.random())podejścia jest prostota i lenistwo.

LeonNikolai
źródło
2
Nie jestem pewien, czy warto to podkreślić, ale(.49999999999999997 >= .5) != (.49999999999999997+ .5 >> 0)
Aaron Plocharczyk
0

A co z tym?

return Math.round((Math.random() * 1) + 0) === 0;
Alex
źródło
1
OP stwierdza, że ​​używa już podobnych metod, nie ma potrzeby publikowania tego.
Jacob Gunther
-1

Odpowiedź Alexandra O'Mary

po prostu dodając fragment kodu węzła

const crypto = require('crypto');
const randomBool = (function () {
    let a = new Uint8Array(1);
    return function () {
        crypto.randomFillSync(a);
        return a[0] > 127;
    };
})();

let trues = 0;
let falses = 0;
for (let i = 0; i < 100; i++) {
    if (randomBool()) {
        trues++;
    }
    else {
        falses++;
    }
}

console.log('true: ' + trues + ', false: ' + falses);

bFunc
źródło