Что с 181783497276652981 и 8682522807148012 в Random (Java 7)?

Почему были 181783497276652981 и 8682522807148012 выбраны в Random.java?

Здесь соответствующий исходный код от Java SE JDK 1.7:

/**
 * Creates a new random number generator. This constructor sets
 * the seed of the random number generator to a value very likely
 * to be distinct from any other invocation of this constructor.
 */
public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}

private static long seedUniquifier() {
    // L'Ecuyer, "Tables of Linear Congruential Generators of
    // Different Sizes and Good Lattice Structure", 1999
    for (;;) {
        long current = seedUniquifier.get();
        long next = current * 181783497276652981L;
        if (seedUniquifier.compareAndSet(current, next))
            return next;
    }
}

private static final AtomicLong seedUniquifier
    = new AtomicLong(8682522807148012L);

Таким образом, вызывая new Random() без какого-либо параметра семени, берет текущий "семенный уникальный идентификатор" и XOR с помощью System.nanoTime(). Затем он использует 181783497276652981 для создания другого семенного уникального идентификатора, который будет сохранен в следующий раз new Random().

Литералы 181783497276652981L и 8682522807148012L не помещаются в константы, но они больше нигде не появляются.

Сначала комментарий дает мне легкое преимущество. Поиск в Интернете по этой статье дает фактическую статью. 8682522807148012 не отображается в документе, но 181783497276652981 появляется - как подстрока другого числа, 1181783497276652981, который 181783497276652981 с 1 добавленным.

В документе утверждается, что 1181783497276652981 - это число, которое дает хорошую "заслугу" для линейного конгруэнтного генератора. Было ли это число просто неправильно скопировано в Java? Имеет ли 181783497276652981 приемлемое достоинство?

И почему выбрали 8682522807148012?

Поиск в Интернете по любому номеру не дает никаких объяснений, только эта страница, которая также отмечает отброшенный 1 перед 181783497276652981.

Были ли выбраны другие числа, которые бы работали так же хорошо, как эти два числа? Почему или почему нет?

Ответ 1

  • Было ли это число просто неправильно скопировано в Java?

    Да, похоже, опечатка.

  • Есть ли у 181783497276652981 приемлемые достоинства?

    Это можно определить, используя алгоритм оценки, представленный в статье. Но достоинство "оригинального" числа, вероятно, выше.

  • И почему выбрали 8682522807148012?

    Кажется случайным. Это может быть результат System.nanoTime(), когда код был написан.

  • Были ли выбраны другие числа, которые бы работали так же хорошо, как эти два числа?

    Не каждое число будет одинаково "хорошим". Итак, нет.

Стратегии седиментации

Существуют различия в схеме посева по умолчанию между различными версиями и реализацией JRE.

 
public Random() { this(System.currentTimeMillis()); }
 
public Random() { this(++seedUniquifier + System.nanoTime()); }
 
public Random() { this(seedUniquifier() ^ System.nanoTime()); }

Первый неприемлем, если вы создаете несколько RNG в строке. Если их время создания падает в том же миллисекундном диапазоне, они будут давать полностью идентичные последовательности. (то же самое семя = > такая же последовательность)

Второй вариант не является потокобезопасным. При инициализации одновременно с несколькими потоками могут возникать идентичные RNG. Кроме того, семена последующих инициализаций, как правило, коррелируют. В зависимости от фактического разрешения по времени в системе последовательность семян может быть линейно возрастающей (n, n + 1, n + 2,...). Как указано в Насколько отличаются случайные семена? и ссылочная статья Общие дефекты при инициализации генераторов псевдослучайных чисел, коррелированные семена могут генерировать корреляцию между фактическими последовательностями нескольких RNG.

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

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

может быть расширен "через потоки" и "некоррелированный"

Качество последовательности семян

Но случайность последовательности посева только так хороша, как основной RNG. RNG, используемый для последовательности семян в этой реализации Java, использует мультипликативный линейный конгруэнтный генератор (MLCG) с c = 0 и m = 2 ^ 64. (Модуль 2 ^ 64 неявно задается переполнением целых 64-битных длин) Из-за нуля c и модуля мощности-2, "качество" (длина цикла, бит-корреляция,...) ограничено. Как отмечается в документе, помимо общей длины цикла каждый отдельный бит имеет собственную длину цикла, которая экспоненциально уменьшается для менее значимых бит. Таким образом, нижние биты имеют меньший шаблон повторения. (Результат seedUniquifier() должен быть восстановлен по битам, прежде чем он будет усечен до 48 бит в фактическом RNG)

