역변환보다는 Ahrens and Dieter (1972)의 방법을 사용하는 지수 랜덤 생성기의 장점은 무엇입니까?


11

내 질문은 R 의 내장 지수 난수 생성기 인 함수에서 영감을 얻었습니다 rexp(). 기하 급수적으로 분포 된 난수를 생성하려고 할 때 많은 교과서 에서이 Wikipedia 페이지에 요약 된 역변환 방법을 권장합니다 . 이 작업을 수행하는 다른 방법이 있다는 것을 알고 있습니다. 특히, R 의 소스 코드는 Ahrens & Dieter (1972)논문에 요약 된 알고리즘을 사용합니다 .

나는 AD (Ahrens-Dieter) 방법이 옳다는 것을 확신했다. 여전히, 나는 역변환 (IT) 방법에 비해 그들의 방법을 사용하는 이점을 보지 못합니다. AD는 IT보다 구현하기가 더 복잡 할뿐만 아니라 속도 이점도없는 것 같습니다. 다음은 두 방법을 벤치마킹하고 결과를 따르는 R 코드입니다.

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

결과 :

[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

AD는 두 가지 방법의 코드를 비교하여 하나 이상의 지수 난수를 얻기 위해 두 개 이상의 균일 난수 ( C 함수 사용 unif_rand())를 그립니다 . IT는 하나의 균일 한 난수 만 필요합니다. 아마도 R 코어 팀은 로그를 취하는 것이 더 균일 한 난수를 생성하는 것보다 느릴 수 있다고 가정했기 때문에 IT 구현을 결정하지 않았습니다 . 나는 로그를 가져 오는 속도가 기계에 따라 다를 수 있지만 적어도 나에게는 그 반대라고 생각합니다. 아마도 IT의 수치 정밀도가 로그의 특이점과 관련이있는 문제가 0에 있습니까? 그러나 R 소스 코드 sexp.cC 코드의 다음 부분이 균일 난수 u 에서 선행 비트를 제거하기 때문에 AD의 구현도 수치 적 정밀도를 잃어버린다는 사실이 밝혀 졌습니다.

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 는 나중에 sexp.c 의 나머지 부분에서 균일 한 난수로 재활용됩니다 . 지금까지는 마치

  • IT는 코딩하기가 더 쉽고
  • IT가 더 빠르며
  • IT와 AD 모두 수치 정확도를 잃을 수 있습니다.

R이 여전히 AD를 유일하게 사용 가능한 옵션으로 구현하는 이유를 누군가가 설명 할 수 있다면 정말 감사하겠습니다 rexp().


4
난수 생성기를 사용하면 "쉽게 코딩하기"를 고려하지 않아도됩니다. 속도와 정확도는 두 가지 고려 사항입니다. 균일 한 발전기의 경우 발전기의 주기도 있습니다. 옛날에는 AD가 더 빨랐습니다. 내 Linux 상자에서 AD는 invTrans 기능보다 약 1/2 시간, 랩톱에서는 약 2/3 시간으로 실행됩니다. 보다 포괄적 인 타이밍을 위해 microbenchmark를 사용할 수도 있습니다.
jbowman

5
마이그레이션하지 않는 것이 좋습니다. 이것은 나에게 주제로 보인다.
amoeba는

1
rexp(n)병목 현상이 발생 하는 단일 시나리오를 생각할 수 없기 때문에 속도의 차이는 (적어도 나에게는) 변화에 대한 강력한 논쟁이 아닙니다. 어떤 수치가 더 신뢰할 수 있는지는 확실하지 않지만 수치 정확도에 대해 더 걱정할 수 있습니다.
Cliff AB

1
@amoeba 나는 "의 장점은 무엇일까요?"는 여기에 분명한 주제가 될 것이며 기존의 답변에는 영향을 미치지 않을 것이라고 생각합니다. "R을 만든 사람들이 왜하기로 결정했는지 ..."가 실제로 (a) 소프트웨어 관련 질문이며, (b) 문서 나 텔레파시의 증거가 필요하기 때문에 논란의 여지가있을 수 있습니다. 개인적으로 나는 사이트의 범위 내에서 더 명확하게하기 위해 질문을 다시 표현하고 싶지만 이것을 닫을만한 충분한 이유라고 생각하지 않습니다.
Silverfish

1
@amoeba 나는 갔다. 내가 제안한 새로운 제목이 특히 문법적이라는 것을 확신하지 못하고 질문 텍스트의 다른 부분이 변경과 관련이있을 수 있습니다. 그러나 나는 이것이 더 명확하게 주제에 있기를 희망하며, 그것이 무효화되거나 두 가지 대답에 대한 변경이 필요하다고 생각하지 않습니다.
Silverfish

답변:


9

내 컴퓨터에서 (프랑스어를 용서하십시오!) :

> 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

역변환은 더 나빠집니다. 그러나 가변성을주의해야합니다. 속도 매개 변수를 도입하면 역변환에 대한 변동성이 훨씬 커집니다.

> 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 

다음을 사용한 비교는 다음과 같습니다 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

따라서 마일리지는 규모에 따라 여전히 다릅니다!


2
내 랩톱에서 시간이 OP의 시간과 너무 일치하여 동일한 시스템 (또는 적어도 동일한 프로세서)이 있다고 생각합니다. 그러나 여기서 당신의 요점은 플랫폼에 따라 관찰되는 속도 이점이며, 최소한의 차이를 감안할 때 속도와 관련하여 다른 것보다 명확한 이점은 없습니다.
Cliff AB

4
아마도 microbenchmark대신 수행 할 수 있습니까?
Firebug

2
rexp-log(runif())5.27±0.02Rlogrunif

7

"알고리즘 LG : (대 수법)"섹션에서 기사를 인용하면됩니다.

X=ALOG(REGOL(IR))μμμu

따라서 저자들은 느린 로그의이 "제조업체"제한을 피하기 위해 다른 방법을 선택한 것처럼 보입니다. 아마도이 질문은 R의 내장에 대한 지식이있는 사람이 주석을 달 수있는 스택 오버 플로우로 가장 잘 옮겨 질 것입니다.


6

그냥 이것을 사용하여 microbenchmark; 내 컴퓨터에서 R의 기본 접근 방식은 균일하게 빠릅니다.

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

λ=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()

여기에 이미지 설명을 입력하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.