อะไรคือข้อได้เปรียบของเครื่องกำเนิดไฟฟ้าแบบเอกซ์โพเนนเชียลแบบสุ่มโดยใช้วิธีของ Ahrens และ Dieter (1972) แทนที่จะใช้การแปลงผกผัน?


11

คำถามของฉันเป็นแรงบันดาลใจR 's rexp()ในตัวเครื่องกำเนิดไฟฟ้าจำนวนสุ่มชี้แจงฟังก์ชั่น เมื่อพยายามที่จะสร้างการกระจายชี้แจงตัวเลขสุ่มตำราหลายแนะนำผกผันเปลี่ยนวิธีการตามที่ระบุไว้ในหน้านี้วิกิพีเดีย ฉันรู้ว่ามีวิธีการอื่นเพื่อให้งานนี้สำเร็จ โดยเฉพาะอย่างยิ่งR 's รหัสที่มาใช้วิธีการที่ระบุไว้ในกระดาษโดย Ahrens & หิวโหย (1972)

ฉันมั่นใจว่าวิธี Ahrens-Dieter (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 จะสุ่มตัวเลขสุ่มอย่างน้อยสองชุด (พร้อมฟังก์ชันCunif_rand() ) เพื่อรับหมายเลขสุ่มเอ็กซ์โพเนนเชียลหนึ่งหมายเลข มันต้องการตัวเลขสุ่มที่เหมือนกันเพียงชุดเดียวเท่านั้น สันนิษฐานว่าทีมR core ตัดสินใจที่จะไม่ใช้ IT เพราะมันคิดว่าการลอการิทึมอาจช้ากว่าการสร้างตัวเลขสุ่มที่สม่ำเสมอกว่า ฉันเข้าใจว่าความเร็วในการถ่ายลอการิทึมนั้นขึ้นอยู่กับเครื่องจักร แต่อย่างน้อยสำหรับฉันตรงกันข้ามก็เป็นจริง บางทีอาจมีปัญหาเกี่ยวกับความแม่นยำเชิงตัวเลขของ IT เกี่ยวกับความเป็นเอกฐานของลอการิทึมที่ 0? แต่แล้ว ซอร์สโค้ดR sexp.cเผยให้เห็นว่าการดำเนินการโฆษณายังสูญเสียความแม่นยำตัวเลขเพราะบางส่วนต่อไปนี้รหัส C เอาบิตชั้นนำจากเครื่องแบบจำนวนสุ่มยู

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

ยูรีไซเคิลต่อมาเป็นจำนวนสุ่มเครื่องแบบในส่วนที่เหลือของsexp.c จนถึงตอนนี้ดูเหมือนว่า

  • รหัสนั้นง่ายกว่า
  • มันเร็วกว่าและ
  • ทั้ง IT และ AD อาจสูญเสียความแม่นยำเชิงตัวเลข

ผมจะขอบคุณถ้ามีคนสามารถอธิบายได้ว่าทำไม R rexp()ยังคงดำเนินการโฆษณาเป็นตัวเลือกที่ใช้ได้เฉพาะ


4
ด้วยตัวสร้างตัวเลขแบบสุ่ม "รหัสที่ง่ายขึ้น" ไม่ใช่ข้อควรพิจารณานอกเสียจากว่าคุณจะเป็นคนทำมัน! ความเร็วและความแม่นยำเป็นเพียงข้อพิจารณาสองข้อเท่านั้น (สำหรับเครื่องกำเนิดไฟฟ้าแบบสม่ำเสมอยังมีช่วงเวลาของเครื่องกำเนิดไฟฟ้าด้วย) ในสมัยก่อน AD นั้นเร็วขึ้น ในกล่อง Linux ของฉันโฆษณาจะทำงานในเวลาประมาณ 1/2 เวลาที่ฟังก์ชัน invTrans ของคุณทำและในแล็ปท็อปของฉันในเวลาประมาณ 2/3 คุณอาจต้องการใช้ microbenchmark เพื่อกำหนดเวลาที่ครอบคลุมมากขึ้นเช่นกัน
jbowman

5
ฉันอยากจะแนะนำเราไม่ได้โยกย้ายมัน ดูเหมือนว่าในหัวข้อสำหรับฉัน
อะมีบา

1
เนื่องจากฉันไม่สามารถคิดสถานการณ์เดียวซึ่งrexp(n)จะเป็นคอขวดความแตกต่างของความเร็วไม่ใช่ข้อโต้แย้งที่แข็งแกร่งสำหรับการเปลี่ยนแปลง (อย่างน้อยสำหรับฉัน) ฉันอาจกังวลเกี่ยวกับความแม่นยำของตัวเลขมากขึ้นถึงแม้ว่ามันจะไม่ชัดเจนสำหรับฉัน แต่อย่างใด
Cliff AB

1
@ amoeba ฉันคิดว่า "อะไรคือข้อดีของ ... " จะเป็นการใช้ถ้อยคำใหม่ที่ชัดเจนในหัวข้อที่นี่และจะไม่ส่งผลกระทบต่อคำตอบที่มีอยู่ ฉันคิดว่า "ทำไมคนที่ตัดสินใจเลือก R ... " เป็นคำถามเฉพาะซอฟต์แวร์ (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(ผมR))μμμยู

ดังนั้นดูเหมือนว่าผู้เขียนเลือกใช้วิธีการอื่นเพื่อหลีกเลี่ยงข้อ จำกัด "ผู้ผลิต" นี้ของลอการิทึมช้า บางทีคำถามนี้อาจถูกย้ายไปที่ stackoverflow ที่ดีที่สุดซึ่งใครบางคนที่มีความรู้เกี่ยวกับความกล้าหาญของ 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.