Ters dönüşümden ziyade Ahrens ve Dieter (1972) yöntemini kullanarak üstel rasgele jeneratörün avantajları nelerdir?


11

Benim sorum R 'yerleşik üstel rasgele sayı üreteci, fonksiyonu esinlenerek rexp(). Üstel olarak dağıtılmış rasgele sayılar oluşturmaya çalışırken, birçok ders kitabı bu Wikipedia sayfasında ana hatlarıyla belirtildiği gibi ters dönüştürme yöntemini önerir . Bu görevi yerine getirmenin başka yöntemleri olduğunun farkındayım. Özellikle, R'nin kaynak kodu Ahrens & Dieter (1972) tarafından bir makalede ana hatları çizilen algoritmayı kullanmaktadır .

Ahrens-Dieter (AD) yönteminin doğru olduğuna kendimi ikna ettim. Yine de, ters dönüşüm (IT) yöntemine kıyasla yöntemlerini kullanmanın faydasını görmüyorum. AD'nin uygulanması BT'den daha karmaşık değildir. Bir hız yararı da yok gibi görünüyor. İşte her iki yöntemi de takip eden sonuçları takip eden R kodum.

invTrans <- function(n)
    -log(runif(n))
print("For the inverse transform:")
print(system.time(invTrans(1e8)))
print("For the Ahrens-Dieter algorithm:")
print(system.time(rexp(1e8)))

Sonuçlar:

[1] "For the inverse transform:" 
user     system     elapsed
4.227    0.266      4.597 
[1] "For the Ahrens-Dieter algorithm:"
user     system     elapsed
4.919    0.265      5.213

İki yöntemin kodunu karşılaştıran AD, bir üstel rasgele sayı elde etmek için en az iki tekdüze rasgele sayı çizer ( C fonksiyonu ile unif_rand()). BT'nin tek bir rastgele sayıya ihtiyacı vardır. Muhtemelen R çekirdek ekibi BT'nin uygulanmasına karşı karar verdi çünkü logaritmayı almanın daha tekdüze rastgele sayılar üretmekten daha yavaş olabileceğini varsayıyordu. Logaritma alma hızının makineye bağlı olabileceğini anlıyorum, ama en azından benim için tam tersi doğru. Belki de BT'nin sayısal hassasiyeti etrafında 0'daki logaritmanın tekilliğiyle ilgili sorunlar var mı? Ama sonra, R kaynak kodu sexp.cCı aşağıdaki kod bölümü muntazam rasgele sayıdan gelen bit kaldırır, çünkü AD uygulanması bazı sayısal hassas kaybeder ortaya u .

double u = unif_rand();
while(u <= 0. || u >= 1.) u = unif_rand();
for (;;) {
    u += u;
    if (u > 1.)
        break;
    a += q[0];
}
u -= 1.;

u daha sonra sexp'in geri kalanında tek tip rastgele bir sayı olarak geri dönüştürülür . Şimdiye kadar, sanki

  • BT'nin kodlanması daha kolaydır,
  • BT daha hızlı ve
  • hem BT hem de AD sayısal doğruluğu kaybedebilir.

Birisi R neden hala kullanılabilir tek seçenek olarak AD uygular açıklayabilir eğer gerçekten takdir ediyorum rexp().


4
Rastgele sayı üreteçleri ile, "kodlaması daha kolay", bunu yapan siz değilseniz gerçekten dikkate alınmaz! Hız ve doğruluk sadece iki noktadır. (Düzgün jeneratörler için jeneratörün periyodu da vardır.) Eski günlerde AD daha hızlıydı. Linux kutumda, AD, invTrans işlevinizin yaptığı sürenin yaklaşık 1 / 2'si, dizüstü bilgisayarımda ise yaklaşık 2 / 3'ünde çalışır. Daha kapsamlı zamanlamalar için mikrobenç işareti de kullanmak isteyebilirsiniz.
jbowman

5
Göç etmememizi öneririm. Bu bana konu ile ilgili gibi geliyor.
amip

