Що з 181783497276652981 та 8682522807148012 у Random (Java 7)?


112

Чому їх 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.

Чи могли бути обрані інші числа, які працювали б так само, як і ці два числа? Чому чи чому б ні?


Я хотів би лише зазначити, що жодна з згаданих констант (навіть більші з попередніми на початку) не надто велика, щоб підходити, хоча множення напевно призведе до переповнення.
nanofarad

6
8682522807148012є спадщиною попередньої версії класу, як це можна побачити в редакціях, зроблених у 2010 році . 181783497276652981L, Здається, помилка , дійсно , і ви можете подати звіт про помилку.
assylias

6
Або це помилка, тобто помилка, або функція з нерозкритою мотивацією. Вам доведеться запитати авторів. Все, що ви отримаєте тут, буде лише більш-менш необізнаною думкою. Якщо ви думаєте, що це помилка, надішліть звіт про помилку.
Маркіз Лорн

1
Особливо з огляду на різні відповіді, це можуть бути два окремих питання для кожної постійної.
Марк Херд

1
Сумно бачити вузьке вузьке вузьке місце, що вбудовується в такий фундаментальний клас. seedUniquifierможе стати надзвичайно важким для 64-ядерної коробки. Місцева нитка була б більш масштабованою.
usr

Відповіді:


57
  1. Чи було це число просто неправильно скопійовано на Java?

    Так, здається, помилка друку.

  2. Чи 181783497276652981 має прийнятну заслугу?

    Це можна визначити, використовуючи алгоритм оцінювання, представлений у статті. Але достоїнство "оригінального" числа, ймовірно, вище.

  3. І чому було обрано 8682522807148012?

    Здається, випадково. Це може бути результатом System.nanoTime (), коли був написаний код.

  4. Чи могли бути обрані інші числа, які працювали б так само, як і ці два числа?

    Не кожне число було б однаково «добре». Отже, ні.

Стратегії висіву

Існують відмінності в схемі висіву за замовчуванням між різними версіями та реалізацією 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-розробники;) Але є помилка друку. (Але хто знає, якщо хтось не оцінить це, існує ймовірність того, що відсутній ведучий 1 насправді покращує посівний РНГ.)

Але деякі множники, безумовно, гірші: "1" призводить до постійної послідовності. "2" призводить до послідовності однобітної переміщення (якось співвіднесеної) ...

Кореляція між послідовностями для RNG насправді актуальна для моделювання (Монте-Карло), де кілька випадкових послідовностей інстанціюються і навіть паралелізуються. Таким чином, необхідна хороша стратегія висіву для отримання «незалежних» моделей. Тому стандарт C ++ 11 запроваджує концепцію послідовності насіння для отримання некорльованих насіння.


3
Принаймні, це все-таки дивно, якби вони скинули найменш значущий замість найзначнішого, то кожне множення трохи втрачає, доки в кінцевому підсумку (після 62 кроків) seedUniquifierне застряє нуль.
Гарольд

9

Якщо ви вважаєте, що рівняння, яке використовується для генератора випадкових чисел, є:

LCGEзапитання

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

Якщо ви заглянете далі в Randoma, 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. Але так як мод m формується довгим переливом, вищевказане рівняння просто стає

eq2

Дивлячись на папір , значення a = 1181783497276652981є для m = 2 ^ 64, c = 0. Отже, це здається, що це просто помилка друку, а значення 8682522807148012для X (0), яке, здається, є випадковим чином обраним числом зі застарілого коду для Random. Як видно тут. Але заслуга цих обраних номерів все-таки може бути дійсною, але, як згадував Томас Б., мабуть, не така "хороша", як та, яка є в статті.

РЕДАКТУВАННЯ - Нижче оригінальні думки були уточнені, тому їх можна ігнорувати, але залишати їх для довідок

Це підводить мене до висновків:

  1. Посилання на документ не для самого значення, а для методів, що використовуються для отримання значень через різні значення a, c і m

  2. Це просто збіг обставин, що значення в іншому випадку відрізняється від провідного 1, а коментар невідомий (все ще намагаюся повірити в це)

АБО

У документі сталося серйозне непорозуміння таблиць, і розробники просто вибрали значення навмання, оскільки до моменту його помноження в чому полягає сенс у використанні значень таблиці в першу чергу, тим більше що ви можете просто надати власне значення насіння будь-яким способом; у цьому випадку ці значення навіть не враховуються

Отже, щоб відповісти на ваше запитання

Чи могли бути обрані інші числа, які працювали б так само, як і ці два числа? Чому чи чому б ні?

Так, будь-яке число могло бути використане, адже якщо ви вказали значення насіння під час Instantiate Random, ви використовуєте будь-яке інше значення. Це значення не впливає на продуктивність генератора, це визначається значеннями a, c і m, які жорстко закодовані в межах класу.


1
Насправді - Існує два алгоритми: (i) 1 для створення нового випадкового насіння щоразу, коли конструктор викликається. Цей алго використовує простий X_n + 1 = X_n * a. Через тривалий перелив це еквівалентно X_n + 1 = X_n * a mod m. З a = 181783497276652981 і m = 2 ^ 64. (ii) Інший альго, який, починаючи з даного насіння, утворює ряд випадкових чисел. Це друге альго - це те, що ви згадуєте, і документи пояснюють, що " Це лінійний конгрурентний псевдовипадковий генератор чисел, як описано Кнутом у" Мистецтві комп'ютерного програмування ".
assylias

1
@assylias Я бачу вашу думку, так захопився вихідним кодом Randomта цитованим документом, що я повністю переглянув оригінальне запитання, незабаром відредагую, дякую.
Java Devil

3

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.