2014-09-05 19 views
6

Aşağıdaki kod sezgisel davranmaya görünmüyor: Bu kod çalıştırmaC++ 11 sınıfının <random> sınıfındaki dağılımları, temeldeki jeneratörü nasıl dönüştürür?

#include <random> 
#include <iostream> 
using namespace std; 

int main() 
{ 

    mt19937 MyGenerator(40); 
    auto gauss = normal_distribution<double>(0,1); 
    auto linear = uniform_real_distribution<double>(0,1); 
    cout << gauss(MyGenerator) << endl; //line a 
    cout << linear(MyGenerator) << endl; //line b 
    cout << gauss(MyGenerator) << endl; 
} 

çıktı üretmesi

-0.816097 
0.705030 
0.303032. 

şimdi ise hatları a ve b emir verir

için, çıkış değişiklikleri takas edilir
0.644008 
0.338080 
-0.639501. 

Farklı dağılımlar tarafından üretildiğinden, ilk iki sayının artık farklı olduğu tamamen açıktır. Yine de, üçüncü sayı neden farklı? Sezgilerime göre dağılım, belirli bir aralıktaki rastgele sayıya eşlenen bir sayı c = MyGenerator() almalıdır. Rasgele sayı üreteci, dağıtım çağrısından sonraki sayıların sırasındaki bir sonraki sayıya işaret eder. Yani, üçüncü görüşmenin sonucu her iki durumda da aynı olmamalı mı?

başka gözlem: bir ileri dağılımları birine çağrı ekleme aslında aynı numaraları çoğaltmak görünüyor.

cevap

12

libstdC++ normal_distribution arasında 'ın uygulanması Marsaglia polar method kullanır. Bu yöntemle ilgili ilginç bir şey her geçiş iki sonuçları üretmek için URNG dan iki rasgele sayılar kullanır. (O reddi örnekleme, ama bazen çift sayıda kullanır, olasılıkla daha fazla kez) ve bir sonuç verir olduğunu

, dağıtım ilk çağrı iki URNG çağırır; Dağıtıma aşağıdaki çağrı URNG'yi çağırmaz, ancak kaydedilen ikinci sonucu döndürür.

Burada biraz reformasyon, bir extract from the source code var:

if (_M_saved_available) 
{ 
    _M_saved_available = false; 
    ret = _M_saved; 
} 
else 
{ 
    result_type x, y, r2; 
    do 
    { 
     x = result_type(2.0) * aurng() - 1.0; 
     y = result_type(2.0) * aurng() - 1.0; 
     r2 = x * x + y * y; 
    } 
    while (r2 > 1.0 || r2 == 0.0); 

    const result_type mult = std::sqrt(-2 * std::log(r2)/r2); 
    _M_saved = x * mult; 
    _M_saved_available = true; 
    ret = y * mult; 
} 
+0

I 'aurng (bulunan) 'daima GCC 6.1.0 sonsuz döngü neden 0, döner. Olmaması gereken –

+0

@ LiDong; URNG'nizin bozuk olduğunu gösterir. – ecatmur

+0

Ben http://stackoverflow.com/questions/38350743/infinite-loop-in-random-tcc-gcc-6-1-0-may-be-bug-in-armadillo –

8

dağıtım her değer için bir kez altta yatan jeneratör çağrı bir gereksinim yoktur. Bazı dağılımlar en iyi rastgele değerleri birleştirerek hesaplanır.

Örneğin GNU uygulanmasında, düzgün dağılım uygulamaya geçiş kez jeneratör __aurng çağrı

return (__aurng() * (__p.b() - __p.a())) + __p.a(); 

olduğu; Normal dağılımın çekirdeği ise, en az iki kere çağrılması durumunda:

do 
{ 
    __x = result_type(2.0) * __aurng() - 1.0; 
    __y = result_type(2.0) * __aurng() - 1.0; 
    __r2 = __x * __x + __y * __y; 
} 
while (__r2 > 1.0 || __r2 == 0.0); 

.

+0

... başka soru soruldu ve çağrı sayısı değişkendir. Yani normal + üniforma, altta yatan jeneratöre üç çağrı yapar, üniform + normal beş veya yedi çağrı yapar. – gnasher729