Skip to content

Commit 4237b88

Browse files
committed
update
2 parents cbe7585 + 40cf050 commit 4237b88

File tree

16 files changed

+852
-4
lines changed

16 files changed

+852
-4
lines changed

.editorconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,11 @@ indent_size = 2
145145
indent_style = space
146146
indent_size = 4
147147

148+
# Scratch (text code)
149+
[contents/*/code/scratch/*.txt]
150+
indent_style = space
151+
indent_size = 4
152+
148153
# Whitespace
149154
[*.ws]
150155
indent_style = space

CONTRIBUTORS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ This file lists everyone, who contributed to this repo and wanted to show up her
4141
- crafter312
4242
- Christopher Milan
4343
- Vexatos
44+
- Raven-Blue Dragon
4445
- Björn Heinrichs
4546
- Olav Sundfør
47+
- Ben Chislett
4648
- dovisutu
4749
- Antetokounpo
50+
- Akash Dhiman

contents/bubble_sort/bubble_sort.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ This means that we need to go through the vector $$\mathcal{O}(n^2)$$ times with
6868
[import:2-14, lang:"emojicode"](code/emojicode/bubble_sort.emojic)
6969
{% sample lang="bash" %}
7070
[import:2-21, lang:"bash"](code/bash/bubble_sort.bash)
71+
{% sample lang="scratch" %}
72+
<p>
73+
<img class="center" src="code/scratch/bubble_sort.svg" width="400" />
74+
</p>
7175
{% endmethod %}
7276

7377
... And that's it for the simplest bubble sort method.
@@ -141,6 +145,8 @@ Trust me, there are plenty of more complicated algorithms that do precisely the
141145
[import, lang:"emojicode"](code/emojicode/bubble_sort.emojic)
142146
{% sample lang="bash" %}
143147
[import, lang:"bash"](code/bash/bubble_sort.bash)
148+
{% sample lang="scratch" %}
149+
The code snippet was taken from this [Scratch project](https://scratch.mit.edu/projects/316483792)
144150
{% endmethod %}
145151

146152
<script>

contents/bubble_sort/code/scratch/bubble_sort.svg

Lines changed: 116 additions & 0 deletions
Loading
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
define BubbleSort
2+
set [i v] to (1)
3+
set [j v] to (1)
4+
repeat (length of [a v])
5+
repeat ((length of [a v]) - (1))
6+
If <(item ((j)+(1)) of [a v]) < (item (j) of [a v])> then
7+
set [tmp v] to (item (j) of [a v])
8+
replace item (j) of [a v] with (item ((j)+(1)) of [a v]
9+
replace item ((j)+(1)) of [a v] with (tmp)
10+
end
11+
change [j v] by (1)
12+
end
13+
set [j v] to (1)
14+
change [i v] by (1)
15+
end
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
const Complex = require("complex.js");
2+
3+
function dft(x) {
4+
const N = x.length;
5+
6+
// Initialize an array with N elements, filled with 0s
7+
return Array(N)
8+
.fill(new Complex(0, 0))
9+
.map((temp, i) => {
10+
// Reduce x into the sum of x_k * exp(-2*sqrt(-1)*pi*i*k/N)
11+
return x.reduce((a, b, k) => {
12+
return a.add(b.mul(new Complex(0, (-2 * Math.PI * i * k) / N).exp()));
13+
}, new Complex(0, 0)); // Start accumulating from 0
14+
});
15+
}
16+
17+
function cooley_tukey(x) {
18+
const N = x.length;
19+
const half = Math.floor(N / 2);
20+
if (N <= 1) {
21+
return x;
22+
}
23+
24+
// Extract even and odd indexed elements with remainder mod 2
25+
const evens = cooley_tukey(x.filter((_, idx) => !(idx % 2)));
26+
const odds = cooley_tukey(x.filter((_, idx) => idx % 2));
27+
28+
// Fill an array with null values
29+
let temp = Array(N).fill(null);
30+
31+
for (let i = 0; i < half; i++) {
32+
const arg = odds[i].mul(new Complex(0, (-2 * Math.PI * i) / N).exp());
33+
34+
temp[i] = evens[i].add(arg);
35+
temp[i + half] = evens[i].sub(arg);
36+
}
37+
38+
return temp;
39+
}
40+
41+
function bit_reverse_idxs(n) {
42+
if (!n) {
43+
return [0];
44+
} else {
45+
const twice = bit_reverse_idxs(n - 1).map(x => 2 * x);
46+
return twice.concat(twice.map(x => x + 1));
47+
}
48+
}
49+
50+
function bit_reverse(x) {
51+
const N = x.length;
52+
const indexes = bit_reverse_idxs(Math.log2(N));
53+
return x.map((_, i) => x[indexes[i]]);
54+
}
55+
56+
// Assumes log_2(N) is an integer
57+
function iterative_cooley_tukey(x) {
58+
const N = x.length;
59+
60+
x = bit_reverse(x);
61+
62+
for (let i = 1; i <= Math.log2(N); i++) {
63+
const stride = 2 ** i;
64+
const half = stride / 2;
65+
const w = new Complex(0, (-2 * Math.PI) / stride).exp();
66+
for (let j = 0; j < N; j += stride) {
67+
let v = new Complex(1, 0);
68+
for (let k = 0; k < half; k++) {
69+
// perform butterfly multiplication
70+
x[k + j + half] = x[k + j].sub(v.mul(x[k + j + half]));
71+
x[k + j] = x[k + j].sub(x[k + j + half].sub(x[k + j]));
72+
// accumulate v as powers of w
73+
v = v.mul(w);
74+
}
75+
}
76+
}
77+
78+
return x;
79+
}
80+
81+
// Check if two arrays of complex numbers are approximately equal
82+
function approx(x, y, tol = 1e-12) {
83+
let diff = 0;
84+
for (let i = 0; i < x.length; i++) {
85+
diff += x[i].sub(y[i]).abs();
86+
}
87+
return diff < tol;
88+
}
89+
90+
const X = Array.from(Array(8), () => new Complex(Math.random(), 0));
91+
const Y = cooley_tukey(X);
92+
const Z = iterative_cooley_tukey(X);
93+
const T = dft(X);
94+
95+
// Check if the calculations are correct within a small tolerance
96+
console.log("Cooley tukey approximation is accurate: ", approx(Y, T));
97+
console.log("Iterative cooley tukey approximation is accurate: ", approx(Z, T));

contents/cooley_tukey/cooley_tukey.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ For some reason, though, putting code to this transformation really helped me fi
8585
[import:4-13, lang:"julia"](code/julia/fft.jl)
8686
{% sample lang="asm-x64" %}
8787
[import:15-74, lang:"asm-x64"](code/asm-x64/fft.s)
88+
{% sample lang="js" %}
89+
[import:3-15, lang:"javascript"](code/javascript/fft.js)
8890
{% endmethod %}
8991

9092
In this function, we define `n` to be a set of integers from $$0 \rightarrow N-1$$ and arrange them to be a column.
@@ -101,7 +103,7 @@ M = [1.0+0.0im 1.0+0.0im 1.0+0.0im 1.0+0.0im;
101103

102104
It was amazing to me when I saw the transform for what it truly was: an actual transformation matrix!
103105
That said, the Discrete Fourier Transform is slow -- primarily because matrix multiplication is slow, and as mentioned before, slow code is not particularly useful.
104-
So what was the trick that everyone used to go from a Discrete Fourier Transform to a *Fast* Fourier Transform?
106+
So what was the trick that everyone used to go from a Discrete Fourier Transform to a _Fast_ Fourier Transform?
105107

106108
Recursion!
107109

@@ -134,6 +136,8 @@ In the end, the code looks like:
134136
[import:16-32, lang:"julia"](code/julia/fft.jl)
135137
{% sample lang="asm-x64" %}
136138
[import:76-165, lang:"asm-x64"](code/asm-x64/fft.s)
139+
{% sample lang="js" %}
140+
[import:17-39, lang="javascript"](code/javascript/fft.js)
137141
{% endmethod %}
138142

139143
As a side note, we are enforcing that the array must be a power of 2 for the operation to work.
@@ -142,6 +146,7 @@ This is a limitation of the fact that we are using recursion and dividing the ar
142146
The above method is a perfectly valid FFT; however, it is missing the pictorial heart and soul of the Cooley-Tukey algorithm: Butterfly Diagrams.
143147

144148
### Butterfly Diagrams
149+
145150
Butterfly Diagrams show where each element in the array goes before, during, and after the FFT.
146151
As mentioned, the FFT must perform a DFT.
147152
This means that even though we need to be careful about how we add elements together, we are still ultimately performing the following operation:
@@ -214,8 +219,8 @@ I'll definitely come back to this at some point, so let me know what you liked a
214219

215220
To be clear, the example code this time will be complicated and requires the following functions:
216221

217-
* An FFT library (either in-built or something like FFTW)
218-
* An approximation function to tell if two arrays are similar
222+
- An FFT library (either in-built or something like FFTW)
223+
- An approximation function to tell if two arrays are similar
219224

220225
As mentioned in the text, the Cooley-Tukey algorithm may be implemented either recursively or non-recursively, with the recursive method being much easier to implement.
221226
I would ask that you implement either the recursive or non-recursive methods (or both, if you feel so inclined).
@@ -242,9 +247,10 @@ Note: I implemented this in Julia because the code seems more straightforward in
242247
Some rather impressive scratch code was submitted by Jie and can be found here: https://scratch.mit.edu/projects/37759604/#editor
243248
{% sample lang="asm-x64" %}
244249
[import, lang:"asm-x64"](code/asm-x64/fft.s)
250+
{% sample lang="js" %}
251+
[import, lang:"javascript"](code/javascript/fft.js)
245252
{% endmethod %}
246253

247-
248254
<script>
249255
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
250256
</script>
@@ -262,6 +268,7 @@ The text of this chapter was written by [James Schloss](https://github.com/leios
262268
[<p><img class="center" src="../cc/CC-BY-SA_icon.svg" /></p>](https://creativecommons.org/licenses/by-sa/4.0/)
263269

264270
##### Images/Graphics
271+
265272
- The image "[FTexample](res/FT_example.png)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode).
266273
- The image "[radix2positive](res/radix-2screen_positive.jpg)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode).
267274
- The image "[radix2](res/radix-2screen.jpg)" was created by [James Schloss](https://github.com/leios) and is licenced under the [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/legalcode).
@@ -271,4 +278,5 @@ The text of this chapter was written by [James Schloss](https://github.com/leios
271278
##### Pull Requests
272279

273280
After initial licensing ([#560](https://github.com/algorithm-archivists/algorithm-archive/pull/560)), the following pull requests have modified the text or graphics of this chapter:
281+
274282
- none

contents/euclidean_algorithm/code/scratch/euclid_mod.svg

Lines changed: 114 additions & 0 deletions
Loading

contents/euclidean_algorithm/code/scratch/euclid_sub.svg

Lines changed: 114 additions & 0 deletions
Loading
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
define euclid_sub
2+
set [a v] to ([abs v] of [a v])
3+
set [b v] to ([abs v] of [b v])
4+
repeat until <(a) = (b)>
5+
if <(a) > (b)> then
6+
set [a v] to ((a) - (b))
7+
else
8+
set [b v] to ((b) - (a))
9+
10+
11+
define euclid_mod
12+
set [a v] to ([abs v] of [a v])
13+
set [b v] to ([abs v] of [b v])
14+
repeat until <(b) = (0)>
15+
set [temp v] to (b)
16+
set [b v] to ((a) mod (b))
17+
set [a v] to (temp)
18+
19+
when green flag clicked
20+
set [a v] to ((64) * (67))
21+
set [b v] to ((64) * (81))
22+
euclid_sub
23+
set [a v] to ((128) * (12))
24+
set [b v] to ((128) * (77))
25+
euclid_mod

contents/euclidean_algorithm/code/scratch/main.svg

Lines changed: 114 additions & 0 deletions
Loading

contents/euclidean_algorithm/euclidean_algorithm.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,11 @@ The algorithm is a simple way to find the *greatest common divisor* (GCD) of two
7171
> ![](code/piet/subtract/euclidian_algorithm_subtract_large.png) ![](code/piet/subtract/euclidian_algorithm_subtract.png)
7272
{% sample lang="ss" %}
7373
[import:1-7, lang="scheme"](code/scheme/euclidalg.ss)
74+
{% sample lang="scratch" %}
75+
<p>
76+
<img class="center" src="code/scratch/euclid_sub.svg" width="200" />
77+
</p>
78+
7479
{% endmethod %}
7580

7681
Here, we simply line the two numbers up every step and subtract the lower value from the higher one every timestep. Once the two values are equal, we call that value the greatest common divisor. A graph of `a` and `b` as they change every step would look something like this:
@@ -148,6 +153,11 @@ Modern implementations, though, often use the modulus operator (%) like so
148153
> ![](code/piet/mod/euclidian_algorithm_mod_large.png) ![](code/piet/mod/euclidian_algorithm_mod.png)
149154
{% sample lang="ss" %}
150155
[import:9-12, lang="scheme"](code/scheme/euclidalg.ss)
156+
{% sample lang="scratch" %}
157+
<p>
158+
<img class="center" src="code/scratch/euclid_mod.svg" width="200" />
159+
</p>
160+
151161
{% endmethod %}
152162

153163
Here, we set `b` to be the remainder of `a%b` and `a` to be whatever `b` was last timestep. Because of how the modulus operator works, this will provide the same information as the subtraction-based implementation, but when we show `a` and `b` as they change with time, we can see that it might take many fewer steps:
@@ -248,6 +258,13 @@ A text version of the program is provided for both versions.
248258
[import:126-146](code/piet/euclidian_algorithm.piet)
249259
{% sample lang="ss" %}
250260
[import:, lang="scheme"](code/scheme/euclidalg.ss)
261+
{% sample lang="scratch" %}
262+
The code snippets were taken from this [Scratch project](https://scratch.mit.edu/projects/278727055/)
263+
264+
<p>
265+
<img class="center" src="code/scratch/main.svg" width="200" />
266+
</p>
267+
251268
{% endmethod %}
252269

253270
<script>

0 commit comments

Comments
 (0)