Skip to content

Commit 508cfb5

Browse files
PaddyKeShadowMitia
andauthored
Added approximate counting in Rust (#900)
* added approximate counting in rust * added comments * made output more consistent * fixed typo and printed values * added Cargo.toml * Fix binary name Co-authored-by: Dimitri Belopopsky <ShadowMitia@users.noreply.github.com> Co-authored-by: Dimitri Belopopsky <dimitri@belopopsky.com>
1 parent ff494db commit 508cfb5

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

contents/approximate_counting/approximate_counting.md

+2
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ As we do not have any objects to count, we will instead simulate the counting wi
366366
[import, lang:"cpp"](code/cpp/approximate_counting.cpp)
367367
{% sample lang="python" %}
368368
[import, lang:"python"](code/python/approximate_counting.py)
369+
{% sample lang="rs" %}
370+
[import, lang:"rust"](code/rust/approximate_counting.rs)
369371
{% sample lang="java" %}
370372
[import, lang:"java"](code/java/ApproximateCounting.java)
371373
{% endmethod %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "approximate_counting"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
rand = "0.8.4"
10+
11+
[[bin]]
12+
name = "main"
13+
path = "approximate_counting.rs"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// This function takes
2+
// - v: value in register
3+
// - a: a scaling value for the logarithm based on Morris's paper
4+
// It returns n(v,a), the approximate count
5+
fn n(v: f64, a: f64) -> f64 {
6+
a * ((1_f64 + 1_f64 / a).powf(v) - 1_f64)
7+
}
8+
9+
10+
// This function takes
11+
// - v: value in register
12+
// - a: a scaling value for the logarithm based on Morris's paper
13+
// It returns a new value for v
14+
fn increment(v: f64, a: f64) -> f64 {
15+
// delta is the probability of incrementing our counter
16+
let delta = 1_f64 / (n(v + 1_f64, a) - n(v, a));
17+
18+
if rand::random::<f64>() <= delta {
19+
v + 1_f64
20+
} else {
21+
v
22+
}
23+
}
24+
25+
// This simulates counting and takes
26+
// - n_items: number of items to count and loop over
27+
// - a: a scaling value for the logarithm based on Morris's paper
28+
// It returns n(v,a), the approximate count
29+
fn approximate_count(n_items: usize, a: f64) -> f64 {
30+
let mut v = 0_f64;
31+
32+
for _ in 0..n_items {
33+
v = increment(v, a);
34+
}
35+
36+
v
37+
}
38+
39+
// This function takes
40+
// - n_trials: the number of counting trials
41+
// - n_items: the number of items to count to
42+
// - a: a scaling value for the logarithm based on Morris's paper
43+
// - threshold: the maximum percent error allowed
44+
// It returns a "passed" / "failed" test value
45+
fn test_approximate_count(n_trials: usize, n_items: usize, a: f64, threshold: f64) {
46+
let avg = std::iter::from_fn(|| Some(approximate_count(n_items, a)))
47+
.take(n_trials)
48+
.sum::<f64>() / n_trials as f64;
49+
50+
let n_items_float = n_items as f64;
51+
52+
if ((avg - n_items_float) / n_items_float) < threshold {
53+
println!("passed");
54+
} else {
55+
println!("failed");
56+
}
57+
58+
}
59+
60+
fn main() {
61+
println!("testing 1,000, a = 30, 10% error");
62+
test_approximate_count(100, 1000, 30_f64, 0.1);
63+
println!("testing 12,345, a = 10, 10% error");
64+
test_approximate_count(100, 12345, 10_f64, 0.1);
65+
println!("testing 222,222, a = 0.5, 20% error");
66+
test_approximate_count(100, 222222, 0.5, 0.2);
67+
}

0 commit comments

Comments
 (0)