Насколько случайным является Math.random в JavaScript?

117

Уже 6 лет у меня на сайте есть страница с генератором случайных чисел . Долгое время это был первый или второй результат в Google для "генератора случайных чисел", и он использовался для принятия решений в десятках, если не сотнях конкурсов и рисунков на дискуссионных форумах и блогах (я знаю, потому что вижу рефереров в своих веб-журналы и обычно идут смотреть).

Сегодня кто-то написал мне, что это может быть не так случайно, как я думал. Она попыталась сгенерировать очень большие случайные числа (например, от 1 до 10000000000000000000) и обнаружила, что они почти всегда имеют одинаковое количество цифр. На самом деле, я заключил функцию в цикл, чтобы я мог генерировать тысячи чисел, и, конечно же, для очень больших чисел вариация составляла всего около 2 порядков.

Зачем?

Вот версия с циклом, поэтому вы можете попробовать ее сами:

http://andrew.hedges.name/experiments/random/randomness.html

Он включает в себя как простую реализацию, взятую из сети разработчиков Mozilla, так и некоторый код 1997 года, который я удалил с веб-страницы, которая больше не существует ("Central Randomizer 1.3" Пола Хоула). Просмотрите исходный код, чтобы узнать, как работает каждый метод.

Я читал здесь и в других местах о Mersenne Twister. Меня интересует, почему не было бы большего разброса в результатах встроенной функции JavaScript Math.random . Спасибо!

Эндрю Хеджес
источник
"sarnath'd" как in, beat to the punch, или, в данном случае, ответ
maetl
5
Если вы ищете ответ на вопрос в заголовке, см. Stackoverflow.com/questions/2344312/…
Эндрю Б.

Ответы:

182

Даны числа от 1 до 100.

  • 9 имеют 1 цифру (1-9)
  • 90 имеют 2 цифры (10-99)
  • 1 состоит из 3 цифр (100)

Даны числа от 1 до 1000.

  • 9 имеют 1 цифру
  • 90 имеют 2 цифры
  • 900 состоит из 3 цифр
  • 1 состоит из 4 цифр

и так далее.

Таким образом, если вы выберете несколько случайных чисел, то подавляющее большинство выбранных чисел будет иметь одинаковое количество цифр, поскольку подавляющее большинство возможных значений имеют одинаковое количество цифр.

Quentin
источник
11
Ваше представление об идеальном и равномерном распределении случайности интригует ...
19
@ R.Pate - генерация случайных чисел бесполезна, если она не распределяется равномерно в большом масштабе
annakata
3
Прочти снова. @David только указывает, какие числа находятся между пределами, а не результат выбора N случайных чисел. Я признаю, что название вводит в заблуждение.
nikc.org
3
Для протокола, я проголосовал как за этот, так и за ответы @jwoolard. Я выбрал этот вариант в качестве принятого ответа, потому что примеры ясно показывают, почему распределение чисел смещено в сторону чисел с большим количеством цифр.
Эндрю Хеджес,
1
@ andrew-hedges совершенно верно - это более четкий ответ, но спасибо :)
jwoolard 01
56

Ваши результаты действительно ожидаются. Если случайные числа равномерно распределены в диапазоне от 1 до 10 ^ n, то можно ожидать, что примерно 9/10 чисел будут иметь n цифр, а еще 9/100 будут иметь n-1 цифры.

jwoolard
источник
8
Именно. Ожидается, что распределение количества цифр будет искажено. Однако распределение журнала числа цифр должно быть равномерным.
Noldorin
45

Есть разные виды случайности. Math.random дает равномерное распределение чисел.

Если вам нужны разные порядки величин, я бы предложил использовать экспоненциальную функцию для создания так называемого степенного распределения :

function random_powerlaw(mini, maxi) {
    return Math.ceil(Math.exp(Math.random()*(Math.log(maxi)-Math.log(mini)))*mini)
}

Эта функция должна дать вам примерно такое же количество однозначных чисел, как двузначные числа и как трехзначные числа.

Существуют также другие распределения для случайных чисел, такие как нормальное распределение (также называемое распределением Гаусса).

Кристиан
источник
С помощью этого алгоритма, я поставил minimum = 1и maximum = 10и иногда получаю 11 в результате. Вы, вероятно, хотели использовать Math.floorвместоMath.round
Сэм Итон
1
Почему это работает? Преобразует ли равномерное распределение в экспоненциальное?
shinzou
@shinzou Я спросил на math.stackexchange и получил немного другую формулу в качестве ответа. Я изменил код, чтобы отразить математически полученную формулу из math.stackexchange.
Christian
21

Для меня это выглядит совершенно случайно! (Подсказка: это зависит от браузера.)

