Подтвердить что ты не робот

Создание функций, которые задают случайное независимое от семени

Иногда я хочу написать рандомизированную функцию, которая всегда возвращает тот же вывод для конкретного ввода. Я всегда выполнял это, установив случайное семя в верхней части функции, а затем продолжая. Рассмотрим две функции, определенные таким образом:

sample.12 <- function(size) {
  set.seed(144)
  sample(1:2, size, replace=TRUE)
}
rand.prod <- function(x) {
  set.seed(144)
  runif(length(x)) * x
}

sample.12 возвращает вектор заданного размера, случайным образом выбранный из набора {1, 2} и rand.prod, умножает каждый элемент заданного вектора на случайное значение, равномерно выбранное из [0, 1]. Обычно я ожидал бы, что x <- sample.12(10000) ; rand.prod(x) будет иметь "ступенчатое" распределение с pdf 3/4 в диапазоне [0, 1] и 1/4 в диапазоне [1, 2], но из-за моего неудачного выбора одинаковых случайных семян выше я вижу другой результат:

x <- sample.12(10000)
hist(rand.prod(x))

enter image description here

Я могу исправить эту проблему в этом случае, изменив случайное семя в одной из функций на какое-то другое значение. Например, при set.seed(10000) в rand.prod я получаю ожидаемое распределение:

enter image description here

Ранее на SO это решение использования разных семян было принято в качестве наилучшего подхода для генерации независимых потоков случайных чисел. Однако я считаю, что решение является неудовлетворительным, потому что потоки с разными семенами могут быть связаны друг с другом (возможно, даже сильно связаны друг с другом); на самом деле они могут даже давать идентичные потоки в соответствии с ?set.seed:

Нет никакой гарантии, что разные значения семян будут засеять RNG по-разному, хотя любые исключения будут крайне редкими.

Есть ли способ реализовать пару рандомизированных функций из R, что:

  • Всегда возвращайте один и тот же вывод для определенного ввода, а
  • Обеспечить независимость между их источниками случайности более чем просто с использованием разных случайных семян?
4b9b3361

Ответ 1

Я вникнул в это еще немного, и похоже, что пакет rlecuyer предоставляет независимые случайные потоки:

Предоставляет интерфейс для реализации C генератора случайных чисел с несколькими независимыми потоками, разработанными L'Ecuyer et al (2002). Основная цель этого пакета - включить использование этого генератора случайных чисел в параллельных приложениях R.

Первым шагом является глобальная инициализация независимых потоков:

library(rlecuyer)
.lec.CreateStream(c("stream.12", "stream.prod"))

Затем каждая функция должна быть изменена до reset соответствующего потока в его начальное состояние (.lec.RestartStartStream), установите генератор случайных чисел R в соответствующий поток (.lec.CurrentStream), а затем установите случайное число R генератор возвращается в свое состояние до того, как была вызвана функция (.lec.CurrentStreamEnd).

sample.12 <- function(size) {
  .lec.ResetStartStream("stream.12")
  .lec.CurrentStream("stream.12")
  x <- sample(1:2, size, replace=TRUE)
  .lec.CurrentStreamEnd()
  x
}
rand.prod <- function(x) {
  .lec.ResetStartStream("stream.prod")
  .lec.CurrentStream("stream.prod")
  y <- runif(length(x)) * x
  .lec.CurrentStreamEnd()
  y
}

Это удовлетворяет условию "всегда возвращает тот же результат с учетом того же ввода":

all.equal(rand.prod(sample.12(10000)), rand.prod(sample.12(10000)))
# [1] TRUE

Потоки также работают независимо в нашем примере:

x <- sample.12(10000)
hist(rand.prod(x))

enter image description here

Обратите внимание, что это не дало бы согласованных значений во всех прогонах нашего script, потому что каждый вызов .lec.CreateStream дал бы другое начальное состояние. Чтобы устранить это, мы могли бы отметить начальное состояние для каждого потока:

.lec.GetState("stream.12")
# [1] 3161578179 1307260052 2724279262 1101690876 1009565594  836476762
.lec.GetState("stream.prod")
# [1]  596094074 2279636413 3050913596 1739649456 2368706608 3058697049

Затем мы можем изменить инициализацию потока в начале script на:

library(rlecuyer)
.lec.CreateStream(c("stream.12", "stream.prod"))
.lec.SetSeed("stream.12", c(3161578179, 1307260052, 2724279262, 1101690876, 1009565594, 836476762))
.lec.SetSeed("stream.prod", c(596094074, 2279636413, 3050913596, 1739649456, 2368706608, 3058697049))

Теперь вызовы sample.12 и rand.prod будут совпадать между вызовами script.