2015-06-26 66 views
13

有時我想編寫一個隨機函數,它總是爲特定輸入返回相同的輸出。我一直通過在函數頂部設置隨機種子然後繼續執行此操作。考慮以這種方式定義了兩個功能:設置隨機種子獨立功能

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)有在範圍[1, 2]範圍[0, 1]和1/4「臺階」與PDF 3/4分佈,但由於我的不幸的選擇相同的隨機種子上面我看到了不同的結果:

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

enter image description here

我可以改變的功能之一的隨機種子爲其他值修正在這種情況下這個問題。例如,在rand.prodset.seed(10000)我得到預期的分佈:

enter image description here

Previously on SO使用不同的種子該解決方案已被認爲是獨立產生的隨機數流的最佳方法。但是,我發現解決方案不令人滿意,因爲具有不同種子的流可能彼此相關(甚至可能爲highly related to one another);事實上,他們可能會根據?set.seed甚至產生相同的流:

沒有保證種子的不同值會有所不同播種RNG,儘管任何的例外是極其罕見的。

有沒有實現對隨機函數中的R一個辦法:

  1. 總是返回特定輸入相同的輸出,並
  2. 由多個執行其隨機性源之間的獨立性比只使用不同的隨機種子?

回答

9

我已經挖成這樣一些,它看起來像rlecuyer包提供獨立的隨機流:

提供對C實現的隨機數發生器通過開發多個獨立的流接口L'Ecuyer等(2002)。這個軟件包的主要目的是在並行R應用程序中使用這個隨機數生成器。

的第一步是在獨立流的全局初始化:

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

然後,每個功能需要被修改以適當的流重置爲開始狀態(.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

請注意,這不會在運行中給出一致的值我們的腳本,因爲每個電話.lec.CreateStream會給出不同的初始狀態。爲了解決這個問題,我們可以注意到每個流的初始狀態:

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

然後我們就可以在腳本的開頭來改變流初始化:

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.12rand.prod會通過對腳本的調用進行匹配。

+1

很棒的發現。爲了完整起見,'rlecuyer'使用'MRG32k3a'(本文的第1.1章(http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf)),所以它可能也有其侷限性(就像Mersenne-Twister所做的那樣)。儘管如此,99%的案例不應該成爲問題。 – tonytonov