Напишите действительно инклюзивный случайный метод для JavaScript - PullRequest
13 голосов
/ 27 января 2010

Объект Javascript MATH имеет случайный метод, который возвращает из набора [0,1) 0 включительно, 1 эксклюзивно. Есть ли способ вернуть действительно случайный метод, который включает в себя 1.

, например

var rand = MATH.random()*2;

if(rand > 1)
{
   rand = MATH.floor(rand);
}

return rand; 

Хотя это всегда возвращает число из набора [0,1], оно не является действительно случайным.

Ответы [ 7 ]

13 голосов
/ 27 января 2010

Это вернет [0,1] включительно:

if(MATH.random() == 0)
    return 1;
else
    return MATH.random();

Объяснение: Если первый вызов random () вернет 0, верните 1. В противном случае снова вызовите random, который будет равен [0,1). Следовательно, он вернет [0,1] все включено.

8 голосов
/ 27 января 2010

Не имеет значения, включает ли он границы; на самом деле, строго говоря, то, что вы пытаетесь сделать, на самом деле не имеет смысла. Помните, что при непрерывном распределении вероятностей вероятность получения определенного значения в любом случае бесконечно мала, поэтому с математической точки зрения вы никогда не увидите точное значение 1.

Конечно, в мире компьютеров распределение ГСЧ не является действительно непрерывным, поэтому «возможно», что вы столкнетесь с определенной ценностью (что бы это ни значило), но с тем фактом, что вы полагаетесь на ограничении того, как хранятся действительные числа, намекает на проблемы с вашим подходом к любой проблеме, которую вы пытаетесь решить.

6 голосов
/ 27 января 2010

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

Чтобы указать включающий верхний предел, вы просто добавляете его, чтобы сделать его исключительным при расчете. Это будет правильно распределять случайные числа между 7 и 12 включительно:

var min = 7;
var max = 12;
var rnd = min + Math.floor(Math.random() * (max - min + 1));
3 голосов
/ 27 января 2010

Вы хотите включить 1?

return 1 - Math.random();

Однако я думаю, что это один из тех вопросов, который намекает на другие проблемы. Почему вам нужно включить 1? Вероятно, есть лучший способ сделать это.

2 голосов
/ 11 мая 2015

Из того, что я вижу из консоли JavaScript в Chrome, Math.random() генерирует число от 0 до 0.9999999999999999. Принимая это во внимание, вы можете получить то, что вы хотите, добавив модификатор. Например, вот функция, которая даст вам квазислучайное число с плавающей точкой от 0 до 1, где 1 включительно:

function randFloat() {
  // Assume random() returns 0 up to 0.9999999999999999
  return Math.random()*(1+2.5e-16);
}

Вы можете попробовать это в консоли, введя 0.9999999999999999*(1+2.5e-16) - он вернет ровно 1. Вы можете пойти дальше и вернуть значение с плавающей точкой от 0 до 1024 (включительно) с помощью этой функции:

function randFloat(nMax) {
  // Assume random() returns 0 up to 0.9999999999999999
  // nMax should be a float in the range 1-1024
  var nMod;
  if (nMax<4) nMod = 2.5e-16;
  else if (nMax<16) nMod = 1e-15;
  else if (nMax<32) nMod = 3.5e-15;
  else if (nMax<128) nMod = 1e-14;
  else if (nMax<512) nMod = 3.5e-14;
  else if (nMax<1024) nMod = 1e-13;
  return Math.random()*(nMax+nMod);
}

Возможно, где-то есть более эффективный алгоритм.

1 голос
/ 13 января 2017

Так как этот вопрос был задан снова, и я не читал этот подход здесь, я добавлю другой ответ.

ИМО лучшее, что вы можете сделать, без особых хлопот будет:

эксклюзивный:

//simply ignore the 0
for(var rand=0; !rand;rand = Math.random());

//or simpler:
var rand = Math.random() || Math.random();
//because the probability for two consecutive `0` is pretty much non existant.

это даже не приводит к ошибке, поскольку мы просто исключили возможность возврата 0, каждое другое значение между 0 и 1 имеет такую ​​же вероятность

включая:

var rand = Math.random() * 2;
if(rand > 1) rand-=1;
//so the 0 shares it's probability with the 1.

просто чтобы понять, насколько крошечная «ошибка»:

  • вероятность для 0 или 1 равна
    1 / Math.pow(2, 54) или около 5.55e-17
  • вероятность любого другого значения между 0 и 1 равна
    1 / Math.pow(2, 53) или около 11.1e-17

и вся случайная функция будет:

function random(min, max, inclusive){
    var r = Math.random();
    if(inclusive)
        r = r>0.5? 2*r-1: 2*r;
    else 
        while(!r) r = Math.random();

    return r * (max - min) + min;
}

