Marsaglia polära metod
Marsaglias normala polära metod är en pseudo-slumpmässigt talsamplingsmetod för att generera ett par oberoende standard slumpvariabler .
Standard normala slumpvariabler används ofta inom datavetenskap , beräkningsstatistik och i synnerhet i tillämpningar av Monte Carlo-metoden .
Den polära metoden fungerar genom att välja slumpmässiga punkter ( x , y ) i kvadraten −1 < x < 1, −1 < y < 1 tills
och sedan returnera det nödvändiga paret av normala slumpvariabler som
eller på motsvarande sätt
där och representerar cosinus och sinus för vinkeln som vektorn ( x , y ) gör med x -axeln.
Teoretisk grund
Den bakomliggande teorin kan sammanfattas enligt följande:
Om u är likformigt fördelad i intervallet 0 ≤ u < 1, så är punkten (cos(2π u ), sin(2π u )) likformigt fördelad på enhetens omkrets x 2 + y 2 = 1, och multiplicera den punkten med en oberoende stokastisk variabel ρ vars fördelning är
kommer att producera en poäng
vars koordinater är gemensamt fördelade som två oberoende standard normala slumpvariabler.
Historia
Denna idé går tillbaka till Laplace , som Gauss tillskriver att han hittat ovanstående
genom att ta kvadratroten av
Transformationen till polära koordinater visar att θ är likformigt fördelad (konstant densitet) från 0 till 2π, och att det radiella avståndet r har densitet
( r 2 har lämplig chi-kvadratfördelning .)
Denna metod för att producera ett par oberoende standardnormalvariationer genom att radiellt projicera en slumpmässig punkt på enhetens omkrets till ett avstånd som ges av kvadratroten av en chi-kvadrat-2-variat kallas den polära metoden för att generera ett par normala slumpvariabler ,
Praktiska överväganden
En direkt tillämpning av denna idé,
kallas Box–Muller-transformen , där chi-variaten vanligtvis genereras som
men den transformationen kräver logaritm, kvadratrot, sinus och cosinusfunktioner. På vissa processorer kan cosinus och sinus för samma argument beräknas parallellt med en enda instruktion. Särskilt för Intel-baserade maskiner kan man använda fsincos assembler-instruktion eller expi-instruktionen (tillgänglig t.ex. i D), för att beräkna komplexa
och bara separera de verkliga och imaginära delarna.
Obs: För att explicit beräkna den komplexa polära formen använd följande ersättningar i den allmänna formen,
Låt och Sedan
Däremot tar den polära metoden här bort behovet av att beräkna en cosinus och sinus. Istället, genom att lösa en punkt på enhetscirkeln, kan dessa två funktioner ersättas med x- och y -koordinaterna normaliserade till radie. I synnerhet projiceras en slumpmässig punkt ( x , y ) inuti enhetscirkeln på enhetens omkrets genom att ställa in och bilda punkt
vilket är en snabbare procedur än att beräkna cosinus och sinus. Vissa forskare hävdar att den villkorade if-instruktionen (för att avvisa en punkt utanför enhetscirkeln), kan göra program långsammare på moderna processorer utrustade med pipelining och förgreningsprediktion. Även denna procedur kräver cirka 27 % fler utvärderingar av den underliggande slumptalsgeneratorn (endast av genererade punkter ligger inuti enhetscirkeln).
Den slumpmässiga punkten på omkretsen projiceras sedan radiellt det erforderliga slumpmässiga avståndet med hjälp av
använder samma s eftersom det s är oberoende av den slumpmässiga punkten på omkretsen och själv är jämnt fördelad från 0 till 1.
Genomförande
Enkel implementering i Java med medelvärde och standardavvikelse:
0
privat statisk dubbel reservdel ; privat statisk boolesk hasSpare = false ; offentlig statisk synkroniserad dubbel genereraGaussian ( dubbel medelvärde , dubbel stdDev ) { if ( hasSpare ) { hasSpare = false ; returnera reserv * stdDev + medelvärde ; } annat { dubbel u , v , s ; gör { u = Math . slumpmässig () * 2 - 1 ; v = matematik . slumpmässig () * 2 - 1 ; s = u * u + v * v ; } while ( s >= 1 || s == ); s = matematik . sqrt ( -2.0 * Math . log ( s ) / s ) ; reserv = v * s ; hasSpare = sant ; returnera medelvärde + stdDev * u * s ; } }
En icke- trådssäker implementering i C++ med medelvärde och standardavvikelse:
dubbel genereraGaussian ( dubbelt medelvärde , dubbel stdDev ) { statisk dubbel reservdel ; static bool hasSpare = false ; if ( hasSpare ) { hasSpare = false ; returnera reserv * stdDev + medelvärde ; } annat { dubbel u , v , s ; do { u = ( rand () / (( double ) RAND_MAX )) * 2,0 - 1,0 ; v = ( rand () / (( dubbel ) RAND_MAX )) * 2,0 - 1,0 ; s = u * u + v * v ; } while ( s >= 1,0 || s == 0,0 ); s = sqrt ( -2,0 * log ( s ) / s ); reserv = v * s ; hasSpare = sant ; returnera medelvärde + stdDev * u * s ; } }
C++11 GNU GCC libstdc++ :s implementering av std::normal_distribution använder den polära Marsaglia-metoden, som citeras här .