Почему были 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
.
Могли ли быть выбраны другие числа, которые работали бы так же хорошо, как эти два числа? Почему или почему нет?
8682522807148012
является наследием предыдущей версии класса, как видно из изменений, внесенных в 2010 году .181783497276652981L
, Кажется, опечатка , действительно , и вы можете подать отчет об ошибке.seedUniquifier
на 64-ядерном ящике может быть очень много. Локальный поток был бы более масштабируемым.Ответы:
Да вроде опечатка.
Это можно определить с помощью алгоритма оценки, представленного в документе. Но заслуга «оригинального» номера, наверное, выше.
Кажется случайным. Это могло быть результатом System.nanoTime () при написании кода.
Не все числа будут одинаково "хорошими". Так что нет.
Стратегии посева
Существуют различия в схеме заполнения по умолчанию между разными версиями и реализацией JRE.
Первый неприемлем, если вы создаете несколько ГСЧ подряд. Если время их создания попадает в один и тот же диапазон миллисекунд, они будут давать полностью идентичные последовательности. (то же семя => та же последовательность)
Второй не является потокобезопасным. Несколько потоков могут получить одинаковые RNG при инициализации одновременно. Кроме того, начальные значения последующих инициализаций обычно коррелируют. В зависимости от фактического разрешения таймера системы, начальная последовательность может линейно увеличиваться (n, n + 1, n + 2, ...). Как указано в разделе «Насколько разными должны быть случайные семена?» и упомянутый документ Общие дефекты при инициализации генераторов псевдослучайных чисел , коррелированные начальные числа могут генерировать корреляцию между фактическими последовательностями множества ГСЧ.
Третий подход создает случайно распределенные и, следовательно, некоррелированные начальные числа, даже между потоками и последующими инициализациями. Итак, текущие java-документы:
может быть расширен «по потокам» и «некоррелирован»
Качество посевной последовательности
Но случайность последовательности раздачи настолько хороша, насколько хорош основной ГСЧ. ГСЧ, используемый для начальной последовательности в этой реализации Java, использует мультипликативный линейный конгруэнтный генератор (MLCG) с c = 0 и m = 2 ^ 64. (Модуль 2 ^ 64 неявно задается переполнением 64-битных длинных целых чисел) Из-за нулевого значения c и модуля степени 2 «качество» (длина цикла, битовая корреляция, ...) ограничено . Как говорится в документе, помимо общей длины цикла, каждый бит имеет собственную длину цикла, которая экспоненциально уменьшается для менее значимых битов. Таким образом, младшие биты имеют меньший шаблон повторения. (Результат seedUniquifier () должен быть перевернут, прежде чем он будет усечен до 48 бит в фактическом ГСЧ)
Но это быстро! И чтобы избежать ненужных циклов сравнения и установки, тело цикла должно быть быстрым. Это, вероятно, объясняет использование этого конкретного MLCG без сложения, без xoring, только с одним умножением.
И в упомянутой статье представлен список хороших «множителей» для c = 0 и m = 2 ^ 64, как 1181783497276652981.
В общем: А за старания @ JRE-developers;) Но есть опечатка. (Но кто знает, если кто-то не оценит это, есть вероятность, что отсутствующая ведущая 1 действительно улучшает начальный ГСЧ.)
Но некоторые множители определенно хуже: «1» ведет к постоянной последовательности. «2» приводит к однобитовой последовательности (как-то коррелированной) ...
Корреляция между последовательностями для ГСЧ действительно актуальна для моделирования (Монте-Карло), когда несколько случайных последовательностей создаются и даже распараллеливаются. Таким образом, для получения «независимых» прогонов моделирования необходима хорошая стратегия посева. Поэтому стандарт C ++ 11 вводит концепцию Seed Sequence для генерации некоррелированных начальных чисел.
источник
seedUniquifier
не застрянет на нуле.Если учесть, что уравнение, используемое для генератора случайных чисел, выглядит следующим образом:
Где X (n + 1) - следующее число, a - множитель, X (n) - текущее число, c - приращение, а m - модуль.
Если вы посмотрите дальше
Random
, a, c и m определены в заголовке классаи глядя на метод
protected int next(int bits)
, в котором реализовано уравнениеЭто означает, что метод
seedUniquifier()
фактически получает X (n) или, в первом случае, при инициализации X (0), что на самом деле8682522807148012 * 181783497276652981
, это значение затем дополнительно изменяется на значениеSystem.nanoTime()
. Этот алгоритм согласуется с приведенным выше уравнением, но со следующими X (0) =8682522807148012
, a =181783497276652981
, m = 2 ^ 64 и c = 0. Но поскольку модуль m of формируется длинным переполнением, приведенное выше уравнение просто становитсяГлядя на бумагу , значение a =
1181783497276652981
для m = 2 ^ 64, c = 0. Таким образом, похоже, что это просто опечатка, а значение8682522807148012
для X (0), которое кажется случайно выбранным числом из устаревшего кода. дляRandom
. Как видно здесь. Но достоинства этих выбранных чисел все еще могут быть в силе, но, как было сказано Томасом Б., вероятно, не столь "хороши", как в статье.РЕДАКТИРОВАТЬ - Ниже исходные мысли были прояснены, поэтому их можно игнорировать, но оставив для справки.
Это подводит меня к выводам:
Ссылка на документ касается не самого значения, а методов, используемых для получения значений из-за различных значений a, c и m.
Это простое совпадение, что значение в остальном то же самое, кроме ведущей единицы, и комментарий неуместен (хотя все еще изо всех сил пытается в это поверить)
ИЛИ
Произошло серьезное недопонимание таблиц в документе, и разработчики просто выбрали значение случайным образом, поскольку к тому времени, когда оно умножается, какой смысл в использовании значения таблицы в первую очередь, тем более что вы можете просто предоставить свое собственное начальное значение в любом случае, и в этом случае эти значения даже не принимаются во внимание
Итак, чтобы ответить на ваш вопрос
Да, можно было использовать любое число, на самом деле, если вы указываете начальное значение при создании экземпляра Random, вы используете любое другое значение. Это значение не влияет на производительность генератора, это определяется значениями a, c и m, которые жестко запрограммированы в классе.
источник
Random
и цитируемой статьей, что я полностью пропустил исходный вопрос, скоро отредактирую, спасибо.Согласно предоставленной вами ссылке, они выбрали ( после добавления недостающего 1 :) ) лучший выход из 2 ^ 64, потому что долго не может иметь число от 2 ^ 128
источник