1
rexp(n)Darboğaz olacak tek bir senaryo düşünemediğim göz önüne alındığında, hızdaki fark, değişim için güçlü bir argüman değil (en azından benim için). Hangisinin daha sayısal olarak güvenilir olacağı açık olmasa da, sayısal doğruluk konusunda daha fazla endişelenebilirim.
Cliff AB

1
@amoeba Bence "Avantajları ne olacak?" burada açıkça konu olan ve mevcut cevapları etkilemeyecek bir yeniden ifade olacaktır. Sanırım "Neden R yapmaya karar verenler ..." gerçekten yazılıma özgü bir soru, (b) ya dokümantasyonda ya da telepatide kanıt gerektiriyor, bu yüzden tartışmasız konu dışı olabilir. Şahsen bu soruyu site kapsamında daha açık hale getirmek için yeniden ifade edilmesini tercih ederim, ancak bunu kapatmak için yeterince güçlü bir neden olarak görmüyorum.
Silverfish

1
@ amoeba bir şansım vardı. Önerilen yeni başlığımın özellikle dilbilgisi olduğuna ikna olmadım ve belki de soru metninin diğer bazı bölümleri de değişebilirdi. Ama umarım bu daha açık bir konudur ve en azından her iki cevapta da geçersiz olduğunu veya değişiklik yapılmasını gerektirdiğini düşünmüyorum.
Silverfish

Yanıtlar:


9

