Насколько это возможно, начальное число для ГСЧ само по себе должно быть случайным. Семена, которые вы используете, будут отличаться только одним или двумя битами.
Очень редко бывает веская причина для создания двух отдельных ГСЧ в одной программе. Ваш код не относится к тем ситуациям, когда он имеет смысл.
Просто создайте один ГСЧ и используйте его повторно, тогда у вас не будет этой проблемы.
В ответ на комментарий mmyers:
Вы случайно не знаете java.util.Random достаточно хорошо, чтобы объяснить, почему в данном случае он выбирает 5 и 6?
Ответ содержится в исходном коде java.util.Random, который представляет собой линейный конгруэнтный ГСЧ. . Когда вы указываете начальное значение в конструкторе, оно обрабатывается следующим образом.
seed = (seed ^ 0x5DEECE66DL) & mask;
Где маска просто сохраняет младшие 48 бит и отбрасывает остальные.
При генерации фактических случайных битов это начальное число обрабатывается следующим образом:
randomBits = (seed * 0x5DEECE66DL + 0xBL) & mask;
Теперь, если вы считаете, что семена, используемые Parker, были последовательными (0-1499), и они использовались один раз, а затем отбрасывались, первые четыре начальных числа генерировали следующие четыре набора случайных битов:
101110110010000010110100011000000000101001110100
101110110001101011010101011100110010010000000111
101110110010110001110010001110011101011101001110
101110110010011010010011010011001111000011100001
Обратите внимание, что верхние 10 битов в каждом случае идентичны. Это проблема, потому что он хочет генерировать значения только в диапазоне 0-7 (для чего требуется всего несколько битов), а реализация ГСЧ делает это, сдвигая старшие биты вправо и отбрасывая младшие биты. Это происходит потому, что в общем случае старшие биты более случайны, чем младшие. В данном случае это не так, потому что исходные данные были плохими.
Наконец, чтобы увидеть, как эти биты преобразуются в десятичные значения, которые мы получаем, вам нужно знать, что java.util.Random создает особый случай, когда n является степенью двойки. Он запрашивает 31 случайный бит (31 верхний бит из выше 48), умножает это значение на n и затем сдвигает его на 31 бит вправо.
Умножение на 8 (значение n в этом примере) равносильно сдвигу влево на 3 разряда. Таким образом, чистый эффект этой процедуры заключается в сдвиге 31 бита на 28 позиций вправо. В каждом из 4 приведенных выше примеров остается битовый шаблон 101 (или 5 в десятичном формате).
Если бы мы не отбрасывали ГСЧ только после одного значения, мы бы увидели, что последовательности расходятся. В то время как четыре последовательности выше всех начинаются с 5, вторые значения каждой из них равны 6, 0, 2 и 4 соответственно. Небольшие различия в исходных семенах начинают оказывать влияние.
В ответ на обновленный вопрос: java.util.Random является потокобезопасным, вы можете совместно использовать один экземпляр в нескольких потоках, поэтому нет необходимости иметь несколько экземпляров. Если вам действительно нужно иметь несколько экземпляров ГСЧ, убедитесь, что они заполняются полностью независимо друг от друга, иначе вы не можете быть уверены, что выходные данные будут независимыми.
Что касается того, почему вы получаете такие эффекты, java.util.Random не лучший ГСЧ. Это просто, довольно быстро и, если не присматриваться, достаточно случайно. Однако, если вы проведете несколько серьезных тестов его выходных данных, вы увидите, что он ошибочен. Вы можете увидеть это визуально здесь.
Если вам нужен более случайный RNG, вы можете использовать java.security.SecureRandom. Это немного медленнее, но работает правильно. Одна вещь, которая может быть проблемой для вас, заключается в том, что это невозможно повторить. Два экземпляра SecureRandom с одним и тем же начальным числом не дадут одинаковых результатов. Это по дизайну.
Итак, какие еще есть варианты? Здесь я подключаю мою собственную библиотеку. Он включает в себя 3 повторяющихся псевдо-ГСЧ, которые быстрее, чем SecureRandom, и более случайны, чем java.util.Random. Я их не изобретал, я просто перенес их из оригинальных версий C. Все они потокобезопасны.
Я реализовал эти ГСЧ, потому что мне нужно было что-то лучшее для моего эволюционного вычислительного кода. В соответствии с моим первоначальным кратким ответом этот код является многопоточным, но использует только один экземпляр ГСЧ, общий для всех потоков.
person
Dan Dyer
schedule
08.01.2009