Skip to content

Commit 10d3f99

Browse files
authored
Add approximate counting C++ sample code (#834)
* Add aproximate counting C++ sample code * Fix test, fix the confusion between double and int
1 parent d2c937b commit 10d3f99

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

contents/approximate_counting/approximate_counting.md

+2
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,8 @@ As we do not have any objects to count, we will instead simulate the counting wi
360360
{% method %}
361361
{% sample lang="jl" %}
362362
[import, lang:"julia"](code/julia/approximate_counting.jl)
363+
{% sample lang="cpp" %}
364+
[import, lang:"cpp"](code/c++/approximate_counting.cpp)
363365
{% endmethod %}
364366

365367
### Bibliography
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include <cmath>
2+
#include <iostream>
3+
#include <numeric>
4+
#include <random>
5+
6+
// Returns a pseudo-random number generator
7+
std::default_random_engine& rng() {
8+
// Initialize static pseudo-random engine with non-deterministic random seed
9+
static std::default_random_engine randEngine(std::random_device{}());
10+
return randEngine;
11+
}
12+
13+
// Returns a random double in [0, 1)
14+
double drand() {
15+
return std::uniform_real_distribution<double>(0.0, 1.0)(rng());
16+
}
17+
18+
// This function takes
19+
// - v: value in register
20+
// - a: a scaling value for the logarithm based on Morris's paper
21+
// It returns n(v,a), the approximate count
22+
auto n(double v, double a) { return a * (pow((1 + 1 / a), v) - 1); }
23+
24+
// This function takes
25+
// - v: value in register
26+
// - a: a scaling value for the logarithm based on Morris's paper
27+
// It returns a new value for v
28+
auto increment(int v, double a) {
29+
// delta is the probability of incrementing our counter
30+
const auto delta = 1 / (n(v + 1, a) - n(v, a));
31+
return (drand() <= delta) ? v + 1 : v;
32+
}
33+
34+
// This simulates counting and takes
35+
// - n_items: number of items to count and loop over
36+
// - a: a scaling value for the logarithm based on Morris's paper
37+
// It returns n(v,a), the approximate count
38+
auto approximate_count(int n_items, double a) {
39+
auto v = 0;
40+
for (auto i = 0; i < n_items; ++i)
41+
v = increment(v, a);
42+
43+
return n(v, a);
44+
}
45+
46+
// This function takes
47+
// - n_trials: the number of counting trials
48+
// - n_items: the number of items to count to
49+
// - a: a scaling value for the logarithm based on Morris's paper
50+
// - threshold: the maximum percent error allowed
51+
// It returns a "pass" / "fail" test value
52+
auto test_approximate_count(
53+
int n_trials, int n_items, double a, double threshold) {
54+
auto sum = 0.0;
55+
for (auto i = 0; i < n_trials; ++i)
56+
sum += approximate_count(n_items, a);
57+
const auto avg = sum / n_trials;
58+
return std::abs((avg - n_items) / n_items) < threshold ? "pass" : "fail";
59+
}
60+
61+
int main() {
62+
std::cout << "Counting Tests, 100 trials\n";
63+
64+
std::cout << "testing 1,000, a = 30, 1% error "
65+
<< test_approximate_count(100, 1000, 30, 0.1) << "\n";
66+
std::cout << "testing 12,345, a = 10, 1% error "
67+
<< test_approximate_count(100, 12345, 10, 0.1) << "\n";
68+
// Note : with a lower a, we need more trials, so a higher % error here.
69+
std::cout << "testing 222,222, a = 0.5, 10% error "
70+
<< test_approximate_count(100, 222222, 0.5, 0.2) << "\n";
71+
}

0 commit comments

Comments
 (0)