2015-06-26 10 views
13

Manchmal möchte ich eine randomisierte Funktion schreiben, die immer die gleiche Ausgabe für eine bestimmte Eingabe zurückgibt. Ich habe das immer implementiert, indem ich den Random-Seed am Anfang der Funktion gesetzt habe und dann fortgefahren bin. Betrachten wir zwei auf diese Weise definierten Funktionen:Making Funktionen, die die Zufalls-Seed unabhängig einstellen

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 liefert einen Vektor mit der angegebenen Größe zufällig aus dem Satz abgetastete {1, 2} und rand.prod multipliziert jedes Element eines bestimmten Vektors durch einen Zufallswert gleichmäßig von [0, 1] ausgewählt. Normalerweise würde ich x <- sample.12(10000) ; rand.prod(x) erwarte eine „Schritt“ Verteilung mit pdf 3/4 im Bereich [0, 1] und 1/4 im Bereich habe [1, 2], aber aufgrund meiner unglücklichen Wahl von identischen zufälligen Samen oben sehe ich ein anderes Ergebnis:

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

enter image description here

ich kann dieses Problem beheben in diesem Fall durch das zufällige Saatgut in einer der Funktionen auf einen anderen Wert zu ändern. Zum Beispiel mit set.seed(10000) in rand.prod bekomme ich die erwartete Verteilung:

enter image description here

Previously on SO diese Lösung verschiedener Samen verwendet wird als der beste Ansatz angenommen worden zu erzeugen unabhängige Zufallszahlenströme. Allerdings finde ich die Lösung unbefriedigend, da Streams mit unterschiedlichen Seeds miteinander verwandt sein könnten (möglicherweise sogar highly related to one another); in der Tat könnten sie sogar identische Ströme ergeben nach ?set.seed:

Es gibt keine Garantie dafür, dass unterschiedliche Werte von Samen unterschiedlich die RNG Saatgut wird, obwohl Ausnahmen extrem selten sein würde.

Gibt es eine Möglichkeit, ein Paar von randomisierten Funktionen in R zu implementieren, dass:

  1. immer die gleiche Leistung für einen bestimmten Eingang zurück und
  2. Erzwingen Unabhängigkeit zwischen den Quellen der Zufälligkeit durch mehr als nur verschiedene zufällige Samen zu verwenden?

Antwort

9

Ich habe in dieser etwas mehr gegraben und es sieht aus wie das rlecuyer Paket unabhängige Zufallsströme bietet:

Stellt eine Schnittstelle zur C Implementierung des Zufallszahlengenerators mit mehreren unabhängigen Strömen entwickelt von L'Ecuyer et al (2002). Der Hauptzweck dieses Pakets besteht darin, die Verwendung dieses Zufallszahlengenerators in parallelen R-Anwendungen zu ermöglichen.

Der erste Schritt globale Initialisierung der unabhängigen Ströme ist:

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

Dann wird jede Funktion eingestellt, die R, zurückgesetzt Zufallszahlengenerator muss geändert werden, um den geeigneten Strom zu seinem Anfangszustand (.lec.RestartStartStream) zu dem entsprechenden Strom (.lec.CurrentStream), und danach den R-Zufallszahlengenerator in seinen Zustand zurückzusetzen, bevor die Funktion aufgerufen wurde (.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 
} 

Dies erfüllt die Anforderung, "immer die gleiche Leistung das gleiche Eingangs gegeben zurück":

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

Die Ströme scheint auch zu funktionieren unabhängig in unserem Beispiel:

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

enter image description here

Beachten Sie, dass dies keine konsistenten Werte für alle Läufe ergibt unseres Skripts, weil jeder Aufruf an .lec.CreateStream einen anderen Anfangszustand geben würde. Um dem abzuhelfen, könnten wir den Anfangszustand für jeden Strom beachten:

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

Wir können dann den Strom Initialisierung am Anfang des Skripts ändern:

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

ruft sample.12 und rand.prod werden Übereinstimmung über Aufrufe des Skripts.

+1

Toller Fund. Aus Gründen der Vollständigkeit verwendet 'rlecuyer'' MRG32k3a' (Kapitel 1.1 von [dieses Papier] (http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf)), so kann es sein ebenso wie seine Grenzen (genau wie Mersenne-Twister). Sollte in 99% Fällen jedoch kein großes Problem sein. – tonytonov