#include "distributions.hpp" #include #include #include #include #include #include #include #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4189) // Disable some compiler warnings that come from fmt #endif #include #if defined(_MSC_VER) #pragma warning(pop) #endif /* @param bins: std::vector&, reference to vector of bins @param element: std::uint32_t, the number to insert - fractional values should be `floored` as to meet the fractional requirements of the program @param start: std::uint8_t, the beginning of the current window @param end: std::uint8_t, the end of the current window @return DistributionPair*: pointer to the bin element fits in */ DistributionPair* binarySearchBins(std::vector& bins, std::uint32_t element, std::uint8_t start, std::uint8_t end); /* @param min: uint32_t, the minvalue value of the first bin @param max: uint32_t, the maxvalue of the last bin @param howMany: uint32_t, the number of bins to create @return std::vector: a vector of bins initialized with count = 0 */ std::vector initializeBins(std::uint32_t min, std::uint32_t max, std::uint8_t howMany); DistributionPair* binarySearchBins(std::vector& bins, std::uint32_t element, std::uint8_t start, std::uint8_t end) { std::uint8_t mid = (end + start) / 2; DistributionPair* dP = &bins.at(mid); // Base cases: // 1. Our element is less than the minimum in the bins // 2. Our element is greater than maximum in the bins // 3. Our element belongs in the current bin if (end <= start || mid == bins.size() - 1 || (dP->minValue <= element && dP->maxValue >= element)) return dP; // RESCURSION!!! if (dP->maxValue < element) return binarySearchBins(bins, element, mid, end); return binarySearchBins(bins, element, start, mid); } std::vector initializeBins(std::uint32_t min, std::uint32_t max, std::uint8_t howMany) { std::vector bins; // The difference between each minValue and maxValue of each bin can be zero // if numBins = num. of elements std::uint32_t delta = static_cast( std::ceil((static_cast(max - min) / howMany) - 1)); for (std::uint32_t i = 0; i < howMany; i++) { std::uint32_t newMin = min; if (i) newMin = bins.at(i - 1).maxValue + 1; DistributionPair newBin(newMin, newMin + delta); bins.push_back(newBin); } return bins; } void plotDistribution(std::string title, const std::vector& distribution, const std::uint8_t maxPlotLineSize) { std::cout << fmt::format("{}", title) << std::endl; std::uint32_t maxCount = std::max_element( std::begin(distribution), std::end(distribution), [](const DistributionPair bin1, const DistributionPair bin2) { return bin1.count < bin2.count; }) ->count; std::uint32_t maxBinElementWidth = static_cast(ceil(log10(distribution.back().maxValue)) + 1); std::uint32_t binThreshold = maxCount / maxPlotLineSize; for (const DistributionPair bin : distribution) { std::cout << fmt::format("[{0:>{1}}, {2:>{3}}] {4:<{5}}", bin.minValue, maxBinElementWidth, bin.maxValue, maxBinElementWidth, std::string(bin.count / binThreshold, '*'), maxPlotLineSize) << std::endl; } } std::vector generateUniformDistribution(std::uint32_t howMany, std::uint32_t min, std::uint32_t max, std::uint8_t numberBins) { std::vector bins = initializeBins(min, max, numberBins); std::default_random_engine generator; std::uniform_int_distribution distribution(min, max); for (std::uint32_t i = 0; i < howMany; i++) { uint32_t randomElement = distribution(generator); binarySearchBins(bins, randomElement, 0, numberBins)->count++; } return bins; } std::vector generateNormalDistribution(std::uint32_t howMany, float mean, float stdev, std::uint8_t numberBins) { std::uint32_t min = static_cast(mean - 4 * stdev); std::uint32_t max = static_cast(mean + 4 * stdev); // Get the right number of bins if (((max - min) % numberBins > 0) && (((max - 1) - min) % numberBins == 0)) max -= 1; std::vector bins = initializeBins(min, max, numberBins); std::default_random_engine generator; std::normal_distribution distribution(mean, stdev); for (std::uint32_t i = 0; i < howMany; i++) { uint32_t randomElement = static_cast(distribution(generator)); binarySearchBins(bins, randomElement, 0, numberBins)->count++; } return bins; } std::vector generatePoissonDistribution(std::uint32_t howMany, std::uint8_t howOften, std::uint8_t numberBins) { std::vector bins = initializeBins(0, numberBins - 1, numberBins); std::default_random_engine generator; std::poisson_distribution distribution(howOften); for (std::uint32_t i = 0; i < howMany; i++) { uint32_t randomElement = distribution(generator); binarySearchBins(bins, randomElement, 0, numberBins)->count++; } return bins; }