व्युत्क्रम रूपांतर के बजाय एहरेंस और डाइटर (1972) की विधि का उपयोग कर एक घातीय यादृच्छिक जनरेटर के फायदे क्या हैं?


11

मेरा प्रश्न आर के अंतर्निहित घातीय यादृच्छिक संख्या जनरेटर, फ़ंक्शन से प्रेरित है rexp()। जब तेजी से वितरित यादृच्छिक संख्याओं को उत्पन्न करने की कोशिश की जा रही है, तो कई पाठ्यपुस्तक इस विकिपीडिया पृष्ठ में उल्लिखित व्युत्क्रम परिवर्तन विधि की सिफारिश करती हैं । मुझे पता है कि इस कार्य को पूरा करने के लिए अन्य तरीके हैं। विशेष रूप से, आर के स्रोत कोड एक पेपर में उल्लिखित एल्गोरिथ्म का उपयोग करता है, जो अहरेंस एंड डाइटर (1972) द्वारा उल्लिखित है ।

मैंने खुद को आश्वस्त किया है कि एहरेंस-डाइटर (एडी) विधि सही है। फिर भी, मुझे उलटा परिवर्तन (आईटी) विधि की तुलना में उनकी विधि का उपयोग करने का लाभ नहीं दिखता है। IT की तुलना में AD केवल लागू करने के लिए अधिक जटिल नहीं है। कोई गति लाभ भी प्रतीत नहीं होता है। परिणामों के बाद दोनों तरीकों को बेंचमार्क करने के लिए मेरा आर कोड है।

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() ) खींचता है । आईटी को केवल एक समान यादृच्छिक संख्या की आवश्यकता है। संभवतः आर कोर टीम ने आईटी को लागू करने के खिलाफ फैसला किया क्योंकि यह मान लिया था कि लॉगरिथम लेने से अधिक समान यादृच्छिक संख्या उत्पन्न करने की तुलना में धीमी हो सकती है। मैं समझता हूं कि लॉगरिथम लेने की गति मशीन पर निर्भर हो सकती है, लेकिन कम से कम मेरे लिए इसके विपरीत सच है। शायद आईटी के संख्यात्मक मुद्दों के चारों ओर 0 पर लघुगणक की विलक्षणता के साथ कुछ मुद्दे हैं? लेकिन फिर, आर स्रोत कोड sexp.cप्रकट करता है कि AD का कार्यान्वयन कुछ संख्यात्मक परिशुद्धता भी खो देता है क्योंकि 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.;

u को बाद में sexp.c के शेष भाग में एक समान यादृच्छिक संख्या के रूप में पुनर्नवीनीकरण किया गया । अब तक, ऐसा प्रतीत होता है जैसे

  • आईटी कोड के लिए आसान है,
  • आईटी तेज है, और
  • आईटी और AD दोनों संभवतः संख्यात्मक सटीकता खो देते हैं।

मैं वास्तव में सराहना करूंगा कि अगर कोई यह समझा सके कि आर अभी भी एडी को केवल उपलब्ध विकल्प के रूप में क्यों लागू करता है rexp()


4
यादृच्छिक संख्या जनरेटर के साथ, "कोड करने में आसान" वास्तव में एक विचार नहीं है जब तक कि आप इसे नहीं कर रहे हैं! गति और सटीकता केवल दो विचार हैं। (समान जनरेटर के लिए, जनरेटर की अवधि भी है।) पुराने दिनों में, विज्ञापन तेज था। मेरे लिनक्स बॉक्स पर, AD लगभग 1/2 उस समय चलता है, जब आपका इन्वर्टर कार्य करता है, और मेरे लैपटॉप पर लगभग 2/3 समय। आप अधिक व्यापक समय के लिए माइक्रोबेनमार्क का उपयोग करना चाह सकते हैं।
जूलमैन

5
मेरा सुझाव है कि हम इसे माइग्रेट नहीं करेंगे। यह मुझे विषय लगता है।
अमीबा

1
यह देखते हुए कि मैं एक भी परिदृश्य के बारे में सोचने में असमर्थ हूं जिसमें rexp(n)अड़चन होगी, गति में अंतर परिवर्तन के लिए एक मजबूत तर्क नहीं है (कम से कम मेरे लिए)। मैं संख्यात्मक सटीकता के बारे में अधिक चिंतित हो सकता हूं, हालांकि यह मेरे लिए स्पष्ट नहीं है कि कौन सा अधिक संख्यात्मक रूप से विश्वसनीय होगा।
क्लिफ एबी

1
@amoeba मुझे लगता है कि "क्या फायदे होंगे ..." एक रीफ़्रेशिंग होगा जो यहां स्पष्ट रूप से विषय पर होगा, और किसी भी मौजूदा उत्तर को प्रभावित नहीं करेगा। मुझे लगता है कि "जिन लोगों ने R को करने का निर्णय लिया है ..." वास्तव में (a) एक सॉफ्टवेयर-विशिष्ट प्रश्न है, (b) को दस्तावेज़ीकरण या टेलीपैथी में या तो प्रमाण की आवश्यकता होती है, इसलिए यकीनन यहाँ पर विषय बंद हो सकता है। व्यक्तिगत रूप से मैं बल्कि प्रश्न को साइट के दायरे में अधिक स्पष्ट रूप से बनाने के लिए rephrased था, लेकिन मैं इसे बंद करने के लिए एक मजबूत पर्याप्त कारण के रूप में नहीं देखता हूं।
सिल्वरफिश

1
@amoeba मैंने जाना है। आश्वस्त नहीं कि मेरे सुझाए गए नए शीर्षक विशेष रूप से व्याकरणिक हैं, और शायद प्रश्न पाठ के कुछ अन्य भागों को बदलने के साथ कर सकते हैं। लेकिन मुझे आशा है कि यह अधिक स्पष्ट रूप से विषय पर है, कम से कम, और मुझे नहीं लगता कि यह अमान्य है या उत्तर के लिए परिवर्तनों की आवश्यकता है।
सिल्वरफिश

जवाबों:


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
मेरे लैपटॉप पर, समय ओपी के साथ इतनी बारीकी से मेल खाता है कि मुझे संदेह है कि हमारे पास एक ही मशीन (या कम से कम एक ही प्रोसेसर) है। लेकिन मुझे लगता है कि आपकी बात यहाँ गति का लाभ उठाया गया प्लेटफॉर्म पर निर्भर है, और न्यूनतम अंतर को देखते हुए, गति के संबंध में एक दूसरे पर कोई स्पष्ट लाभ नहीं है।
एबी एबी

4
क्या आप शायद microbenchmarkइसके बजाय प्रदर्शन कर सकते हैं ?
फायरबग

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

7

यह सिर्फ "एलगोरिदम एलजी: (लॉगरिथम विधि)" अनुभाग के तहत लेख को उद्धृत कर रहा है:

एक्स=-एलहेजी(आरजीहेएल(मैंआर))μμμयू

ऐसा लगता है कि लेखकों ने धीमी "लॉगरिथम" की इस "निर्माता" सीमा से बचने के लिए अन्य तरीकों का विकल्प चुना। शायद यह सवाल तब सबसे अच्छा होता है जब स्टैकओवरफ्लो में स्थानांतरित किया जाता है जहां आर के हिम्मत पर कोई भी व्यक्ति टिप्पणी कर सकता है।


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

यहाँ छवि विवरण दर्ज करें

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.