Skip to content

Commit

Permalink
🚀 Deploy with hugo-remote
Browse files Browse the repository at this point in the history
  • Loading branch information
adamdrake committed Sep 13, 2020
1 parent 5a2df36 commit 8802a8e
Show file tree
Hide file tree
Showing 67 changed files with 142 additions and 147 deletions.
2 changes: 1 addition & 1 deletion a-quick-overview-on-the-kaggle-competition-for-avito.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion about.html

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion always-be-shipping.html

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions an-unreasonably-deep-dive-into-project-euler-problem-1.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<a class=twitter-share-button href="https://twitter.com/intent/tweet?text=Read%20An%20Unreasonably%20Deep%20Dive%20into%20Project%20Euler%20Problem%201%20https%3a%2f%2fadamdrake.com%2fan-unreasonably-deep-dive-into-project-euler-problem-1.html" onclick="window.open(this.href,'twitter-share','width=550,height=235');return false;">twitter</a> //
<a class=icon-facebook href="https://www.facebook.com/sharer/sharer.php?u=https%3a%2f%2fadamdrake.com%2fan-unreasonably-deep-dive-into-project-euler-problem-1.html" onclick="window.open(this.href,'facebook-share','width=580,height=296');return false;">facebook</a> //
<a class=icon-linkedin href="https://www.linkedin.com/shareArticle?mini=true&url=https://adamdrake.com&title=An%20Unreasonably%20Deep%20Dive%20into%20Project%20Euler%20Problem%201&source=Adam%20Drake" onclick="window.open(this.href,'linkedin-share','width=980,height=980');return false;">linkedin</a> //
<a class=icon-google-plus href="https://plus.google.com/share?url=https%3a%2f%2fadamdrake.com%2fan-unreasonably-deep-dive-into-project-euler-problem-1.html" onclick="window.open(this.href,'google-plus-share','width=490,height=530');return false;">google+</a></div><div class=content><h1 id=introduction>Introduction</h1><p>As part of my work in keeping my technical skills sharp, I periodically go back to basics or solve old problems again in order to ensure my foundations are strong. So it was with great fun that I decided to start back at the beginning with <a href=https://projecteuler.net>Project Euler</a>.</p><p>One of the techniques I also use for this sort of thing is not just to solve the problem, but to really explore it. Write additional code, tests, benchmarks, and explore the underlying mathematics where practical.</p><p>With that in mind, here is a deep dive into <a href="https://projecteuler.net/problem=1">Project Euler - Problem 1</a>.</p><h1 id=overview>Overview</h1><p>The problem is short and easy to understand:</p><blockquote><p>If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.</p></blockquote><p>A simple brute-force approach to this is simply to iterate through all of the numbers from 1 to 999 (since we are only to check numbers below 1000), check if the number is divisible by 3 or 5, and sum the ones which are.</p><p>If you want the Python one-liner:</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=color:#366>sum</span>([x <span style=color:#069;font-weight:700>for</span> x <span style=color:#000;font-weight:700>in</span> <span style=color:#366>range</span>(<span style=color:#f60>1000</span>) <span style=color:#069;font-weight:700>if</span> ((x <span style=color:#555>%</span> <span style=color:#f60>5</span> <span style=color:#555>==</span> <span style=color:#f60>0</span>) <span style=color:#000;font-weight:700>or</span> (x <span style=color:#555>%</span> <span style=color:#f60>3</span> <span style=color:#555>==</span> <span style=color:#f60>0</span>))])
<a class=icon-google-plus href="https://plus.google.com/share?url=https%3a%2f%2fadamdrake.com%2fan-unreasonably-deep-dive-into-project-euler-problem-1.html" onclick="window.open(this.href,'google-plus-share','width=490,height=530');return false;">google+</a></div><div class=content><h1 id=introduction class=anchor-link><a href=#introduction>Introduction</a></h1><p>As part of my work in keeping my technical skills sharp, I periodically go back to basics or solve old problems again in order to ensure my foundations are strong. So it was with great fun that I decided to start back at the beginning with <a href=https://projecteuler.net>Project Euler</a>.</p><p>One of the techniques I also use for this sort of thing is not just to solve the problem, but to really explore it. Write additional code, tests, benchmarks, and explore the underlying mathematics where practical.</p><p>With that in mind, here is a deep dive into <a href="https://projecteuler.net/problem=1">Project Euler - Problem 1</a>.</p><h1 id=overview class=anchor-link><a href=#overview>Overview</a></h1><p>The problem is short and easy to understand:</p><blockquote><p>If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.</p></blockquote><p>A simple brute-force approach to this is simply to iterate through all of the numbers from 1 to 999 (since we are only to check numbers below 1000), check if the number is divisible by 3 or 5, and sum the ones which are.</p><p>If you want the Python one-liner:</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Python data-lang=Python><span style=color:#366>sum</span>([x <span style=color:#069;font-weight:700>for</span> x <span style=color:#000;font-weight:700>in</span> <span style=color:#366>range</span>(<span style=color:#f60>1000</span>) <span style=color:#069;font-weight:700>if</span> ((x <span style=color:#555>%</span> <span style=color:#f60>5</span> <span style=color:#555>==</span> <span style=color:#f60>0</span>) <span style=color:#000;font-weight:700>or</span> (x <span style=color:#555>%</span> <span style=color:#f60>3</span> <span style=color:#555>==</span> <span style=color:#f60>0</span>))])
</code></pre></div><p>Of if you are using Go, as I was:</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Go data-lang=Go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>bruteForce</span>(limit <span style=color:#078;font-weight:700>int</span>) <span style=color:#078;font-weight:700>int</span> {
total <span style=color:#555>:=</span> <span style=color:#f60>0</span>
<span style=color:#069;font-weight:700>for</span> i <span style=color:#555>:=</span> <span style=color:#f60>0</span>; i <span style=color:#555>&lt;=</span> limit; i<span style=color:#555>++</span> {
Expand All @@ -18,7 +18,7 @@
}
<span style=color:#069;font-weight:700>return</span> total
}
</code></pre></div><h1 id=does-it-work>Does it work?</h1><p>This works fine and both produce the required answer of 233168, which we can verify with some tests in Go:</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Go data-lang=Go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>TestBruteForce</span>(t <span style=color:#555>*</span>testing.T) {
</code></pre></div><h1 id=does-it-work class=anchor-link><a href=#does-it-work>Does it work?</a></h1><p>This works fine and both produce the required answer of 233168, which we can verify with some tests in Go:</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Go data-lang=Go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>TestBruteForce</span>(t <span style=color:#555>*</span>testing.T) {
<span style=color:#069;font-weight:700>if</span> <span style=color:#c0f>bruteForce</span>(<span style=color:#f60>9</span>) <span style=color:#555>!=</span> <span style=color:#f60>23</span> {
t.<span style=color:#c0f>Fatal</span>(<span style=color:#c30>&#34;The sum of values from 1 to 10 which are divisible by 5 and 3 should be 23&#34;</span>)
}
Expand All @@ -31,7 +31,7 @@
--- PASS: TestBruteForce <span style=color:#555>(</span>0.00s<span style=color:#555>)</span>
PASS
ok _path/... 0.008s
</code></pre></div><h1 id=how-fast-is-it>How fast is it?</h1><p>This is enough for a working solution, and it isn&rsquo;t too slow, but it&rsquo;s not great as we can see by the built-in benchmark capabilities in Go.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Go data-lang=Go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>BenchmarkBruteForce</span>(b <span style=color:#555>*</span>testing.B) {
</code></pre></div><h1 id=how-fast-is-it class=anchor-link><a href=#how-fast-is-it>How fast is it?</a></h1><p>This is enough for a working solution, and it isn&rsquo;t too slow, but it&rsquo;s not great as we can see by the built-in benchmark capabilities in Go.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-Go data-lang=Go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>BenchmarkBruteForce</span>(b <span style=color:#555>*</span>testing.B) {
<span style=color:#069;font-weight:700>for</span> i <span style=color:#555>:=</span> <span style=color:#f60>0</span>; i &lt; b.N; i<span style=color:#555>++</span> {
<span style=color:#c0f>bruteForce</span>(<span style=color:#f60>999</span>)
}
Expand All @@ -45,7 +45,7 @@
}
</code></pre></div><p>Now we will have different timing and benchmark information.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-bash data-lang=bash>go <span style=color:#366>test</span> -bench .
BenchmarkBruteForce-4 <span style=color:#f60>3</span> <span style=color:#f60>441286594</span> ns/op
</code></pre></div><p>So by increasing the numbers we have to check by a factor of 1000, we now have a runtime of 441,286,594 ns/op instead of 227,761 ns/op, which is an increase of about 1900x. Not good. For more on asymptotic analysis of runtime, check out the Wikipedia article on <a href=https://en.wikipedia.org/wiki/Big_O_notation>Big O Notation</a>.</p><h1 id=performance-upgrades>Performance upgrades</h1><p>There are some options to improve the performance, like splitting the range of numbers into parts and spawning multiple worker threads to check each part, thereby parallelizing the work. This is a lot of complexity though, especially when you want to make sure that ranges checked don&rsquo;t overlap, managing potential shared state, and so on. If we think a bit more about the problem, there is a better way.</p><p>We want to sum all the multiples of 3 or 5 less than 1000. We can think about the sequence of multiples of 3 as (3, 6, 9, 12, 15, &mldr;, 999), which is the same as 3 * (1, 2, 3, 4, 5, &mldr;, 333). We can do the same for the case of multiples of 5. The benefit of considering the problem in this way is that the list of numbers has a closed-form solution (i.e., a formula) for calculating the sum.</p><p>Such a sequence, where the difference between each number is constant, is called a <a href=https://en.wikipedia.org/wiki/Arithmetic_progression>finite arithmetic progression</a> or finite arithmetic sequence and the sum of a finite arithmetic progression is called a finite arithmetic series. The formula for the sum is <code>1/2 * n * (a_1 + a_n)</code>. where <code>n</code> is the number of terms being added, <code>a_1</code> is the first element in the sequence, and <code>a_n</code> is the last element in the sequence.</p><p>From our example for multiples of 3, we know that <code>a_1 = 1</code> and we know that <code>a_n = floor(999/3) = 333</code> and we also know that the total number of elements in the sequence will be <code>n = floor(999/3) = 333 = a_n</code>. So for our purposes, the sum of our sequences is equal to <code>n * (n + 1) * 0.5</code>. We can make a small helper function to calculate this for us.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-go data-lang=go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>arithSum</span>(n <span style=color:#078;font-weight:700>float64</span>) <span style=color:#078;font-weight:700>float64</span> {
</code></pre></div><p>So by increasing the numbers we have to check by a factor of 1000, we now have a runtime of 441,286,594 ns/op instead of 227,761 ns/op, which is an increase of about 1900x. Not good. For more on asymptotic analysis of runtime, check out the Wikipedia article on <a href=https://en.wikipedia.org/wiki/Big_O_notation>Big O Notation</a>.</p><h1 id=performance-upgrades class=anchor-link><a href=#performance-upgrades>Performance upgrades</a></h1><p>There are some options to improve the performance, like splitting the range of numbers into parts and spawning multiple worker threads to check each part, thereby parallelizing the work. This is a lot of complexity though, especially when you want to make sure that ranges checked don&rsquo;t overlap, managing potential shared state, and so on. If we think a bit more about the problem, there is a better way.</p><p>We want to sum all the multiples of 3 or 5 less than 1000. We can think about the sequence of multiples of 3 as (3, 6, 9, 12, 15, &mldr;, 999), which is the same as 3 * (1, 2, 3, 4, 5, &mldr;, 333). We can do the same for the case of multiples of 5. The benefit of considering the problem in this way is that the list of numbers has a closed-form solution (i.e., a formula) for calculating the sum.</p><p>Such a sequence, where the difference between each number is constant, is called a <a href=https://en.wikipedia.org/wiki/Arithmetic_progression>finite arithmetic progression</a> or finite arithmetic sequence and the sum of a finite arithmetic progression is called a finite arithmetic series. The formula for the sum is <code>1/2 * n * (a_1 + a_n)</code>. where <code>n</code> is the number of terms being added, <code>a_1</code> is the first element in the sequence, and <code>a_n</code> is the last element in the sequence.</p><p>From our example for multiples of 3, we know that <code>a_1 = 1</code> and we know that <code>a_n = floor(999/3) = 333</code> and we also know that the total number of elements in the sequence will be <code>n = floor(999/3) = 333 = a_n</code>. So for our purposes, the sum of our sequences is equal to <code>n * (n + 1) * 0.5</code>. We can make a small helper function to calculate this for us.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-go data-lang=go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>arithSum</span>(n <span style=color:#078;font-weight:700>float64</span>) <span style=color:#078;font-weight:700>float64</span> {
<span style=color:#069;font-weight:700>return</span> (n <span style=color:#555>*</span> (n <span style=color:#555>+</span> <span style=color:#f60>1</span>)) <span style=color:#555>/</span> <span style=color:#f60>2</span>
}
</code></pre></div><p>Now we can simply compute the sum of the arithmetic sequence for all the multiples of 3 and 5 without iterating through anything at all. Since we already know the answers to the questions from the brute force case, we can also write the appropriate tests.</p><div class=highlight><pre style=background-color:#f0f3f3;-moz-tab-size:4;-o-tab-size:4;tab-size:4><code class=language-go data-lang=go><span style=color:#069;font-weight:700>func</span> <span style=color:#c0f>TestArithSeq</span>(t <span style=color:#555>*</span>testing.T) {
Expand Down
Loading

0 comments on commit 8802a8e

Please sign in to comment.