Bilgisayarımda (Fransızca'yı affedin!):

> print(system.time(rexp(1e8)))
utilisateur     système      écoulé 
      4.617       0.320       4.935 
> print(system.time(rexp(1e8)))
utilisateur     système      écoulé 
      4.589       2.045       6.629 
> print(system.time(-log(runif(1e8))))
utilisateur     système      écoulé 
      7.455       1.080       8.528 
> print(system.time(-log(runif(1e8))))
utilisateur     système      écoulé 
      9.140       1.489      10.623

ters dönüşüm daha da kötüleşir. Ancak değişkenliğe dikkat etmelisiniz. Bir oran parametresinin kullanılması, ters dönüşüm için daha da fazla değişkenliğe yol açar:

> print(system.time(rexp(1e8,rate=.01)))
utilisateur     système      écoulé 
      4.594       0.456       5.047 
> print(system.time(rexp(1e8,rate=.01)))
utilisateur     système      écoulé 
      4.661       1.319       5.976 
> print(system.time(-log(runif(1e8))/.01))
utilisateur     système      écoulé 
     15.675       2.139      17.803 
> print(system.time(-log(runif(1e8))/.01))
utilisateur     système      écoulé 
      7.863       1.122       8.977 
> print(system.time(rexp(1e8,rate=101.01)))
utilisateur     système      écoulé 
      4.610       0.220       4.826 
> print(system.time(rexp(1e8,rate=101.01)))
utilisateur     système      écoulé 
      4.621       0.156       4.774 
> print(system.time(-log(runif(1e8))/101.01))
utilisateur     système      écoulé 
      7.858       0.965       8.819 > 
> print(system.time(-log(runif(1e8))/101.01))
utilisateur     système      écoulé 
     13.924       1.345      15.262 

İşte karşılaştırmalar rbenchmark:

> benchmark(x=rexp(1e6,rate=101.01))
  elapsed user.self sys.self
  4.617     4.564    0.056
> benchmark(x=-log(runif(1e6))/101.01)
  elapsed user.self sys.self
  14.749   14.571    0.184
> benchmark(x=rgamma(1e6,shape=1,rate=101.01))
  elapsed user.self sys.self
  14.421   14.362    0.063
> benchmark(x=rexp(1e6,rate=.01))
  elapsed user.self sys.self
  9.414     9.281    0.136
> benchmark(x=-log(runif(1e6))/.01)
  elapsed user.self sys.self
  7.953     7.866    0.092
> benchmark(x=rgamma(1e6,shape=1,rate=.01))
  elapsed user.self sys.self
  26.69    26.649    0.056

Yani kilometre ölçeğe bağlı olarak hala değişmektedir!


2
Dizüstü bilgisayarımda, saatler OP ile o kadar yakından eşleşiyor ki, aynı makineye (veya en azından aynı işlemciye) sahip olduğumuzdan şüpheleniyorum. Ancak bence buradaki nokta, gözlemlenen hız avantajının platforma bağlı olması ve minimum fark göz önüne alındığında, hız açısından bir diğerine göre belirgin bir avantaj yok.
Cliff AB

4
microbenchmarkBunun yerine belki bir sahne yapabilir misiniz ?
Firebug

2
Sistem süreleri, belki de kesintiler ve bellek sayfalaması nedeniyle oldukça değişken ek yükü ölçüyor gibi görünüyor. @Cliff'in belirttiği gibi, sistemler arasındaki performanstaki büyük göreceli farklılıkları görmek ilginçtir . Örneğin, çok fazla RAM'e sahip bir Xeon'da neredeyse hiç sistem zamanı (0.05 ila 0.32 saniye), yaklaşık% 12 daha uzun kullanıcı zamanı rexp,% 3 daha kısa kullanıcı zamanı -log(runif())ve oran parametresiyle ( değişkenlik yok toplam saniye). Hepimiz dolaylı olarak , bir Fortran altyordamıyla elde edilebilecek zamanlar için ve kıyaslanabilir olduğunu varsayıyoruz . 5.27±0.02Rlogrunif
whuber

7

Bu sadece "Algoritma LG: (Logaritma yöntemi)" bölümündeki makaleden alıntı yapmaktadır:

FORTRAN'da algoritma, herhangi bir alt program çağrısından kaçınarak doğrudan programlanır. Performans, numune başına 504 . Bu süre zarfında, 361 sn sn, üretici logaritma rutini tarafından ve 105 sn sn, homojen olarak dağıtılan değişken REGOL'si için jeneratör tarafından alındı . Bu nedenle, aynı iki alt programı kullanmak zorunda kalacak bir birleştirici işlevi yazarak hızı artırmaya çalışmanın bir anlamı yoktu.μ μ μ uX=ALOG(REGOL(IR))μμμu

Yani yazarlar yavaş logaritmaların bu "üretici" sınırlama önlemek için başka yöntemler tercih gibi görünüyor. Belki de bu soru en iyi R bağırsakları hakkında bilgi sahibi olan birinin yorum yapabileceği yığın akışına taşınır.


6

Sadece bununla microbenchmark; makinemde, R'nin doğal yaklaşımı eşit derecede daha hızlı:

library(microbenchmark)
microbenchmark(times = 10L,
               R_native = rexp(1e8),
               dir_inv = -log(runif(1e8)))
# Unit: seconds
#      expr      min       lq     mean   median       uq      max neval
#  R_native 3.643980 3.655015 3.687062 3.677351 3.699971 3.783529    10
#   dir_inv 5.780103 5.783707 5.888088 5.912384 5.946964 6.050098    10

Yenilik uğruna, bunun tamamen sahip olmadığından emin :λ=1

lambdas = seq(0, 10, length.out = 25L)[-1L]
png("~/Desktop/micro.png")
matplot(lambdas, 
        ts <- 
          t(sapply(lambdas, function(ll)
            print(microbenchmark(times = 50L,
                                 R_native = rexp(5e5, rate = ll),
                                 dir_inv = -log(runif(5e5))/ll),
                  unit = "relative")[ , "median"])),
        type = "l", lwd = 3L, xlab = expression(lambda),
        ylab = "Relative Timing", lty = 1L,
        col = c("black", "red"), las = 1L,
        main = paste0("Direct Computation of Exponential Variates\n",
                      "vs. R Native Generator (Ahrens-Dieter)"))
text(lambdas[1L], ts[1L, ], c("A-D", "Direct"), pos = 3L)
dev.off()

resim açıklamasını buraya girin

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.