Каковы шансы того, что Math.random вернет 0? - PullRequest
0 голосов
/ 30 августа 2018

Как и вопросник в этом вопросе , мне было интересно, почему Math.ceil(Math.random() * 10) не было предпочтительнее, чем Math.floor(Math.random() * 10) + 1, и я обнаружил, что это потому, что Math.random имеет крошечный (но значимый) шанс на возвращение 0 точно. Но как крошечный?

Дальнейшие исследования показали мне, что это случайное число с точностью до 16 десятичных знаков ... ну, вроде как. И это "своего рода", что мне интересно.

Я понимаю, что числа с плавающей запятой работают иначе, чем десятичные дроби. Я борюсь со спецификой, хотя. Если бы число было строгим десятичным значением, я полагаю, что шансы были бы один из десяти биллиардов (или десять квадриллионов в американской системе) - 1: 10 16 .

Это правильно, или я все испортил, или с плавающей запятой что-то меняет?

1 Ответ

0 голосов
/ 30 августа 2018

JavaScript - это диалект ECMAScript. Стандарт ECMAScript-262 не может точно указать Math.random. В пункте 20.2.2.7 говорится:

20.2.2.27 Math.random ()

Возвращает числовое значение с положительным знаком, большим или равным 0, но меньшим 1, выбранным случайным или псевдослучайным образом с приблизительно равномерным распределением по этому диапазону, используя алгоритм или стратегию, зависящие от реализации. Эта функция не принимает аргументов. Каждая функция Math.random , созданная для различных областей, должна генерировать определенную последовательность значений из последовательных вызовов.

В отсутствие полной спецификации невозможно сделать однозначное утверждение относительно вероятности того, что Math.random вернет ноль. Каждая реализация ECMAScript может выбирать разные алгоритмы и не должна обеспечивать действительно равномерное распределение.

ECMAScript использует базовый 64-битный двоичный формат IEEE-754 для своего типа Number. В этом формате значимое (дробная часть) числа имеет 53 бита. Каждое число с плавающей точкой имеет вид s f • 2 e , где s (для знака ) равен +1 или -1, f (для дробной части) - значение и целое число в [0, 2 53 ) и e (для экспонента) представляет собой целое число в [−1074, 971]. Число считается нормализованным, если установлен старший бит f (поэтому f находится в [2 52 , 2 53 )). Так как отрицательные числа не имеют значения в этом ответе, пусть s будет неявно +1 для остальной части этого ответа.

Одна проблема с распределением случайных чисел в [0, 1) заключается в том, что представимые значения не расположены равномерно. Существует 2 52 представимых значений в [½, 1) - все они с f в [2 52 , 2 53 ) и e = −53. И в [¼, ½) одинаковое количество значений - все они с f в [2 52 , 2 53 ) и e = −54. Поскольку в этом интервале одинаковое количество чисел, но интервал вдвое меньше, числа расположены ближе друг к другу. Аналогично, в [⅛, ¼) расстояние снова уменьшается вдвое. Это продолжается до тех пор, пока показатель не достигнет -1074, после чего нормальные числа оканчиваются на f = 2 52 . Числа, меньшие, чем это, называются субнормальными (или нулевыми), с f в [0, 2 52 ) и e = -1074, и они равномерно расположены.

Один из способов распределения чисел для Math.random заключается в использовании только набора равномерно распределенных чисел f • 2 −53 для f в [0, 2 53 ). При этом используются все представимые значения в [½, 1), но только половина значений в [¼, ½), четвертая часть значений в [⅛, ¼) и т. Д. Это просто и позволяет избежать некоторых странностей в распределении. При правильной реализации ноль вероятности равен единице в 2 53 .

Другой вариант - использовать все представимые значения в [0, 1), каждое из которых имеет вероятность, пропорциональную расстоянию от него до следующего более высокого представимого значения. Таким образом, каждое представимое число в [½, 1) будет выбрано с вероятностью 1/2 53 , каждое представимое число в [¼, ½) будет выбрано с вероятностью 1/2 54 , каждое представимое число в [⅛, ¼) будет выбрано с вероятностью 1/2 55 и т. д. Это распределение приближается к равномерному распределению по реалам и поставщикам с более высокой точностью, где формат с плавающей точкой является более точным. При правильной реализации ноль вероятности равен единице в 2 1074 .

Другой вариант - использовать все повторы.resentable значения в [0, 1), каждое с вероятностью, пропорциональной длине сегмента, в котором представимое значение является ближайшим представимым значением всех действительных чисел в сегменте. Я опущу обсуждение некоторых деталей этого распределения, за исключением того, что оно имитирует результаты, которые можно получить, выбрав действительное число с равномерным распределением, а затем округлив его до представимого значения с использованием правила округления до ближайших связей к четным. , При правильной реализации ноль вероятности равен единице из 2 1075 . (Одна проблема с этим распределением состоит в том, что равномерное распределение по действительным значениям в [0, 1) иногда дает число, настолько близкое к 1, что при округлении получается 1. Для этого требуется либо, чтобы Math.random было разрешено возвращать 1, либо что Распределение можно обмануть каким-то образом, возможно, возвращая следующее более низкое представимое значение вместо 1.)

Отмечу, что спецификация ECMAScript достаточно слаба, и можно утверждать, что Math.random может распределять числа с равной вероятностью для каждого представимого значения, игнорируя интервал между ними. Это вовсе не имитирует равномерное распределение по реальным числам, и я ожидаю, что очень немногие одобрили бы это. Однако, если реализовано, возвращается ноль вероятности, равный единице в 1021 • 2 52 , потому что есть 2 52 нормализованных чисел с показателями от -53 до -1074 (1020 значений e ) и 2 52 ненормальных или нулевых чисел.

...