Но это быстро! Чтобы избежать ненужных циклов сравнения и установки, тело цикла должно быть быстрым. Это, вероятно, объясняет использование этого конкретного MLCG без добавления без xoring всего лишь одного умножения.

И в упомянутой статье представлен список хороших "множителей" для c = 0 и m = 2 ^ 64, как 1181783497276652981.

В целом: A для усилий @JRE-developers;) Но есть опечатка. (Но кто знает, если кто-то не оценит это, есть вероятность того, что недостающий ведущий 1 действительно улучшит сеялку RNG.)

Но некоторые множители определенно хуже: "1" приводит к постоянной последовательности. "2" приводит к однобитовой последовательности (некоторая корреляция) ...

Интерпоследовательная корреляция для RNG действительно актуальна для (Монте-Карло) симуляций, где множественные случайные последовательности создаются и даже распараллеливаются. Таким образом, необходима хорошая стратегия посева, чтобы получить "независимые" симуляции. Поэтому в стандарте С++ 11 вводится понятие Seed Sequence для генерации некоррелированных семян.

Ответ 2

Если вы считаете, что уравнение, используемое для генератора случайных чисел, равно:

LCGEquation

Где X (n + 1) - следующее число, a - мультипликатор, X (n) - это текущее число, c - приращение, а m - модуль.

Если вы посмотрите дальше на Random, a, c и m определены в заголовке класса

private static final long multiplier = 0x5DEECE66DL;   //= 25214903917 -- 'a'
private static final long addend = 0xBL;               //= 11          -- 'c'
private static final long mask = (1L << 48) - 1;       //= 2 ^ 48 - 1  -- 'm'

и глядя на метод protected int next(int bits), это было реализовано уравнение

nextseed = (oldseed * multiplier + addend) & mask;
//X(n+1) =  (X(n)   *      a     +    c  ) mod m

Это означает, что метод seedUniquifier() фактически получает X (n) или в первом случае при инициализации X (0), который фактически является 8682522807148012 * 181783497276652981, это значение затем дополнительно изменяется значением System.nanoTime(). Этот алгоритм согласуется с приведенным выше уравнением, но со следующим X (0) = 8682522807148012, a = 181783497276652981, m = 2 ^ 64 и c = 0. Но так как mod m of preformed длинным переполнением, выше уравнение просто становится

eq2

Рассматривая статью, значение a = 1181783497276652981 для m = 2 ^ 64, c = 0. Таким образом, это кажется просто опечаткой и значением 8682522807148012 для X (0), который представляется кажущимся случайно выбранным числом из устаревшего кода для Random. Как видно здесь. Но достоинство этих выбранных чисел все равно может быть действительным, но, как упомянуто Томасом Б., вероятно, не так "хорошо", как один в документе.

РЕДАКТИРОВАТЬ - Ниже оригинальные мысли с тех пор были выяснены, поэтому можно пренебречь, но оставить его для справки

Это приводит меня к выводам:

  • Ссылка на бумагу не для самого значения, а для методов, используемых для получения значений из-за разных значений a, c и m

  • Это просто совпадение, что значение в остальном не отличается от первого 1, а комментарий неуместен (все еще пытается поверить в это).

ИЛИ

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

Итак, чтобы ответить на ваш вопрос

Были ли выбраны другие числа, которые бы работали так же хорошо, как эти два числа? Почему или почему нет?

Да, любое число могло бы использоваться, фактически, если вы укажете начальное значение, когда вы создаете Random, вы используете любое другое значение. Это значение не влияет на производительность генератора, это определяется значениями a, c и m, которые жестко закодированы внутри класса.

Ответ 3

В соответствии с предоставленной вами ссылкой они выбрали (после добавления отсутствующего 1:)) лучший результат от 2 ^ 64, потому что долго не может иметь число от 2 ^ 128