Interessante Frage.
Also habe ich mich gefragt, ob mit stören, wie die Verteilung Arbeiten von es ständig zurückzusetzen (das heißt die Verteilung bei jedem Aufruf von get_int_from_range Neuer) ich richtig Ergebnisse verteilt bekommen.
Ich habe Code geschrieben, um dies mit uniform_int_distribution
und poisson_distribution
zu testen. Es ist einfach genug, dies zu erweitern, um eine andere Distribution zu testen, wenn Sie es wünschen. Die Antwort scheint zu sein ja.
Boiler-Plate-Code:
#include <random>
#include <memory>
#include <chrono>
#include <utility>
typedef std::mt19937_64 engine_type;
inline size_t get_seed()
{ return std::chrono::system_clock::now().time_since_epoch().count(); }
engine_type& engine_singleton()
{
static std::unique_ptr<engine_type> ptr;
if (!ptr)
ptr.reset(new engine_type(get_seed()));
return *ptr;
}
// ------------------------------------------------------------------------
#include <cmath>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>
void plot_distribution(const std::vector<double>& D, size_t mass = 200)
{
const size_t n = D.size();
for (size_t i = 0; i < n; ++i)
{
printf("%02ld: %s\n", i,
std::string(static_cast<size_t>(D[i]*mass),'*').c_str());
}
}
double maximum_difference(const std::vector<double>& x, const std::vector<double>& y)
{
const size_t n = x.size();
double m = 0.0;
for (size_t i = 0; i < n; ++i)
m = std::max(m, std::abs(x[i]-y[i]));
return m;
}
-Code für die eigentlichen Tests:
#include <iostream>
#include <vector>
#include <cstdio>
#include <random>
#include <string>
#include <cmath>
void compare_uniform_distributions(int lo, int hi)
{
const size_t sample_size = 1e5;
// Initialize histograms
std::vector<double> H1(hi-lo+1, 0.0), H2(hi-lo+1, 0.0);
// Initialize distribution
auto U = std::uniform_int_distribution<int>(lo,hi);
// Count!
for (size_t i = 0; i < sample_size; ++i)
{
engine_type E(get_seed());
H1[ U(engine_singleton())-lo ] += 1.0;
H2[ U(E)-lo ] += 1.0;
}
// Normalize histograms to obtain "densities"
for (size_t i = 0; i < H1.size(); ++i)
{
H1[i] /= sample_size;
H2[i] /= sample_size;
}
printf("Engine singleton:\n"); plot_distribution(H1);
printf("Engine creation :\n"); plot_distribution(H2);
printf("Maximum difference: %.3f\n", maximum_difference(H1,H2));
std::cout<< std::string(50,'-') << std::endl << std::endl;
}
void compare_poisson_distributions(double mean)
{
const size_t sample_size = 1e5;
const size_t nbins = static_cast<size_t>(std::ceil(2*mean));
// Initialize histograms
std::vector<double> H1(nbins, 0.0), H2(nbins, 0.0);
// Initialize distribution
auto U = std::poisson_distribution<int>(mean);
// Count!
for (size_t i = 0; i < sample_size; ++i)
{
engine_type E(get_seed());
int u1 = U(engine_singleton());
int u2 = U(E);
if (u1 < nbins) H1[u1] += 1.0;
if (u2 < nbins) H2[u2] += 1.0;
}
// Normalize histograms to obtain "densities"
for (size_t i = 0; i < H1.size(); ++i)
{
H1[i] /= sample_size;
H2[i] /= sample_size;
}
printf("Engine singleton:\n"); plot_distribution(H1);
printf("Engine creation :\n"); plot_distribution(H2);
printf("Maximum difference: %.3f\n", maximum_difference(H1,H2));
std::cout<< std::string(50,'-') << std::endl << std::endl;
}
// ------------------------------------------------------------------------
int main()
{
compare_uniform_distributions(0, 25);
compare_poisson_distributions(12);
}
Run es here.
der Standard C++ Hat zu diesem Thema keine Garantie machen?
Nicht, dass ich weiß. Ich würde jedoch sagen, dass der Standard eine implizite Empfehlung abgibt, die Engine nicht jedes Mal neu zu erstellen. für jede Verteilung Distrib
, der Prototyp Distrib::operator()
nimmt eine Referenz URNG&
und nicht eine const Referenz. Dies ist erforderlich, verständlicherweise, weil der Motor brauchen könnte seinen internen Zustand zu aktualisieren, aber es bedeutet auch, dass Code suchen, wie dieses
auto U = std::uniform_int_distribution(0,10);
for (<something here>) U(engine_type());
lässt sich nicht kompilieren, die mir ein klarer Anreiz ist nicht Code wie diesen zu schreiben.
Ich bin sicher, es gibt viele Ratschläge da draußen, wie man die zufällige Bibliothek richtig verwendet. Es ist kompliziert, wenn Sie die Möglichkeit der Verwendung von random_device
s und ermöglicht determinis Seeding für Testzwecke zu handhaben, aber ich dachte, es nützlich sein könnte, meine eigene Empfehlung da draußen zu werfen:
es
#include <random>
#include <chrono>
#include <utility>
#include <functional>
inline size_t get_seed()
{ return std::chrono::system_clock::now().time_since_epoch().count(); }
template <class Distrib>
using generator_type = std::function< typename Distrib::result_type() >;
template <class Distrib, class Engine = std::mt19937_64, class... Args>
inline generator_type<Distrib> get_generator(Args&&... args)
{
return std::bind(Distrib(std::forward<Args>(args)...), Engine(get_seed()));
}
// ------------------------------------------------------------------------
#include <iostream>
int main()
{
auto U = get_generator<std::uniform_int_distribution<int>>(0,10);
std::cout<< U() << std::endl;
}
Run here. Hoffe das hilft!
BEARBEITEN Meine erste Empfehlung war ein Fehler, und ich entschuldige mich dafür; Wir können keine Singleton-Engine wie in den obigen Tests verwenden, weil dies bedeuten würde, dass zwei uniforme int-Verteilungen die gleiche zufällige Sequenz erzeugen würden. Stattdessen verlasse ich mich auf die Tatsache, dass std::bind
die neu erstellte Engine lokal in std::function
mit einem eigenen Seed kopiert, und dies ergibt das erwartete Verhalten; verschiedene Generatoren mit der gleichen Verteilung erzeugen unterschiedliche zufällige Sequenzen.
Woher bekommen Sie "selbst eine einfache Verteilung [...] wird normalerweise einen internen Status" von diesem Link? – Yakk
Aus praktischen Gründen, warum das Risiko eingehen? Mit nur einem Generator (statt einem neuen bei jedem Funktionsaufruf) ist bereits klar, was ist das Problem mit nur einer Distribution zu verwenden? Vielleicht funktioniert die andere Lösung auch einwandfrei, aber Sie haben bereits eine Lösung, die garantiert funktioniert. – deviantfan
@Yakk der _incipit_ von [Joseph Mansfields Antwort] (http://stackoverflow.com/a/16017949/3235496): "Eine Verteilung kann sehr gut und wird normalerweise einen Zustand haben" – manlio