2015-06-26 49 views
13

A volte desidero scrivere una funzione randomizzata che restituisca sempre lo stesso output per un input specifico. L'ho sempre implementato impostando il seme casuale all'inizio della funzione e procedendo. Considerare due funzioni definite in questo modo:Creazione di funzioni che impostano il seed independent indipendente

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 restituisce un vettore di dimensione specificata campionatura casuale dal set {1, 2} e rand.prod moltiplica ciascun elemento di un vettore specificato da un valore casuale uniformemente scelto tra [0, 1]. Normalmente mi aspetterei una distribuzione "step" con pdf 3/4 nell'intervallo [0, 1] e 1/4 nell'intervallo [1, 2], ma a causa della mia sfortunata scelta di semi casuali identici sopra vedo un risultato diverso:

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

enter image description here

posso risolvere questo problema in questo caso cambiando il seme casuale in una delle funzioni per qualche altro valore. Ad esempio, con set.seed(10000) in rand.prod ottengo la distribuzione previsto:

enter image description here

Previously on SO questa soluzione di utilizzare diversi semi è stato accettato come il metodo migliore per generare numeri casuali indipendenti flussi. Tuttavia, trovo che la soluzione sia insoddisfacente perché i flussi con semi diversi potrebbero essere correlati tra loro (forse anche highly related to one another); infatti, essi potrebbero anche producono flussi identici secondo ?set.seed:

Non v'è alcuna garanzia che differenti valori di seme seme RNG diverso, anche se le eccezioni sarebbero estremamente raro.

C'è un modo per implementare una coppia di funzioni randomizzati in R che:

  1. Riportare sempre la stessa uscita per un particolare ingresso, e
  2. Imporre indipendenza tra loro fonti di casualità di più che usare solo semi casuali diversi?

risposta

9

Ho scavato in questo un po 'di più e sembra che il pacchetto rlecuyer fornisce flussi casuali indipendenti:

fornisce un'interfaccia per l'attuazione C del generatore di numeri casuali con più flussi indipendenti sviluppati da L'Ecuyer et al (2002). Lo scopo principale di questo pacchetto è di abilitare l'uso di questo generatore di numeri casuali in applicazioni R parallele.

Il primo passo è inizializzazione globale dei flussi indipendenti:

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

Poi ciascuna funzione deve essere modificata per ripristinare il flusso appropriato al suo stato iniziale (.lec.RestartStartStream), impostare il generatore di numeri casuali R allo stream appropriato (.lec.CurrentStream) e in seguito impostare il generatore di numeri casuali R sul suo stato prima che fosse chiamata la funzione (.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 
} 

Questo soddisfa la "restituisce sempre la stessa uscita dato lo stesso input" requisito:

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

I flussi appare anche di operare autonomamente nel nostro esempio:

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

enter image description here

Si noti che questo non fornirebbe valori coerenti attraverso le corse del nostro script perché ogni chiamata a .lec.CreateStream darebbe uno stato iniziale diverso. Per risolvere questo problema, potremmo notare lo stato iniziale per ogni flusso:

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

Possiamo quindi modificare l'inizializzazione torrente all'inizio dello script a:

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)) 

Ora chiamate a sample.12 e rand.prod sarà abbinare le chiamate allo script.

+1

Grande scoperta. Per completezza, 'rlecuyer' usa' MRG32k3a' (capitolo 1.1 di [questo articolo] (http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf)), quindi può anche i suoi limiti (proprio come fa Mersenne-Twister). Non dovrebbe essere un grosso problema nel 99% dei casi. – tonytonov