Лично я думаю, что моя реализация была бы лучше, хотя я украл ее у XKCD , которого ВСЕГДА следует признать:

function random() {
  return 4; // Chosen by a fair dice throw. Guaranteed to be random.
}
Арафангион
источник
19
+1 за упоминание о зависимости от браузера, -1 за заимствование xkcd без ссылки.
Обязательно или нет, так как это xkcd, он приписывается. :)
Арафангион
2
ОТ: Я удивлен и счастлив, что «XKCD» стал ответом на вопрос University Challenge на этой неделе: D
Мэтт Сак,
2
Берги: Прямой ссылки недостаточно?
Arafangion
Я думаю, они имеют в виду, что шутка была процитирована неправильно («random = 4;» вместо «return 4;»)
Эрен Тантекин 07
18

В следующем документе объясняется, насколько math.random () в основных веб-браузерах является (небезопасным): «Временное отслеживание пользователей в основных браузерах и междоменная утечка информации и атаки» Амида Кляйна (2008) . Он не сильнее типичных встроенных функций ГПСЧ в Java или Windows.

С другой стороны, реализация SFMT периода 2 ^ 19937-1 требует 2496 байтов внутреннего состояния, поддерживаемого для каждой последовательности PRNG. Некоторым это может показаться непростительной ценой.

jj1bdx
источник
1
+1: Упомянутая статья великолепна, далеко за пределами того, о чем был первоначальный вопрос.
Роланд Иллиг
6

Если вы используете число вроде 10000000000000000000, вы выходите за пределы точности типа данных, который использует Javascript. Обратите внимание, что все генерируемые числа заканчиваются на «00».

Greg
источник
1
Однако в данном случае это не его проблема.
Joey
3
@Johannes - это одна из его проблем :)
annakata
Распределение IEE754 не четное. Возможно, вы можете представить от 0 до 999 с шагом в два и иметь для этого достаточную точность, чтобы вы заметили равномерное распределение в этом диапазоне, если вы выберете число много раз. 10% будут двузначными, а 90% - трехзначными. Однако, когда вы начнете набирать действительно большие числа, приращение превысит 1. Возможно, вы сможете перейти только от триллиона миллиардов к триллиону миллиардов тысячу, а не триллион миллиардов и один. Хотя для небольших чисел / масштабов этот эффект будет незначительным или отсутствующим. Однако эффект масштаба будет иметь гораздо большее влияние.
jgmjgm
5

Я попробовал генератор псевдослучайных чисел JS в Chaos Game .

Мой треугольник Серпинского говорит, что он довольно случайный: Фрактал

zie1ony
источник
2
Не могли бы вы поделиться здесь кодом треугольника и jsfiddle / jsbin, чтобы мы могли легко проверить его на практике для разных браузеров?
Fabrício Matté 08
1
Хорошо, но дайте мне несколько дней, потому что мне нужно перевести код на английский. Теперь он польско-английский, и у меня много работы.
zie1ony
1
@ zie1ony истекла пара дней.
trusktr
1
usp :( работа, работа, работа Ссылка: kubaplas.vot.pl/green/fractal Первый параметр - это номер вершины. Второй - точка пересечения (от 0 до 1) отрезка прямой. Просто поэкспериментируйте.
zie1ony
4
Ссылка мертва - может быть, вместо этого репо на Github?
Марк К. Коуэн,
3

Что ж, если вы генерируете числа, скажем, до 1e6, вы, надеюсь, получите все числа примерно с равной вероятностью. Это также означает, что у вас есть только один шанс из десяти получить число с одной цифрой меньше. Один из ста шанс получить на две цифры меньше и т. Д. Я сомневаюсь, что вы заметите большую разницу при использовании другого ГСЧ, потому что у вас есть равномерное распределение по числам, а не их логарифм.

детеныш
источник
0

Неслучайные числа, равномерно распределенные от 1 до N, обладают тем же свойством. Обратите внимание, что (в некотором смысле) это вопрос точности. При равномерном распределении чисел от 0 до 99 (в виде целых чисел) 90% номеров имеют две цифры. В равномерном распределении на 0-999999 905 пятизначных номеров.

Любой набор чисел (при некоторых не слишком ограничительных условиях) имеет плотность. Когда кто-то хочет обсудить «случайные» числа, следует указать плотность этих чисел (как отмечалось выше). Обычная плотность - это однородная плотность. Есть и другие: экспоненциальная плотность, нормальная плотность и т. Д. Прежде чем предлагать генератор случайных чисел, нужно выбрать, какая плотность важна. Кроме того, числа, поступающие из одной плотности, часто могут быть легко преобразованы в другую плотность кариозными способами.

TTW
источник