chore: import upstream snapshot with attribution
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
Awesome CI Workflow / Code Formatter (push) Has been cancelled
Awesome CI Workflow / Compile checks (macOS-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (ubuntu-latest) (push) Has been cancelled
Awesome CI Workflow / Compile checks (windows-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
# If necessary, use the RELATIVE flag, otherwise each source file may be listed
|
||||
# with full pathname. RELATIVE may makes it easier to extract an executable name
|
||||
# automatically.
|
||||
file( GLOB APP_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp )
|
||||
# file( GLOB APP_SOURCES ${CMAKE_SOURCE_DIR}/*.c )
|
||||
# AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR} APP_SOURCES)
|
||||
foreach( testsourcefile ${APP_SOURCES} )
|
||||
# I used a simple string replace, to cut off .cpp.
|
||||
string( REPLACE ".cpp" "" testname ${testsourcefile} )
|
||||
add_executable( ${testname} ${testsourcefile} )
|
||||
|
||||
set_target_properties(${testname} PROPERTIES LINKER_LANGUAGE CXX)
|
||||
if(OpenMP_CXX_FOUND)
|
||||
target_link_libraries(${testname} OpenMP::OpenMP_CXX)
|
||||
endif()
|
||||
install(TARGETS ${testname} DESTINATION "bin/probability")
|
||||
|
||||
endforeach( testsourcefile ${APP_SOURCES} )
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Addition rule of probabilities
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* calculates the probability of the independent events A or B for independent
|
||||
* events
|
||||
* \parama [in] A probability of event A
|
||||
* \parama [in] B probability of event B
|
||||
* \returns probability of A and B
|
||||
*/
|
||||
double addition_rule_independent(double A, double B) {
|
||||
return (A + B) - (A * B);
|
||||
}
|
||||
|
||||
/** Calculates the probability of the events A or B for dependent events
|
||||
* note that if value of B_given_A is unknown, use chainrule to find it
|
||||
* \parama [in] A probability of event A
|
||||
* \parama [in] B probability of event B
|
||||
* \parama [in] B_given_A probability of event B condition A
|
||||
* \returns probability of A and B
|
||||
*/
|
||||
double addition_rule_dependent(double A, double B, double B_given_A) {
|
||||
return (A + B) - (A * B_given_A);
|
||||
}
|
||||
|
||||
/** Main function */
|
||||
int main() {
|
||||
double A = 0.5;
|
||||
double B = 0.25;
|
||||
double B_given_A = 0.05;
|
||||
|
||||
std::cout << "independent P(A or B) = " << addition_rule_independent(A, B)
|
||||
<< std::endl;
|
||||
|
||||
std::cout << "dependent P(A or B) = "
|
||||
<< addition_rule_dependent(A, B, B_given_A) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Bayes' theorem](https://en.wikipedia.org/wiki/Bayes%27_theorem)
|
||||
*
|
||||
* Bayes' theorem allows one to find \f$P(A|B)\f$ given \f$P(B|A)\f$ or
|
||||
* \f$P(B|A)\f$ given \f$P(A|B)\f$ and \f$P(A)\f$ and \f$P(B)\f$.\n
|
||||
* Note that \f$P(A|B)\f$ is read 'The probability of A given that the event B
|
||||
* has occured'.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
/** returns P(A|B)
|
||||
*/
|
||||
double bayes_AgivenB(double BgivenA, double A, double B) {
|
||||
return (BgivenA * A) / B;
|
||||
}
|
||||
|
||||
/** returns P(B|A)
|
||||
*/
|
||||
double bayes_BgivenA(double AgivenB, double A, double B) {
|
||||
return (AgivenB * B) / A;
|
||||
}
|
||||
|
||||
/** Main function
|
||||
*/
|
||||
int main() {
|
||||
double A = 0.01;
|
||||
double B = 0.1;
|
||||
double BgivenA = 0.9;
|
||||
double AgivenB = bayes_AgivenB(BgivenA, A, B);
|
||||
std::cout << "A given B = " << AgivenB << std::endl;
|
||||
std::cout << "B given A = " << bayes_BgivenA(AgivenB, A, B) << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Binomial
|
||||
* distribution](https://en.wikipedia.org/wiki/Binomial_distribution) example
|
||||
*
|
||||
* The binomial distribution models the number of
|
||||
* successes in a sequence of n independent events
|
||||
*
|
||||
* Summary of variables used:
|
||||
* * n : number of trials
|
||||
* * p : probability of success
|
||||
* * x : desired successes
|
||||
*/
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
/** finds the expected value of a binomial distribution
|
||||
* \param [in] n
|
||||
* \param [in] p
|
||||
* \returns \f$\mu=np\f$
|
||||
*/
|
||||
double binomial_expected(double n, double p) { return n * p; }
|
||||
|
||||
/** finds the variance of the binomial distribution
|
||||
* \param [in] n
|
||||
* \param [in] p
|
||||
* \returns \f$\sigma^2 = n\cdot p\cdot (1-p)\f$
|
||||
*/
|
||||
double binomial_variance(double n, double p) { return n * p * (1 - p); }
|
||||
|
||||
/** finds the standard deviation of the binomial distribution
|
||||
* \param [in] n
|
||||
* \param [in] p
|
||||
* \returns \f$\sigma = \sqrt{\sigma^2} = \sqrt{n\cdot p\cdot (1-p)}\f$
|
||||
*/
|
||||
double binomial_standard_deviation(double n, double p) {
|
||||
return std::sqrt(binomial_variance(n, p));
|
||||
}
|
||||
|
||||
/** Computes n choose r
|
||||
* \param [in] n
|
||||
* \param [in] r
|
||||
* \returns \f$\displaystyle {n\choose r} =
|
||||
* \frac{n!}{r!(n-r)!} = \frac{n\times(n-1)\times(n-2)\times\cdots(n-r)}{r!}
|
||||
* \f$
|
||||
*/
|
||||
double nCr(double n, double r) {
|
||||
double numerator = n;
|
||||
double denominator = r;
|
||||
|
||||
for (int i = n - 1; i >= ((n - r) + 1); i--) {
|
||||
numerator *= i;
|
||||
}
|
||||
|
||||
for (int i = 1; i < r; i++) {
|
||||
denominator *= i;
|
||||
}
|
||||
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
/** calculates the probability of exactly x successes
|
||||
* \returns \f$\displaystyle P(n,p,x) = {n\choose x} p^x (1-p)^{n-x}\f$
|
||||
*/
|
||||
double binomial_x_successes(double n, double p, double x) {
|
||||
return nCr(n, x) * std::pow(p, x) * std::pow(1 - p, n - x);
|
||||
}
|
||||
|
||||
/** calculates the probability of a result within a range (inclusive, inclusive)
|
||||
* \returns \f$\displaystyle \left.P(n,p)\right|_{x_0}^{x_1} =
|
||||
* \sum_{i=x_0}^{x_1} P(i)
|
||||
* =\sum_{i=x_0}^{x_1} {n\choose i} p^i (1-p)^{n-i}\f$
|
||||
*/
|
||||
double binomial_range_successes(double n, double p, double lower_bound,
|
||||
double upper_bound) {
|
||||
double probability = 0;
|
||||
for (int i = lower_bound; i <= upper_bound; i++) {
|
||||
probability += nCr(n, i) * std::pow(p, i) * std::pow(1 - p, n - i);
|
||||
}
|
||||
return probability;
|
||||
}
|
||||
|
||||
/** main function */
|
||||
int main() {
|
||||
std::cout << "expected value : " << binomial_expected(100, 0.5)
|
||||
<< std::endl;
|
||||
|
||||
std::cout << "variance : " << binomial_variance(100, 0.5) << std::endl;
|
||||
|
||||
std::cout << "standard deviation : "
|
||||
<< binomial_standard_deviation(100, 0.5) << std::endl;
|
||||
|
||||
std::cout << "exactly 30 successes : " << binomial_x_successes(100, 0.5, 30)
|
||||
<< std::endl;
|
||||
|
||||
std::cout << "45 or more successes : "
|
||||
<< binomial_range_successes(100, 0.5, 45, 100) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Exponential
|
||||
* Distribution](https://en.wikipedia.org/wiki/Exponential_distribution)
|
||||
*
|
||||
* The exponential distribution is used to model
|
||||
* events occuring between a Poisson process like radioactive decay.
|
||||
*
|
||||
* \f[P(x, \lambda) = \lambda e^{-\lambda x}\f]
|
||||
*
|
||||
* Summary of variables used:
|
||||
* \f$\lambda\f$ : rate parameter
|
||||
*/
|
||||
|
||||
#include <cassert> // For assert
|
||||
#include <cmath> // For std::pow
|
||||
#include <iostream> // For I/O operation
|
||||
#include <stdexcept> // For std::invalid_argument
|
||||
#include <string> // For std::string
|
||||
|
||||
/**
|
||||
* @namespace probability
|
||||
* @brief Probability algorithms
|
||||
*/
|
||||
namespace probability {
|
||||
/**
|
||||
* @namespace exponential_dist
|
||||
* @brief Functions for the [Exponential
|
||||
* Distribution](https://en.wikipedia.org/wiki/Exponential_distribution)
|
||||
* algorithm implementation
|
||||
*/
|
||||
namespace geometric_dist {
|
||||
/**
|
||||
* @brief the expected value of the exponential distribution
|
||||
* @returns \f[\mu = \frac{1}{\lambda}\f]
|
||||
*/
|
||||
double exponential_expected(double lambda) {
|
||||
if (lambda <= 0) {
|
||||
throw std::invalid_argument("lambda must be greater than 0");
|
||||
}
|
||||
return 1 / lambda;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the variance of the exponential distribution
|
||||
* @returns \f[\sigma^2 = \frac{1}{\lambda^2}\f]
|
||||
*/
|
||||
double exponential_var(double lambda) {
|
||||
if (lambda <= 0) {
|
||||
throw std::invalid_argument("lambda must be greater than 0");
|
||||
}
|
||||
return 1 / pow(lambda, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the standard deviation of the exponential distribution
|
||||
* @returns \f[\sigma = \frac{1}{\lambda}\f]
|
||||
*/
|
||||
double exponential_std(double lambda) {
|
||||
if (lambda <= 0) {
|
||||
throw std::invalid_argument("lambda must be greater than 0");
|
||||
}
|
||||
return 1 / lambda;
|
||||
}
|
||||
} // namespace geometric_dist
|
||||
} // namespace probability
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
double lambda_1 = 1;
|
||||
double expected_1 = 1;
|
||||
double var_1 = 1;
|
||||
double std_1 = 1;
|
||||
|
||||
double lambda_2 = 2;
|
||||
double expected_2 = 0.5;
|
||||
double var_2 = 0.25;
|
||||
double std_2 = 0.5;
|
||||
|
||||
double lambda_3 = 3;
|
||||
double expected_3 = 0.333333;
|
||||
double var_3 = 0.111111;
|
||||
double std_3 = 0.333333;
|
||||
|
||||
double lambda_4 = 0; // Test 0
|
||||
double lambda_5 = -2.3; // Test negative value
|
||||
|
||||
const float threshold = 1e-3f;
|
||||
|
||||
std::cout << "Test for lambda = 1 \n";
|
||||
assert(
|
||||
std::abs(expected_1 - probability::geometric_dist::exponential_expected(
|
||||
lambda_1)) < threshold);
|
||||
assert(std::abs(var_1 - probability::geometric_dist::exponential_var(
|
||||
lambda_1)) < threshold);
|
||||
assert(std::abs(std_1 - probability::geometric_dist::exponential_std(
|
||||
lambda_1)) < threshold);
|
||||
std::cout << "ALL TEST PASSED\n\n";
|
||||
|
||||
std::cout << "Test for lambda = 2 \n";
|
||||
assert(
|
||||
std::abs(expected_2 - probability::geometric_dist::exponential_expected(
|
||||
lambda_2)) < threshold);
|
||||
assert(std::abs(var_2 - probability::geometric_dist::exponential_var(
|
||||
lambda_2)) < threshold);
|
||||
assert(std::abs(std_2 - probability::geometric_dist::exponential_std(
|
||||
lambda_2)) < threshold);
|
||||
std::cout << "ALL TEST PASSED\n\n";
|
||||
|
||||
std::cout << "Test for lambda = 3 \n";
|
||||
assert(
|
||||
std::abs(expected_3 - probability::geometric_dist::exponential_expected(
|
||||
lambda_3)) < threshold);
|
||||
assert(std::abs(var_3 - probability::geometric_dist::exponential_var(
|
||||
lambda_3)) < threshold);
|
||||
assert(std::abs(std_3 - probability::geometric_dist::exponential_std(
|
||||
lambda_3)) < threshold);
|
||||
std::cout << "ALL TEST PASSED\n\n";
|
||||
|
||||
std::cout << "Test for lambda = 0 \n";
|
||||
try {
|
||||
probability::geometric_dist::exponential_expected(lambda_4);
|
||||
probability::geometric_dist::exponential_var(lambda_4);
|
||||
probability::geometric_dist::exponential_std(lambda_4);
|
||||
} catch (std::invalid_argument& err) {
|
||||
assert(std::string(err.what()) == "lambda must be greater than 0");
|
||||
}
|
||||
std::cout << "ALL TEST PASSED\n\n";
|
||||
|
||||
std::cout << "Test for lambda = -2.3 \n";
|
||||
try {
|
||||
probability::geometric_dist::exponential_expected(lambda_5);
|
||||
probability::geometric_dist::exponential_var(lambda_5);
|
||||
probability::geometric_dist::exponential_std(lambda_5);
|
||||
} catch (std::invalid_argument& err) {
|
||||
assert(std::string(err.what()) == "lambda must be greater than 0");
|
||||
}
|
||||
std::cout << "ALL TEST PASSED\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @return 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
test(); // Self test implementation
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Geometric
|
||||
* Distribution](https://en.wikipedia.org/wiki/Geometric_distribution)
|
||||
*
|
||||
* @details
|
||||
* The geometric distribution models the experiment of doing Bernoulli trials
|
||||
* until a sucess was observed. There are two formulations of the geometric
|
||||
* distribution: 1) The probability distribution of the number X of Bernoulli
|
||||
* trials needed to get one success, supported on the set { 1, 2, 3, ... } 2)
|
||||
* The probability distribution of the number Y = X − 1 of failures before the
|
||||
* first success, supported on the set { 0, 1, 2, 3, ... } Here, the first one
|
||||
* is implemented.
|
||||
*
|
||||
* Common variables used:
|
||||
* p - The success probability
|
||||
* k - The number of tries
|
||||
*
|
||||
* @author [Domenic Zingsheim](https://github.com/DerAndereDomenic)
|
||||
*/
|
||||
|
||||
#include <cassert> /// for assert
|
||||
#include <cmath> /// for math functions
|
||||
#include <cstdint> /// for fixed size data types
|
||||
#include <ctime> /// for time to initialize rng
|
||||
#include <iostream> /// for std::cout
|
||||
#include <limits> /// for std::numeric_limits
|
||||
#include <random> /// for random numbers
|
||||
#include <vector> /// for std::vector
|
||||
|
||||
/**
|
||||
* @namespace probability
|
||||
* @brief Probability algorithms
|
||||
*/
|
||||
namespace probability {
|
||||
/**
|
||||
* @namespace geometric_dist
|
||||
* @brief Functions for the [Geometric
|
||||
* Distribution](https://en.wikipedia.org/wiki/Geometric_distribution) algorithm
|
||||
* implementation
|
||||
*/
|
||||
namespace geometric_dist {
|
||||
/**
|
||||
* @brief Returns a random number between [0,1]
|
||||
* @returns A uniformly distributed random number between 0 (included) and 1
|
||||
* (included)
|
||||
*/
|
||||
float generate_uniform() {
|
||||
return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A class to model the geometric distribution
|
||||
*/
|
||||
class geometric_distribution {
|
||||
private:
|
||||
float p; ///< The succes probability p
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor for the geometric distribution
|
||||
* @param p The success probability
|
||||
*/
|
||||
explicit geometric_distribution(const float& p) : p(p) {}
|
||||
|
||||
/**
|
||||
* @brief The expected value of a geometrically distributed random variable
|
||||
* X
|
||||
* @returns E[X] = 1/p
|
||||
*/
|
||||
float expected_value() const { return 1.0f / p; }
|
||||
|
||||
/**
|
||||
* @brief The variance of a geometrically distributed random variable X
|
||||
* @returns V[X] = (1 - p) / p^2
|
||||
*/
|
||||
float variance() const { return (1.0f - p) / (p * p); }
|
||||
|
||||
/**
|
||||
* @brief The standard deviation of a geometrically distributed random
|
||||
* variable X
|
||||
* @returns \sigma = \sqrt{V[X]}
|
||||
*/
|
||||
float standard_deviation() const { return std::sqrt(variance()); }
|
||||
|
||||
/**
|
||||
* @brief The probability density function
|
||||
* @details As we use the first definition of the geometric series (1),
|
||||
* we are doing k - 1 failed trials and the k-th trial is a success.
|
||||
* @param k The number of trials to observe the first success in [1,\infty)
|
||||
* @returns A number between [0,1] according to p * (1-p)^{k-1}
|
||||
*/
|
||||
float probability_density(const uint32_t& k) const {
|
||||
return std::pow((1.0f - p), static_cast<float>(k - 1)) * p;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The cumulative distribution function
|
||||
* @details The sum of all probabilities up to (and including) k trials.
|
||||
* Basically CDF(k) = P(x <= k)
|
||||
* @param k The number of trials in [1,\infty)
|
||||
* @returns The probability to have success within k trials
|
||||
*/
|
||||
float cumulative_distribution(const uint32_t& k) const {
|
||||
return 1.0f - std::pow((1.0f - p), static_cast<float>(k));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The inverse cumulative distribution function
|
||||
* @details This functions answers the question: Up to how many trials are
|
||||
* needed to have success with a probability of cdf? The exact floating
|
||||
* point value is reported.
|
||||
* @param cdf The probability in [0,1]
|
||||
* @returns The number of (exact) trials.
|
||||
*/
|
||||
float inverse_cumulative_distribution(const float& cdf) const {
|
||||
return std::log(1.0f - cdf) / std::log(1.0f - p);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Generates a (discrete) sample according to the geometrical
|
||||
* distribution
|
||||
* @returns A geometrically distributed number in [1,\infty)
|
||||
*/
|
||||
uint32_t draw_sample() const {
|
||||
float uniform_sample = generate_uniform();
|
||||
return static_cast<uint32_t>(
|
||||
inverse_cumulative_distribution(uniform_sample)) +
|
||||
1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This function computes the probability to have success in a given
|
||||
* range of tries
|
||||
* @details Computes P(min_tries <= x <= max_tries).
|
||||
* Can be used to calculate P(x >= min_tries) by not passing a second
|
||||
* argument. Can be used to calculate P(x <= max_tries) by passing 1 as the
|
||||
* first argument
|
||||
* @param min_tries The minimum number of tries in [1,\infty) (inclusive)
|
||||
* @param max_tries The maximum number of tries in [min_tries, \infty)
|
||||
* (inclusive)
|
||||
* @returns The probability of having success within a range of tries
|
||||
* [min_tries, max_tries]
|
||||
*/
|
||||
float range_tries(const uint32_t& min_tries = 1,
|
||||
const uint32_t& max_tries =
|
||||
std::numeric_limits<uint32_t>::max()) const {
|
||||
float cdf_lower = cumulative_distribution(min_tries - 1);
|
||||
float cdf_upper = max_tries == std::numeric_limits<uint32_t>::max()
|
||||
? 1.0f
|
||||
: cumulative_distribution(max_tries);
|
||||
return cdf_upper - cdf_lower;
|
||||
}
|
||||
};
|
||||
} // namespace geometric_dist
|
||||
} // namespace probability
|
||||
|
||||
/**
|
||||
* @brief Tests the sampling method of the geometric distribution
|
||||
* @details Draws 1000000 random samples and estimates mean and variance
|
||||
* These should be close to the expected value and variance of the given
|
||||
* distribution to pass.
|
||||
* @param dist The distribution to test
|
||||
*/
|
||||
void sample_test(
|
||||
const probability::geometric_dist::geometric_distribution& dist) {
|
||||
uint32_t n_tries = 1000000;
|
||||
std::vector<float> tries;
|
||||
tries.resize(n_tries);
|
||||
|
||||
float mean = 0.0f;
|
||||
for (uint32_t i = 0; i < n_tries; ++i) {
|
||||
tries[i] = static_cast<float>(dist.draw_sample());
|
||||
mean += tries[i];
|
||||
}
|
||||
|
||||
mean /= static_cast<float>(n_tries);
|
||||
|
||||
float var = 0.0f;
|
||||
for (uint32_t i = 0; i < n_tries; ++i) {
|
||||
var += (tries[i] - mean) * (tries[i] - mean);
|
||||
}
|
||||
|
||||
// Unbiased estimate of variance
|
||||
var /= static_cast<float>(n_tries - 1);
|
||||
|
||||
std::cout << "This value should be near " << dist.expected_value() << ": "
|
||||
<< mean << std::endl;
|
||||
std::cout << "This value should be near " << dist.variance() << ": " << var
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @returns void
|
||||
*/
|
||||
static void test() {
|
||||
probability::geometric_dist::geometric_distribution dist(0.3);
|
||||
|
||||
const float threshold = 1e-3f;
|
||||
|
||||
std::cout << "Starting tests for p = 0.3..." << std::endl;
|
||||
assert(std::abs(dist.expected_value() - 3.33333333f) < threshold);
|
||||
assert(std::abs(dist.variance() - 7.77777777f) < threshold);
|
||||
assert(std::abs(dist.standard_deviation() - 2.788866755) < threshold);
|
||||
assert(std::abs(dist.probability_density(5) - 0.07203) < threshold);
|
||||
assert(std::abs(dist.cumulative_distribution(6) - 0.882351) < threshold);
|
||||
assert(std::abs(dist.inverse_cumulative_distribution(
|
||||
dist.cumulative_distribution(8)) -
|
||||
8) < threshold);
|
||||
assert(std::abs(dist.range_tries() - 1.0f) < threshold);
|
||||
assert(std::abs(dist.range_tries(3) - 0.49f) < threshold);
|
||||
assert(std::abs(dist.range_tries(5, 11) - 0.2203267f) < threshold);
|
||||
std::cout << "All tests passed" << std::endl;
|
||||
sample_test(dist);
|
||||
|
||||
dist = probability::geometric_dist::geometric_distribution(0.5f);
|
||||
|
||||
std::cout << "Starting tests for p = 0.5..." << std::endl;
|
||||
assert(std::abs(dist.expected_value() - 2.0f) < threshold);
|
||||
assert(std::abs(dist.variance() - 2.0f) < threshold);
|
||||
assert(std::abs(dist.standard_deviation() - 1.4142135f) < threshold);
|
||||
assert(std::abs(dist.probability_density(5) - 0.03125) < threshold);
|
||||
assert(std::abs(dist.cumulative_distribution(6) - 0.984375) < threshold);
|
||||
assert(std::abs(dist.inverse_cumulative_distribution(
|
||||
dist.cumulative_distribution(8)) -
|
||||
8) < threshold);
|
||||
assert(std::abs(dist.range_tries() - 1.0f) < threshold);
|
||||
assert(std::abs(dist.range_tries(3) - 0.25f) < threshold);
|
||||
assert(std::abs(dist.range_tries(5, 11) - 0.062011f) < threshold);
|
||||
std::cout << "All tests passed" << std::endl;
|
||||
sample_test(dist);
|
||||
|
||||
dist = probability::geometric_dist::geometric_distribution(0.8f);
|
||||
|
||||
std::cout << "Starting tests for p = 0.8..." << std::endl;
|
||||
assert(std::abs(dist.expected_value() - 1.25f) < threshold);
|
||||
assert(std::abs(dist.variance() - 0.3125f) < threshold);
|
||||
assert(std::abs(dist.standard_deviation() - 0.559016f) < threshold);
|
||||
assert(std::abs(dist.probability_density(5) - 0.00128) < threshold);
|
||||
assert(std::abs(dist.cumulative_distribution(6) - 0.999936) < threshold);
|
||||
assert(std::abs(dist.inverse_cumulative_distribution(
|
||||
dist.cumulative_distribution(8)) -
|
||||
8) < threshold);
|
||||
assert(std::abs(dist.range_tries() - 1.0f) < threshold);
|
||||
assert(std::abs(dist.range_tries(3) - 0.04f) < threshold);
|
||||
assert(std::abs(dist.range_tries(5, 11) - 0.00159997f) < threshold);
|
||||
std::cout << "All tests have successfully passed!" << std::endl;
|
||||
sample_test(dist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @return 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
srand(time(nullptr));
|
||||
test(); // run self-test implementations
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief [Poisson
|
||||
* statistics](https://en.wikipedia.org/wiki/Poisson_distribution)
|
||||
*
|
||||
* The Poisson distribution counts how many
|
||||
* events occur over a set time interval.
|
||||
*/
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
/**
|
||||
* poisson rate:\n
|
||||
* calculate the events per unit time\n
|
||||
* e.g 5 dollars every 2 mins = 5 / 2 = 2.5
|
||||
*/
|
||||
double poisson_rate(double events, double timeframe) {
|
||||
return events / timeframe;
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate the expected value over a time
|
||||
* e.g rate of 2.5 over 10 mins = 2.5 x 10 = 25
|
||||
*/
|
||||
double poisson_expected(double rate, double time) { return rate * time; }
|
||||
|
||||
/**
|
||||
* Compute factorial of a given number
|
||||
*/
|
||||
double fact(double x) {
|
||||
double x_fact = x;
|
||||
for (int i = x - 1; i > 0; i--) {
|
||||
x_fact *= i;
|
||||
}
|
||||
|
||||
if (x_fact <= 0) {
|
||||
x_fact = 1;
|
||||
}
|
||||
return x_fact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the probability of x successes in a Poisson dist.
|
||||
* \f[p(\mu,x) = \frac{\mu^x e^{-\mu}}{x!}\f]
|
||||
*/
|
||||
double poisson_x_successes(double expected, double x) {
|
||||
return (std::pow(expected, x) * std::exp(-expected)) / fact(x);
|
||||
}
|
||||
|
||||
/**
|
||||
* probability of a success in range for Poisson dist (inclusive, inclusive)
|
||||
* \f[P = \sum_i p(\mu,i)\f]
|
||||
*/
|
||||
double poisson_range_successes(double expected, double lower, double upper) {
|
||||
double probability = 0;
|
||||
for (int i = lower; i <= upper; i++) {
|
||||
probability += poisson_x_successes(expected, i);
|
||||
}
|
||||
return probability;
|
||||
}
|
||||
|
||||
/**
|
||||
* main function
|
||||
*/
|
||||
int main() {
|
||||
double rate, expected;
|
||||
rate = poisson_rate(3, 1);
|
||||
std::cout << "Poisson rate : " << rate << std::endl;
|
||||
|
||||
expected = poisson_expected(rate, 2);
|
||||
std::cout << "Poisson expected : " << expected << std::endl;
|
||||
|
||||
std::cout << "Poisson 0 successes : " << poisson_x_successes(expected, 0)
|
||||
<< std::endl;
|
||||
std::cout << "Poisson 0-8 successes : "
|
||||
<< poisson_range_successes(expected, 0, 8) << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief An implementation of a median calculation of a sliding window along a
|
||||
* data stream
|
||||
*
|
||||
* @details
|
||||
* Given a stream of integers, the algorithm calculates the median of a fixed
|
||||
* size window at the back of the stream. The leading time complexity of this
|
||||
* algorithm is O(log(N), and it is inspired by the known algorithm to [find
|
||||
* median from (infinite) data
|
||||
* stream](https://www.tutorialcup.com/interview/algorithm/find-median-from-data-stream.htm),
|
||||
* with the proper modifications to account for the finite window size for which
|
||||
* the median is requested
|
||||
*
|
||||
* ### Algorithm
|
||||
* The sliding window is managed by a list, which guarantees O(1) for both
|
||||
* pushing and popping. Each new value is pushed to the window back, while a
|
||||
* value from the front of the window is popped. In addition, the algorithm
|
||||
* manages a multi-value binary search tree (BST), implemented by std::multiset.
|
||||
* For each new value that is inserted into the window, it is also inserted to
|
||||
* the BST. When a value is popped from the window, it is also erased from the
|
||||
* BST. Both insertion and erasion to/from the BST are O(logN) in time, with N
|
||||
* the size of the window. Finally, the algorithm keeps a pointer to the root of
|
||||
* the BST, and updates its position whenever values are inserted or erased
|
||||
* to/from BST. The root of the tree is the median! Hence, median retrieval is
|
||||
* always O(1)
|
||||
*
|
||||
* Time complexity: O(logN). Space complexity: O(N). N - size of window
|
||||
* @author [Yaniv Hollander](https://github.com/YanivHollander)
|
||||
*/
|
||||
#include <cassert> /// for assert
|
||||
#include <cstdlib> /// for std::rand - needed in testing
|
||||
#include <ctime> /// for std::time - needed in testing
|
||||
#include <list> /// for std::list - used to manage sliding window
|
||||
#include <set> /// for std::multiset - used to manage multi-value sorted sliding window values
|
||||
#include <vector> /// for std::vector - needed in testing
|
||||
|
||||
/**
|
||||
* @namespace probability
|
||||
* @brief Probability algorithms
|
||||
*/
|
||||
namespace probability {
|
||||
/**
|
||||
* @namespace windowed_median
|
||||
* @brief Functions for the Windowed Median algorithm implementation
|
||||
*/
|
||||
namespace windowed_median {
|
||||
using Window = std::list<int>;
|
||||
using size_type = Window::size_type;
|
||||
|
||||
/**
|
||||
* @class WindowedMedian
|
||||
* @brief A class to calculate the median of a leading sliding window at the
|
||||
* back of a stream of integer values.
|
||||
*/
|
||||
class WindowedMedian {
|
||||
const size_type _windowSize; ///< sliding window size
|
||||
Window _window; ///< a sliding window of values along the stream
|
||||
std::multiset<int> _sortedValues; ///< a DS to represent a balanced
|
||||
/// multi-value binary search tree (BST)
|
||||
std::multiset<int>::const_iterator
|
||||
_itMedian; ///< an iterator that points to the root of the multi-value
|
||||
/// BST
|
||||
|
||||
/**
|
||||
* @brief Inserts a value to a sorted multi-value BST
|
||||
* @param value Value to insert
|
||||
*/
|
||||
void insertToSorted(int value) {
|
||||
_sortedValues.insert(value); /// Insert value to BST - O(logN)
|
||||
const auto sz = _sortedValues.size();
|
||||
if (sz == 1) { /// For the first value, set median iterator to BST root
|
||||
_itMedian = _sortedValues.begin();
|
||||
return;
|
||||
}
|
||||
|
||||
/// If new value goes to left tree branch, and number of elements is
|
||||
/// even, the new median in the balanced tree is the left child of the
|
||||
/// median before the insertion
|
||||
if (value < *_itMedian && sz % 2 == 0) {
|
||||
--_itMedian; // O(1) - traversing one step to the left child
|
||||
}
|
||||
|
||||
/// However, if the new value goes to the right branch, the previous
|
||||
/// median's right child is the new median in the balanced tree
|
||||
else if (value >= *_itMedian && sz % 2 != 0) {
|
||||
++_itMedian; /// O(1) - traversing one step to the right child
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases a value from a sorted multi-value BST
|
||||
* @param value Value to insert
|
||||
*/
|
||||
void eraseFromSorted(int value) {
|
||||
const auto sz = _sortedValues.size();
|
||||
|
||||
/// If the erased value is on the left branch or the median itself and
|
||||
/// the number of elements is even, the new median will be the right
|
||||
/// child of the current one
|
||||
if (value <= *_itMedian && sz % 2 == 0) {
|
||||
++_itMedian; /// O(1) - traversing one step to the right child
|
||||
}
|
||||
|
||||
/// However, if the erased value is on the right branch or the median
|
||||
/// itself, and the number of elements is odd, the new median will be
|
||||
/// the left child of the current one
|
||||
else if (value >= *_itMedian && sz % 2 != 0) {
|
||||
--_itMedian; // O(1) - traversing one step to the left child
|
||||
}
|
||||
|
||||
/// Find the (first) position of the value we want to erase, and erase
|
||||
/// it
|
||||
const auto it = _sortedValues.find(value); // O(logN)
|
||||
_sortedValues.erase(it); // O(logN)
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a WindowedMedian object
|
||||
* @param windowSize Sliding window size
|
||||
*/
|
||||
explicit WindowedMedian(size_type windowSize) : _windowSize(windowSize){};
|
||||
|
||||
/**
|
||||
* @brief Insert a new value to the stream
|
||||
* @param value New value to insert
|
||||
*/
|
||||
void insert(int value) {
|
||||
/// Push new value to the back of the sliding window - O(1)
|
||||
_window.push_back(value);
|
||||
insertToSorted(value); // Insert value to the multi-value BST - O(logN)
|
||||
if (_window.size() > _windowSize) { /// If exceeding size of window,
|
||||
/// pop from its left side
|
||||
eraseFromSorted(
|
||||
_window.front()); /// Erase from the multi-value BST
|
||||
/// the window left side value
|
||||
_window.pop_front(); /// Pop the left side value from the window -
|
||||
/// O(1)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the median of the values in the sliding window
|
||||
* @return Median of sliding window. For even window size return the average
|
||||
* between the two values in the middle
|
||||
*/
|
||||
float getMedian() const {
|
||||
if (_sortedValues.size() % 2 != 0) {
|
||||
return *_itMedian; // O(1)
|
||||
}
|
||||
return 0.5f * *_itMedian + 0.5f * *next(_itMedian); /// O(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A naive and inefficient method to obtain the median of the sliding
|
||||
* window. Used for testing!
|
||||
* @return Median of sliding window. For even window size return the average
|
||||
* between the two values in the middle
|
||||
*/
|
||||
float getMedianNaive() const {
|
||||
auto window = _window;
|
||||
window.sort(); /// Sort window - O(NlogN)
|
||||
auto median =
|
||||
*next(window.begin(),
|
||||
window.size() / 2); /// Find value in the middle - O(N)
|
||||
if (window.size() % 2 != 0) {
|
||||
return median;
|
||||
}
|
||||
return 0.5f * median +
|
||||
0.5f * *next(window.begin(), window.size() / 2 - 1); /// O(N)
|
||||
}
|
||||
};
|
||||
} // namespace windowed_median
|
||||
} // namespace probability
|
||||
|
||||
/**
|
||||
* @brief Self-test implementations
|
||||
* @param vals Stream of values
|
||||
* @param windowSize Size of sliding window
|
||||
*/
|
||||
static void test(const std::vector<int> &vals, int windowSize) {
|
||||
probability::windowed_median::WindowedMedian windowedMedian(windowSize);
|
||||
for (const auto val : vals) {
|
||||
windowedMedian.insert(val);
|
||||
|
||||
/// Comparing medians: efficient function vs. Naive one
|
||||
assert(windowedMedian.getMedian() == windowedMedian.getMedianNaive());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Main function
|
||||
* @returns 0 on exit
|
||||
*/
|
||||
int main() {
|
||||
/// A few fixed test cases
|
||||
test({1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||
3); /// Array of sorted values; odd window size
|
||||
test({9, 8, 7, 6, 5, 4, 3, 2, 1},
|
||||
3); /// Array of sorted values - decreasing; odd window size
|
||||
test({9, 8, 7, 6, 5, 4, 5, 6}, 4); /// Even window size
|
||||
test({3, 3, 3, 3, 3, 3, 3, 3, 3}, 3); /// Array with repeating values
|
||||
test({3, 3, 3, 3, 7, 3, 3, 3, 3}, 3); /// Array with same values except one
|
||||
test({4, 3, 3, -5, -5, 1, 3, 4, 5},
|
||||
5); /// Array that includes repeating values including negatives
|
||||
|
||||
/// Array with large values - sum of few pairs exceeds MAX_INT. Window size
|
||||
/// is even - testing calculation of average median between two middle
|
||||
/// values
|
||||
test({470211272, 101027544, 1457850878, 1458777923, 2007237709, 823564440,
|
||||
1115438165, 1784484492, 74243042, 114807987},
|
||||
6);
|
||||
|
||||
/// Random test cases
|
||||
std::srand(static_cast<unsigned int>(std::time(nullptr)));
|
||||
std::vector<int> vals;
|
||||
for (int i = 8; i < 100; i++) {
|
||||
const auto n =
|
||||
1 + std::rand() /
|
||||
((RAND_MAX + 5u) / 20); /// Array size in the range [5, 20]
|
||||
auto windowSize =
|
||||
1 + std::rand() / ((RAND_MAX + 3u) /
|
||||
10); /// Window size in the range [3, 10]
|
||||
vals.clear();
|
||||
vals.reserve(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
vals.push_back(
|
||||
rand() - RAND_MAX); /// Random array values (positive/negative)
|
||||
}
|
||||
test(vals, windowSize); /// Testing randomized test
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user