Skip to content

Commit c17ed01

Browse files
authored
Merge branch 'algorithm-archivists:master' into computus_in_javascript_typescript
2 parents 48b52eb + 139c4a1 commit c17ed01

File tree

13 files changed

+560
-4
lines changed

13 files changed

+560
-4
lines changed

CONTRIBUTORS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,5 @@ This file lists everyone, who contributed to this repo and wanted to show up her
5656
- Ishaan Verma
5757
- Delphi1024
5858
- ntindle
59+
- Mahdi Sarikhani
60+
- Ridham177

book.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@
206206
"lang": "kotlin",
207207
"name": "Kotlin"
208208
},
209+
{
210+
"lang": "ts",
211+
"name": "TypeScript"
212+
},
209213
{
210214
"lang": "vim",
211215
"name": "VimL"

contents/approximate_counting/approximate_counting.md

Lines changed: 2 additions & 0 deletions
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
Lines changed: 71 additions & 0 deletions
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+
}

contents/approximate_counting/code/julia/approximate_counting.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function increment(v, a)
1717
delta = 1/(n(v+1, a)-n(v, a))
1818

1919
if rand() <= delta
20-
return v += 1
20+
return v + 1
2121
else
2222
return v
2323
end
@@ -47,7 +47,7 @@ function test_approximate_count(n_trials, n_items, a, threshold)
4747

4848
avg = sum(samples)/n_trials
4949

50-
@test ((avg - n_items) / n_items < threshold)
50+
@test (abs((avg - n_items) / n_items) < threshold)
5151
end
5252

5353
@testset "Counting Tests, 100 trials" begin

contents/barnsley/barnsley.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ The biggest differences between the two code implementations is that the Barnsle
125125
{% method %}
126126
{% sample lang="jl" %}
127127
[import, lang:"julia"](code/julia/barnsley.jl)
128+
{% sample lang="cpp" %}
129+
[import, lang:"cpp"](code/c++/barnsley.cpp)
130+
{% sample lang="c" %}
131+
[import, lang:"c"](code/c/barnsley.c)
132+
{% sample lang="java" %}
133+
[import, lang:"java"](code/java/Barnsley.java)
128134
{% endmethod %}
129135