Редактировать: Я не уверен, совершил ли я ошибку, но не следует ли фиксировать вероятность на инклюзивном подходе, если я добавлю еще один бит к нулям и единицам и, следовательно, дублирую вероятность:

var rand = Math.random() * 4;
rand = (rand % 1) || (rand & 1);
0 голосов
/ 23 января 2014

Приложение:

Взгляните на исходный код java.util.Random, включенный в дистрибутив Oracle JDK 7 ( "Copyright (c) 1995, 2010, Oracle и / или его филиалы. Все права защищены. ORACLE PROPRIETARY / CONFIDENTIAL. Использование в соответствии с условиями лицензии ") показывает этот простой код:

class Random {

    public float nextFloat() {
        return next(24) / ((float)(1 << 24));
    }

    protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }
}

Таким образом, для nextFloat():

  1. Возьмите "случайное целочисленное значение" между 0 и 2 ^ 24-1 (или, скорее, случайный 24-битный битовый шаблон, интерпретируемый как целочисленное значение),
  2. Преобразовать его в float (в Java «float» должен быть 32-битной плавающей точкой IEEE 724, которая может представлять до 2 ^ 24 без потери точности, и, таким образом, это будет значение между 0 и 1.6777215E7)
  3. Затем разделите его на представление с плавающей точкой 2 ^ 24, снова просто представимое без потери точности как 1.6777216E7. 2 ^ 24 + 1 = 16777217 упадет до 1.6777216E7, когда будет вынужден быть плавающим. В коде это действительно должно быть константой. Эй, Солнце, циклы не растут на деревьях !!
  4. Результатом деления является плавающее число в [0.0 .. 0.99999994] (правильный результат деления будет около 0.999999940395355224609375) со всеми, как мне кажется, всеми возможными значениями с плавающей запятой IEEE 724 между одинаково возможными ».

См. Также IEEE с плавающей точкой и Арифметика с плавающей точкой на JVM .

Комментарии Javadoc для `next ():

<code>/**
 * Generates the next pseudorandom number. Subclasses should
 * override this, as this is used by all other methods.
 *
 * <p>The general contract of {@code next} is that it returns an
 * {@code int} value and if the argument {@code bits} is between
 * {@code 1} and {@code 32} (inclusive), then that many low-order
 * bits of the returned value will be (approximately) independently
 * chosen bit values, each of which is (approximately) equally
 * likely to be {@code 0} or {@code 1}. The method {@code next} is
 * implemented by class {@code Random} by atomically updating the seed to
 *  <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}
* и возвращение *
{@code (int)(seed >>> (48 - bits))}.
* * Это линейный конгруэнтный генератор псевдослучайных чисел, так как * определено Д. Х. Лемером и описано Дональдом Кнутом в * Искусство компьютерного программирования, Том 3: * Получисленные алгоритмы , раздел 3.2.1. * * @парам биты случайные биты * @ вернуть следующее псевдослучайное значение из этого случайного числа * последовательность генератора * @ с 1.1 * /

Комментарии Javadoc для nextFloat():

<code>/**
 * Returns the next pseudorandom, uniformly distributed {@code float}
 * value between {@code 0.0} and {@code 1.0} from this random
 * number generator's sequence.
 *
 * <p>The general contract of {@code nextFloat} is that one
 * {@code float} value, chosen (approximately) uniformly from the
 * range {@code 0.0f} (inclusive) to {@code 1.0f} (exclusive), is
 * pseudorandomly generated and returned. All 2<font
 * size="-1"><sup>24</sup></font> possible {@code float} values
 * of the form <i>m&nbsp;x&nbsp</i>2<font
 * size="-1"><sup>-24</sup></font>, where <i>m</i> is a positive
 * integer less than 2<font size="-1"><sup>24</sup> </font>, are
 * produced with (approximately) equal probability.
 *
 * <p>The method {@code nextFloat} is implemented by class {@code Random}
 * as if by:
 *  <pre> {@code
 * public float nextFloat() {
 *   return next(24) / ((float)(1 << 24));
 * }}
* *

Хеджирование "приблизительно" используется только в предыдущем описании. * потому что следующий метод является лишь приблизительно непредвзятым источником * независимо выбранные биты. Если бы это был идеальный источник случайно * выбранные биты, тогда показанный алгоритм выберет {@code float} * значения из указанного диапазона с идеальной однородностью.

* [В ранних версиях Java результат был неверно рассчитан как: *

{@code
 *   return next(30) / ((float)(1 << 30));}
* Это может показаться эквивалентным, если не лучше, но на самом деле это * введена небольшая неравномерность из-за смещения в округлении * чисел с плавающей точкой: было немного более вероятно, что * младший бит значения и будет 0, чем то, что будет 1.] * * @ возврат следующего псевдослучайного, равномерно распределенного {@code float} * значение между {@code 0.0} и {@code 1.0} из этого * последовательность генератора случайных чисел * /
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...