This repository has been archived on 2020-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
Files
Indoor/math/random/DrawList.h
2018-10-25 11:50:12 +02:00

180 lines
4.4 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* © Copyright 2014 Urheberrechtshinweis
* Alle Rechte vorbehalten / All Rights Reserved
*
* Programmcode ist urheberrechtlich geschuetzt.
* Das Urheberrecht liegt, soweit nicht ausdruecklich anders gekennzeichnet, bei Frank Ebner.
* Keine Verwendung ohne explizite Genehmigung.
* (vgl. § 106 ff UrhG / § 97 UrhG)
*/
#ifndef K_MATH_RANDOM_DRAWLIST_H
#define K_MATH_RANDOM_DRAWLIST_H
#include <vector>
/**
* add entries to a list and be able to draw from them depending oh their probability
*/
namespace Random {
/**
* this class represents one entry within a DrawList.
* such an entry consists of userData denoted by the template argument
* and a probability.
*/
template <typename Entry> struct DrawListEntry {
template <typename> friend class DrawList;
friend class DrawList_Cumulative_Test;
public:
/** the user data behind this entry */
Entry entry;
/** this entry's probability */
double probability;
private:
/** the cumulative probability, tracked among all entries within the DrawList */
double cumulativeProbability;
public:
/** empty ctor */
DrawListEntry() :
entry(), probability(0), cumulativeProbability(0) {;}
/** ctor */
DrawListEntry(const Entry& entry, double probability) :
entry(entry), probability(probability), cumulativeProbability(0) {;}
/** compare this entrie's summed probability with the given probability */
bool operator < (double probability) const {return cumulativeProbability < probability;}
private:
/** ctor */
DrawListEntry(Entry& e, double probability, double summedProbability) :
entry(entry), probability(probability), cumulativeProbability(summedProbability) {;}
};
/**
* a DrawList is a data-structure containing entries that have a
* probability assigned to them.
* using the draw() function one may draw from these entries according
* to their assigned probability in O(log(n))
*/
template <typename Entry> class DrawList {
friend class DrawList_Cumulative_Test;
public:
/** ctor */
DrawList() : sumValid(false) {;}
/** append a new entry to the end of the list */
void push_back(const Entry& entry, const double probability) {
const DrawListEntry<Entry> dle(entry, probability);
entries.push_back(dle);
sumValid = false;
}
/** change the entry at the given position. ensure the vector is resized()! */
void set(const uint32_t idx, const Entry& entry, const double probability) {
entries[idx].entry = entry;
entries[idx].probability = probability;
sumValid = false;
}
/** resize the underlying vector to hold the given number of entries */
void resize(const uint32_t numEntries) {entries.resize(numEntries);}
/** clear all currently inserted entries */
void clear() {entries.clear();}
/** does the underlying vector contain any entries? */
bool empty() const {return entries.empty();}
/** the number of entries */
uint32_t size() const {return entries.size();}
/** draw a random entry from the draw list */
Entry& draw() {
// random value between [0, 1]
double rand01 = double(rand()) / double(RAND_MAX);
return draw(rand01);
}
/** draw an entry according to the given probability [0,1] */
Entry& draw(double rand01) {
// sanity check
assert(!entries.empty());
ensureCumulativeProbability();
// random value between [0, summedProbability]
// (this prevents us from norming the list to [0, 1])
double rand = rand01 * entries[entries.size()-1].cumulativeProbability;
// binary search for the matching entry O(log(n))
auto tmp = std::lower_bound(entries.begin(), entries.end(), rand);
return (*tmp).entry;
// // O(n)
// for (DrawListEntry<Entry>& dle : entries) {
// if (dle.cumulativeProbability > rand) {return dle.entry;}
// }
// return entries[this->size()-1].entry;
}
private:
/** ensure the cumulative probability is valid. if not -> calculate it */
void ensureCumulativeProbability() {
// already valid?
if (sumValid) {return;}
// calculate the cumulative probability
double sum = 0;
for (DrawListEntry<Entry>& dle : entries) {
sum += dle.probability;
dle.cumulativeProbability = sum;
}
// the sum is now valid
sumValid = true;
}
private:
/** all entries within the DrawList */
std::vector<DrawListEntry<Entry>> entries;
/** track wether the summedProbability is valid or not */
bool sumValid;
};
}
#endif // K_MATH_RANDOM_DRAWLIST_H