130136
### Bibliography
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// The code bellow uses C++-17 features, compile it with C++-17 flags, e.g.:
2+
// clang++ -Wall -Wextra -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wpedantic -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -gdwarf-3 -D_GLIBCXX_DEBUG -std=c++17 -O3 -c ./barnsley.cpp barnsley
3+
4+
#include <array>
5+
#include <cassert>
6+
#include <fstream>
7+
#include <random>
8+
9+
using Vec2 = std::array<double, 2>;
10+
using Vec3 = std::array<double, 3>;
11+
using Row = std::array<double, 3>;
12+
using Op = std::array<Row, 3>;
13+
14+
constexpr auto OpN = 4U;
15+
16+
template <size_t N>
17+
auto operator+(std::array<double, N> x, std::array<double, N> y) {
18+
for (auto i = 0U; i < N; ++i)
19+
x[i] += y[i];
20+
return x;
21+
}
22+
23+
template <size_t N>
24+
auto operator*(double k, std::array<double, N> v) {
25+
for (auto i = 0U; i < N; ++i)
26+
v[i] *= k;
27+
return v;
28+
}
29+
30+
template <size_t N>
31+
auto operator*(std::array<double, N> v, double k) {
32+
return k * v;
33+
}
34+
35+
auto operator*(const Op& x, const Vec3& y) {
36+
auto ret = Vec3{};
37+
for (auto i = 0U; i < 3U; ++i) {
38+
ret[i] = 0;
39+
for (auto j = 0U; j < 3U; ++j)
40+
ret[i] += y[j] * x[i][j];
41+
}
42+
return ret;
43+
}
44+
45+
// Returns a pseudo-random number generator
46+
std::default_random_engine& rng() {
47+
// Initialize static pseudo-random engine with non-deterministic random seed
48+
static std::default_random_engine randEngine(std::random_device{}());
49+
return randEngine;
50+
}
51+
52+
// Returns a random double in [0, 1)
53+
double drand() {
54+
return std::uniform_real_distribution<double>(0.0, 1.0)(rng());
55+
}
56+
57+
// This is a function that reads in the Hutchinson operator and
58+
// corresponding
59+
// probabilities and outputs a randomly selected transform
60+
// This works by choosing a random number and then iterating through all
61+
// probabilities until it finds an appropriate bin
62+
auto select_array(
63+
const std::array<Op, OpN>& hutchinson_op,
64+
const std::array<double, OpN>& probabilities) {
65+
66+
// random number to be binned
67+
auto rnd = drand();
68+
69+
// This checks to see if a random number is in a bin, if not, that
70+
// probability is subtracted from the random number and we check the
71+
// next bin in the list
72+
for (auto i = 0U; i < probabilities.size(); ++i) {
73+
if (rnd < probabilities[i])
74+
return hutchinson_op[i];
75+
rnd -= probabilities[i];
76+
}
77+
assert(!static_cast<bool>("check if probabilities adding up to 1"));
78+
}
79+
80+
// This is a general function to simulate a chaos game
81+
// n is the number of iterations
82+
// initial_location is the the starting point of the chaos game
83+
// hutchinson_op is the set of functions to iterate through
84+
// probabilities is the set of probabilities corresponding to the likelihood
85+
// of choosing their corresponding function in hutchinson_op
86+
auto chaos_game(
87+
size_t n,
88+
Vec2 initial_location,
89+
const std::array<Op, OpN>& hutchinson_op,
90+
const std::array<double, OpN>& probabilities) {
91+
92+
// Initializing the output array and the initial point
93+
auto output_points = std::vector<Vec2>{};
94+
95+
// extending point to 3D for affine transform
96+
auto point = Vec3{initial_location[0], initial_location[1], 1};
97+
98+
for (auto i = 0U; i < n; ++i) {
99+
output_points.push_back(Vec2{point[0], point[1]});
100+
point = select_array(hutchinson_op, probabilities) * point;
101+
}
102+
103+
return output_points;
104+
}
105+
106+
int main() {
107+
108+
const std::array barnsley_hutchinson = {
109+
Op{Row{0.0, 0.0, 0.0}, Row{0.0, 0.16, 0.0}, Row{0.0, 0.0, 1.0}},
110+
Op{Row{0.85, 0.04, 0.0}, Row{-0.04, 0.85, 1.60}, Row{0.0, 0.0, 1.0}},
111+
Op{Row{0.20, -0.26, 0.0}, Row{0.23, 0.22, 1.60}, Row{0.0, 0.0, 1.0}},
112+
Op{Row{-0.15, 0.28, 0.0}, Row{0.26, 0.24, 0.44}, Row{0.0, 0.0, 1.0}}};
113+
114+
const std::array barnsley_probabilities = {0.01, 0.85, 0.07, 0.07};
115+
auto output_points = chaos_game(
116+
10'000, Vec2{0, 0}, barnsley_hutchinson, barnsley_probabilities);
117+
118+
std::ofstream ofs("out.dat");
119+
for (auto pt : output_points)
120+
ofs << pt[0] << '\t' << pt[1] << '\n';
121+
}

contents/barnsley/code/c/barnsley.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
4+
struct matrix {
5+
double xx, xy, xz,
6+
yx, yy, yz,
7+
zx, zy, zz;
8+
};
9+
10+
struct point2d {
11+
double x, y;
12+
};
13+
14+
struct point3d {
15+
double x, y, z;
16+
};
17+
18+
struct point3d matmul(struct matrix mat, struct point3d point)
19+
{
20+
struct point3d out = {
21+
mat.xx * point.x + mat.xy * point.y + mat.xz * point.z,
22+
mat.yx * point.x + mat.yy * point.y + mat.yz * point.z,
23+
mat.zx * point.x + mat.zy * point.y + mat.zz * point.z
24+
};
25+
return out;
26+
}
27+
28+
// This function reads in the Hutchinson operator and corresponding
29+
// probabilities and returns a randomly selected transform
30+
// This works by choosing a random number and then iterating through all
31+
// probabilities until it finds an appropriate bin
32+
struct matrix select_array(struct matrix *hutchinson_op, double *probabilities,
33+
size_t num_op)
34+
{
35+
// random number to be binned
36+
double rnd = (double)rand() / RAND_MAX;
37+
38+
// This checks to see if a random number is in a bin, if not, that
39+
// probability is subtracted from the random number and we check the next
40+
// bin in the list
41+
for (size_t i = 0; i < num_op; ++i) {
42+
if (rnd < probabilities[i]) {
43+
return hutchinson_op[i];
44+
}
45+
rnd -= probabilities[i];
46+
}
47+
}
48+
49+
// This is a general function to simulate a chaos game
50+
// - output_points: pointer to an initialized output array
51+
// - num: the number of iterations
52+
// - initial_point: the starting point of the chaos game
53+
// - hutchinson_op: the set of functions to iterate through
54+
// - probabilities: the set of probabilities corresponding to the likelihood
55+
// of choosing their corresponding function in hutchingson_op
56+
// - nop: the number of functions in hutchinson_op
57+
void chaos_game(struct point2d *output_points, size_t num,
58+
struct point2d initial_point, struct matrix *hutchinson_op,
59+
double *probabilities, size_t nop)
60+
{
61+
// extending point to 3D for affine transform
62+
struct point3d point = {initial_point.x, initial_point.y, 1.0};
63+
64+
for (size_t i = 0; i < num; ++i) {
65+
point = matmul(select_array(hutchinson_op, probabilities, nop), point);
66+
output_points[i].x = point.x;
67+
output_points[i].y = point.y;
68+
}
69+
}
70+
71+
int main()
72+
{
73+
struct matrix barnsley_hutchinson[4] = {
74+
{
75+
0.0, 0.0, 0.0,
76+
0.0, 0.16, 0.0,
77+
0.0, 0.0, 1.0
78+
},
79+
{
80+
0.85, 0.04, 0.0,
81+
-0.04, 0.85, 1.60,
82+
0.0, 0.0, 1.0
83+
},
84+
{
85+
0.2, -0.26, 0.0,
86+
0.23, 0.22, 1.60,
87+
0.0, 0.0, 1.0
88+
},
89+
{
90+
-0.15, 0.28, 0.0,
91+
0.26, 0.24, 0.44,
92+
0.0, 0.0, 1.0
93+
}
94+
};
95+
96+
double barnsley_probabilities[4] = {0.01, 0.85, 0.07, 0.07};
97+
struct point2d output_points[10000];
98+
struct point2d initial_point = {0.0, 0.0};
99+
chaos_game(output_points, 10000, initial_point, barnsley_hutchinson,
100+
barnsley_probabilities, 4);
101+
FILE *f = fopen("barnsley.dat", "w");
102+
for (size_t i = 0; i < 10000; ++i) {
103+
fprintf(f, "%f\t%f\n", output_points[i].x, output_points[i].y);
104+
}
105+
fclose(f);
106+
107+
return 0;
108+
}

0 commit comments

Comments
 (0)