diff --git a/samples/blackmisc/samplesalgorithm.cpp b/samples/blackmisc/samplesalgorithm.cpp index 1d6952776..c52518c52 100644 --- a/samples/blackmisc/samplesalgorithm.cpp +++ b/samples/blackmisc/samplesalgorithm.cpp @@ -9,8 +9,11 @@ #include "samplesalgorithm.h" #include "blackmisc/algorithm.h" +#include "blackmisc/sequence.h" +#include "blackmisc/collection.h" #include #include +#include namespace BlackMiscTest { @@ -20,6 +23,41 @@ namespace BlackMiscTest */ int CSamplesAlgorithm::samples() { + BlackMisc::CSequence seq; + for (int i = 1; i <= 100; ++i) + { + seq.push_back(i); + } + const int samples = 200; + BlackMisc::CSequence means; + { + for (int i = 0; i < samples; ++i) + { + auto randoms = seq.randomElements(10); + means.push_back(std::accumulate(randoms.cbegin(), randoms.cend(), 0) / 10); + } + int mean = std::accumulate(means.cbegin(), means.cend(), 0) / samples; + int stdDev = std::sqrt(std::accumulate(means.cbegin(), means.cend(), 0, [ & ](int a, int n) { return a + (n - mean) * (n - mean); }) / samples); + qDebug() << "randomElements"; + qDebug() << "means:" << means; + qDebug() << "mean of the means:" << mean; + qDebug() << "std deviation of the means:" << stdDev; + } + means.clear(); + { + for (int i = 0; i < samples; ++i) + { + auto randoms = seq.sampleElements(10); + means.push_back(std::accumulate(randoms.cbegin(), randoms.cend(), 0) / 10); + } + int mean = std::accumulate(means.cbegin(), means.cend(), 0) / samples; + int stdDev = std::sqrt(std::accumulate(means.cbegin(), means.cend(), 0, [ & ](int a, int n) { return a + (n - mean) * (n - mean); }) / samples); + qDebug() << "sampleElements"; + qDebug() << "means:" << means; + qDebug() << "mean of the means:" << mean; + qDebug() << "std deviation of the means:" << stdDev; + } + QStringList src { "a1", "a2", "a3", "b1", "b2", "b3", "c1", "c2", "c3" }; std::random_shuffle(src.begin(), src.end()); qDebug() << src; diff --git a/src/blackmisc/algorithm.h b/src/blackmisc/algorithm.h index 7fbd36100..aa1a099cd 100644 --- a/src/blackmisc/algorithm.h +++ b/src/blackmisc/algorithm.h @@ -12,12 +12,75 @@ #ifndef BLACKMISC_ALGORITHM_H #define BLACKMISC_ALGORITHM_H +#include #include #include #include +#include namespace BlackMisc { + namespace Private + { + //! \private A high quality deterministic pseudo-random number generator. + //! \threadsafe + inline std::mt19937 &defaultRandomGenerator() + { + static QThreadStorage rng; + if (rng.hasLocalData()) { rng.setLocalData(std::mt19937(qrand())); } + return rng.localData(); + } + } + + /*! + * Use the random number generator rng to choose n elements from the range [in,end) and copy them to out. + */ + template + void copyRandomElements(ForwardIt in, ForwardIt end, OutputIt out, int n, Generator &&rng) + { + for (auto size = std::distance(in, end); in != end && n > 0; ++in, --size) + { + if (std::uniform_int_distribution<>(0, size - 1)(rng) < n) + { + *out++ = *in; + --n; + } + } + } + + /*! + * Randomly choose n elements from the range [in,end) and copy them to out. + */ + template + void copyRandomElements(ForwardIt in, ForwardIt end, OutputIt out, int n) + { + copyRandomElements(in, end, out, n, Private::defaultRandomGenerator()); + } + + /*! + * Split the range [in,end) into n equal chunks and use the random number generator rng to choose one element from each. + */ + template + void copySampleElements(ForwardIt in, ForwardIt end, OutputIt out, const int n, Generator &&rng) + { + for (const auto size = std::distance(in, end); in != end && n > 0; ) + { + const auto index = std::uniform_int_distribution<>(0, size / n - 1)(rng); + std::advance(in, index); + *out++ = *in; + std::advance(in, size / n - index); + } + } + + /*! + * Split the range [in,end) into n equal chunks and randomly choose one element from each. + */ + template + void copySampleElements(ForwardIt in, ForwardIt end, OutputIt out, int n) + { + copySampleElements(in, end, out, n, Private::defaultRandomGenerator()); + } + /*! * Topological sorting algorithm. * diff --git a/src/blackmisc/range.h b/src/blackmisc/range.h index 3d5da49eb..a17026a52 100644 --- a/src/blackmisc/range.h +++ b/src/blackmisc/range.h @@ -15,6 +15,7 @@ #include "blackmiscexport.h" #include "iterator.h" #include "predicates.h" +#include "algorithm.h" #include #include #include @@ -128,6 +129,22 @@ namespace BlackMisc return containsBy(BlackMisc::Predicates::MemberEqual(k0, v0, keysValues...)); } + //! Copy n elements from the container at random. + Derived randomElements(int n) const + { + Derived result; + BlackMisc::copyRandomElements(derived().begin(), derived().end(), std::inserter(result, result.end()), n); + return result; + } + + //! Copy n elements from the container, randomly selected but evenly distributed. + Derived sampleElements(int n) const + { + Derived result; + BlackMisc::copySampleElements(derived().begin(), derived().end(), std::inserter(result, result.end()), n); + return result; + } + private: Derived &derived() { return static_cast(*this); } const Derived &derived() const { return static_cast(*this); }