2014-10-23 9 views
9

Podczas gdy losowe silniki są wymagane, aby podać taką samą sekwencję liczb na każdym kompilatorze. Przynajmniej niektóre z rozkładów losowych nie są wymagane, tylko wymagają spełnienia progów statystycznych i probabilistycznych. Jako przykład:C++ 11 odtwarzalność losowej dystrybucji krzyżówki/biblioteki standardowej

#include <random> 
#include <iostream> 

int main() { 
    std::mt19937 foo; 
    std::uniform_int_distribution<int> bar(0, 1000); 

    for (int i=0; i<99; ++i) { 
    bar(foo); 
    } 

    std::cout << bar(foo) << std::endl; 

    return 0; 
} 

Które będzie drukować 808 gdy kompilowany przeciwko (moja wersja) libstdC++ i 89 gdy kompilowany przeciwko libC++.

Która ze standardowych dostarczonych funkcji dystrybucji, jeśli istnieje, zapewnia spójne wyniki niezależnie od tego, jakie środowisko jest zgodne z podanymi?

+6

89 jest o wiele bardziej losowy niż 808, uwierz mi. ;-) –

+1

Oczywiście nie tak losowo jak 4. https://xkcd.com/221/ – OmnipotentEntity

+0

+1 za wdzięczną obsługę mojego dowcipu. :-) –

Odpowiedz

5

Niestety, od N3936 (wersja ostateczna C++ 14) żaden standard dostarczania rozkładu losowego nie wymaga takiego rozwiązania. I łatwo zrozumieć, dlaczego. Istnieje wiele ważnych sposobów pisania funkcji dystrybucji. Niektóre lepsze niż inne. A algorytmy dla czegoś tak podstawowego, jak rozkład normalny, stają się coraz lepsze i są przedmiotem aktywnych badań. Upoważnienie do korzystania z jednego niepotrzebnie utrudniłoby wdrażanie przyszłych algorytmów.

Na szczęście można pisać własne. Specyfikacja nagłówków różnych klas dystrybucji znajduje się w § 26.5.8. Ale nie ma powodu, dla którego twoja musi koniecznie podążać za tą strukturą.

(Uwaga: nie testowałem dokładnie tego kodu i może występować złe zachowanie w przypadku niektórych silników lub z nadmiarem, chociaż dołożyłem pewnych starań, aby uniknąć tego ostatniego, jest to raczej przykład ilustrujący niż kanoniczna źródłem niesamowitej rozkładzie równomiernym. w takiej sytuacji, jeśli okaże się, nic złego się z nim, daj mi znać w komentarzach, a ja chętnie je poprawić.)

#include <random> 
#include <tuple> 
#include <iostream> 

template<class IntType = int> 
class my_uniform_int_distribution { 
public: 
    // types 
    typedef IntType result_type; 
    typedef std::pair<int, int> param_type; 

    // constructors and reset functions 
    explicit my_uniform_int_distribution(IntType a = 0, IntType b = std::numeric_limits<IntType>::max()); 
    explicit my_uniform_int_distribution(const param_type& parm); 
    void reset(); 

    // generating functions 
    template<class URNG> 
    result_type operator()(URNG& g); 
    template<class URNG> 
    result_type operator()(URNG& g, const param_type& parm); 

    // property functions 
    result_type a() const; 
    result_type b() const; 
    param_type param() const; 
    void param(const param_type& parm); 
    result_type min() const; 
    result_type max() const; 

private: 
    typedef typename std::make_unsigned<IntType>::type diff_type; 

    IntType lower; 
    IntType upper; 
}; 

template<class IntType> 
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(IntType a, IntType b) { 
    param({a, b}); 
} 

template<class IntType> 
my_uniform_int_distribution<IntType>::my_uniform_int_distribution(const param_type& parm) { 
    param(parm); 
} 

template<class IntType> 
void my_uniform_int_distribution<IntType>::reset() {} 

template<class IntType> 
template<class URNG> 
auto my_uniform_int_distribution<IntType>::operator()(URNG& g) -> result_type { 
    return operator()(g, param()); 
} 

template<class IntType> 
template<class URNG> 
auto my_uniform_int_distribution<IntType>::operator()(URNG& g, const param_type& parm) -> result_type { 
    diff_type diff = (diff_type)parm.second - (diff_type)parm.first + 1; 
    if (diff == 0) // If the +1 overflows we are using the full range, just return g() 
    return g(); 

    diff_type badDistLimit = std::numeric_limits<diff_type>::max()/diff; 
    do { 
    diff_type generatedRand = g(); 

    if (generatedRand/diff < badDistLimit) 
     return (IntType)((generatedRand % diff) + (diff_type)parm.first); 
    } while (true); 
} 

template<class IntType> 
auto my_uniform_int_distribution<IntType>::a() const -> result_type { 
    return lower; 
} 

template<class IntType> 
auto my_uniform_int_distribution<IntType>::b() const -> result_type { 
    return upper; 
} 

template<class IntType> 
auto my_uniform_int_distribution<IntType>::param() const -> param_type { 
    return {lower, upper}; 
} 

template<class IntType> 
void my_uniform_int_distribution<IntType>::param(const param_type& parm) { 
    std::tie(lower, upper) = parm; 
    if (upper < lower) 
    throw std::exception(); 
} 

template<class IntType> 
auto my_uniform_int_distribution<IntType>::min() const -> result_type { 
    return lower; 
} 

template<class IntType> 
auto my_uniform_int_distribution<IntType>::max() const -> result_type { 
    return upper; 
} 

int main() { 
    std::mt19937 foo; 
    my_uniform_int_distribution<int> bar(0,1000); 

    for (int i=0; i<99; ++i) { 
    bar(foo); 
    } 

    std::cout << bar(foo) << std::endl; 

    return 0; 
} 

Kod ten wypisuje na 490 wszystkie platformy, które testowałem.

+1

N3797 nie jest wersją ostateczną dla C++ 14. N3936 to. –

+0

Naprawiono, dzięki za heads up. – OmnipotentEntity

+0

@ T.C .: Technicznie rzecz biorąc, N4141 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4139.html). Różnica jest tylko garstką poprawek redakcyjnych. I żaden z nich nie jest publicznie dostępny. :-( –