Jekyll2019-05-16T01:02:19+00:00https://rage.powered.ninja/feed.xmlRage Powered NinjaCode musings of a disillusioned web developer.Francis WhittleSquaring up to the Perfect Centre – Perl weekly challenge, week 82019-05-15T22:34:24+00:002019-05-15T22:34:24+00:00https://rage.powered.ninja/2019/05/15/-squaring--perfect-centre<p>Blogging back, for the <a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-008/">eighth Perl weekly
challenge</a>.</p>
<p>This week our two challenges were to calculate the first 5 perfect numbers, and
to centre a series of lines on the page.</p>
<h2 id="perfect-numbers">Perfect Numbers</h2>
<p>I’m a little late to the blog this week, so I’ve had a look at what other people
did before writing this up (I did my solution before checking out others’), and
it looks like a number of people tried to filter the list of positive integers
directly. As they discovered, this is mostly fine for the first four perfect
numbers, but the fifth… takes a while to discover this way. I didn’t run into
this timing issue, because I like to generate.</p>
<h3 id="how-do-generate-perfect-number">How do generate perfect number?</h3>
<p>The first step is to find an algorithm. This is pretty well documented on the
<a href="https://en.wikipedia.org/wiki/Perfect_number">Wikipedia page</a>:</p>
<ul>
<li>Euclid proved that all numbers of the form <em>q</em>(<em>q</em> + 1) / 2 are perfect numbers
where <em>q</em> is (what would later be known as) a Mersenne Prime.</li>
<li>Much more recently, Euler proved that in fact <em>all</em> perfect numbers are formed
like this.</li>
</ul>
<p>So the answer then is to generate Mersenne primes, and calculate perfect numbers
using these.</p>
<h3 id="lazily-we-mersenne">Lazily we Mersenne</h3>
<p>A Mersenne prime is of the form 2<em><sup>p</sup></em> - 1 where <em>p</em> is a prime number. So we
lazily gather a list of all prime numbers up to ∞, check if they’re prime, apply
the formula, and then check if the result is prime as well, because the sequence
<strong>can</strong> produce composite numbers, but Mersenne numbers / primes are always
prime.</p>
<p>I bound this to the term <code class="highlighter-rouge">M</code>:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="k">my</span> <span class="o">\</span><span class="nv">M</span> <span class="p">:</span><span class="o">=</span> <span class="p">(</span><span class="o">^</span><span class="err">∞</span><span class="p">)</span>
<span class="o">.</span><span class="nb">grep</span><span class="p">(</span><span class="o">*.</span><span class="nv">is</span><span class="o">-</span><span class="nv">prime</span><span class="p">)</span>
<span class="o">.</span><span class="nb">map</span><span class="p">(</span><span class="o">-></span> <span class="nv">$n</span> <span class="p">{</span> <span class="mi">2</span> <span class="o">**</span> <span class="nv">$n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">})</span>
<span class="o">.</span><span class="nb">grep</span><span class="p">(</span><span class="o">*.</span><span class="nv">is</span><span class="o">-</span><span class="nv">prime</span><span class="p">);</span></code></pre></figure>
<h3 id="perfect-map">Perfect Map</h3>
<p>The next step is to bind a mapping of the Mersenne primes to the corresponding
perfect number:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="k">my</span> <span class="o">\</span><span class="nv">P</span> <span class="p">:</span><span class="o">=</span> <span class="nv">M</span><span class="o">.</span><span class="nb">map</span><span class="p">:</span> <span class="o">-></span> <span class="nv">$q</span> <span class="p">{</span> <span class="nv">$q</span> <span class="o">*</span> <span class="p">(</span><span class="nv">$q</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="nv">div</span> <span class="mi">2</span> <span class="p">};</span></code></pre></figure>
<p>I used <code class="highlighter-rouge">div</code> so the result is <code class="highlighter-rouge">Int</code> not <code class="highlighter-rouge">Rat</code>.</p>
<h3 id="finally-get-the-results">Finally, get the results</h3>
<p><code class="highlighter-rouge">P</code> is now a lazily generated array that will find the <em>n</em>th Perfect
number as <code class="highlighter-rouge">P[n]</code>. <code class="highlighter-rouge">^5</code> gives a list of the first 5, so</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="nv">P</span><span class="p">[</span><span class="o">^</span><span class="mi">5</span><span class="p">]</span><span class="err">»</span><span class="o">.</span><span class="nv">put</span></code></pre></figure>
<p>Gets the first 5 perfect numbers, then prints each one on its own line.</p>
<h2 id="text-centring">Text centring</h2>
<p>Nothing special here. Like most other people, I found the maximum line length,
then padded the average of this and the line length on the left.
Only things I did different from what I can see:</p>
<ul>
<li>Used sprintf. The <code class="highlighter-rouge">%*s</code> format specifier lets me pass in the amount of
padding without interpolation.</li>
<li>Filtered through <code class="highlighter-rouge">.trim</code> to get rid of any surrounding spaces, because
paranoia.</li>
</ul>
<h2 id="optional-api-challenge">Optional API challenge</h2>
<p>Writing a simple client for the Mailgun API is distressingly similar to the kind
of work I do in <code class="highlighter-rouge">$day-job</code>, so I’ve opted not to do this one, as it would be a
sad reminder that life would be so much better if I didn’t have to PHP.</p>FrancisBlogging back, for the eighth Perl weekly challenge.Anagramming to the Max – Perl weekly challenge, week 52019-04-22T05:36:33+00:002019-04-22T05:36:33+00:00https://rage.powered.ninja/2019/04/22/anagramming--max<h2 id="some-repetition-for-challenge-one">Some repetition for challenge one.</h2>
<p>As <a href="/2019/04/15/no-pi-file.html#letters-in-words-in-list-in--">hinted at in my previous entry</a>, the difference between finding words that
contain a particular sequence (in any order) and finding anagrams is somewhat
minor.</p>
<p>In fact, the only appreciable difference between my
<a href="https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-004/fjwhittle/perl6/ch-2.p6">week 4 challenge 2 solution</a>
and my <a href="https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-005/fjwhittle/perl6/ch-1.p6">week 5 challenge 1 solution</a>
in context is the replacement of a <code class="highlighter-rouge">⊆</code> operator with a <code class="highlighter-rouge">===</code> operator.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ perl6 ch-1.p6 /usr/share/dict/words top
opt, pot, top
</code></pre></div></div>
<h2 id="build-it-up-to-tear-it-down-in-challenge-two">Build it up to tear it down in challenge two.</h2>
<p>I feel like what I ended up doing for challenge two was somewhat in the Perl 5
idiom. Each line of the file is processed into a sorted sequence of letters,
which is used as a key in a Hash of <code class="highlighter-rouge">SetHash</code>es where the key of the second
dimension is the <code class="highlighter-rouge">.lc</code>ed word itself to avoid counting duplicates.</p>
<p>Once the Hash is built, the maximum number of elements is found, and each entry
with that number of elements is printed out — I’ve used a formatter to show the
sequences, the number of matched words, and the words themselves.</p>
<p>See the full solution at <a href="https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-005/fjwhittle/perl6/ch-2.p6">https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-005/fjwhittle/perl6/ch-2.p6</a></p>FrancisSome repetition for challenge one.Number of Pi in file – Perl weekly challenge, week 42019-04-15T00:53:34+00:002019-04-15T00:53:34+00:00https://rage.powered.ninja/2019/04/15/no-pi-file<p>A new challenge appears!</p>
<h2 id="how-much-π-in-your-script">How much π in your script?</h2>
<p>At first glance, part 1 of this challenge is pretty simple:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="nb">printf</span> <span class="s">"%.16f"</span><span class="p">,</span><span class="nv">pi</span></code></pre></figure>
<p>Of course, I had to create the file using echo to avoid have a newline at the
end.
Look close and there’s a problem though.</p>
<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><span class="nv">$ </span>perl6 ch-1.p6
3.1415926535897930</code></pre></figure>
<p>That’s <strong>almost</strong> but not <em>quite</em> 16 significant digits of pi. I’m keeping this
as a first solution though, because IMHO it <em>should</em> be the answer.</p>
<p>Okay, time to break out the cynicallest solution for perl6:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="nv">pi</span><span class="o">.\</span> <span class="nv">put</span></code></pre></figure>
<p>15 significant digits of π for 15 characters, including the line break 🤪.</p>
<p>Now let’s get serious. Number sequences were actually one of my favourite
parts of Mathematics, and when the world presents me with an opportunity to
strike a Calculating π shaped nail, I’m going to hit it with my convergent
sequence hammer.</p>
<h3 id="isnt-that-a-bit-far-away-from-the-original-question">Isn’t that a bit far away from the original question?</h3>
<p>Yeah, I doubt this was the challenge’s intention; I’m doing it anyway.</p>
<p>Interestingly enough, revisiting <em>last</em> week’s challenge brought up something
about calculating π using Pascal’s Triangle; apparently since I was last doing
things with Blaise Pascal’s work in high school, <a href="https://www.cut-the-knot.org/arithmetic/algebra/TriPiInPascal.shtml">one Jonas Castillo Toloza
discovered another place it exposed
π</a> – in the
triangular numbers.</p>
<p>I implemented this in Perl6; it went something like this:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="k">my</span> <span class="nv">$Ts</span> <span class="p">:</span><span class="o">=</span> <span class="nv">lazy</span> <span class="nv">gather</span> <span class="nv">loop</span> <span class="p">(</span><span class="k">my</span> <span class="nv">$n</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;;)</span> <span class="p">{</span>
<span class="k">for</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span> <span class="o">-></span> <span class="nv">$m</span> <span class="p">{</span>
<span class="nv">take</span> <span class="p">(</span><span class="nv">$m</span> <span class="sr">/ ($n * ($n + 1) /</span> <span class="mi">2</span><span class="p">));</span>
<span class="nv">$n</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">my</span> <span class="nv">$</span><span class="err">π</span> <span class="err">=</span> <span class="nv">2</span> <span class="o">+</span> <span class="p">[</span><span class="o">+</span><span class="p">]</span> <span class="nv">$Ts</span><span class="p">[</span><span class="o">^</span><span class="nv">$lim</span><span class="p">];</span></code></pre></figure>
<p>Which is functional, and if you try out a couple of different sizes of $lim you
can see it converging; keeping in mind that Perl6’s <code class="highlighter-rouge">pi</code> is apparently accurate
to the 15 digits it <strong>has</strong>:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>> pi
3.141592653589793
> 2 + [+] $Ts[^2000]
3.1415921540896665
> 2 + [+] $Ts[^80000] # Jump of 40x, 3 digits closer
3.1415926532772707
> 2 + [+] $Ts[^160000] # 2x, one more digit
3.1415926535116068
> 2 + [+] $Ts[^320000] # 2x, closer but 11th digit is still wrong
3.141592653570272
> 2 + [+] $Ts[^640000] # 2x again, there we go
3.1415926535849534
> 2 + [+] $Ts[^2**20] # 1048576; many iterations
3.1415926535880487
> pi
3.141592653589793
</code></pre></div></div>
<p>… that’s a whole megasequence to still be accurate accurate to only 12 digits. That
speed of convergence is effectively nonfunctional.</p>
<p>I’ve used a much faster algorithm in the past; The Euler Convergence
Transformation, which is:</p>
<figure>
<math>
<semantics>
<mrow class="MJX-TeXAtom-ORD">
<mstyle displaystyle="true" scriptlevel="0">
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mi>π</mi>
<mn>2</mn>
</mfrac>
</mrow>
<mo>=</mo>
<munderover>
<mo>∑</mo>
<mrow class="MJX-TeXAtom-ORD">
<mi>k</mi><mo>=</mo><mn>0</mn>
</mrow>
<mrow class="MJX-TeXAtom-ORD">
<mi mathvariant="normal">∞</mi>
</mrow>
</munderover>
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mrow>
<mi>k</mi><mo>!</mo>
</mrow>
<mrow>
<mo stretchy="false">(</mo><mn>2</mn><mi>k</mi><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo><mo>!</mo><mo>!</mo>
</mrow>
</mfrac>
</mrow>
<mo>=</mo>
<munderover>
<mo>∑</mo>
<mrow class="MJX-TeXAtom-ORD">
<mi>k</mi><mo>=</mo><mn>0</mn>
</mrow>
<mrow class="MJX-TeXAtom-ORD">
<mi mathvariant="normal">∞</mi>
</mrow>
</munderover>
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mrow>
<mpadded width="0" height="8.6pt" depth="3pt">
<mrow></mrow>
</mpadded>
<mstyle displaystyle="false" scriptlevel="0">
<mrow class="MJX-TeXAtom-ORD">
<msup>
<mn>2</mn>
<mrow class="MJX-TeXAtom-ORD">
<mi>k</mi>
</mrow>
</msup>
<mi>k</mi>
<msup>
<mo>!</mo>
<mrow class="MJX-TeXAtom-ORD">
<mn>2</mn>
</mrow>
</msup>
</mrow>
</mstyle>
</mrow>
<mrow>
<mpadded width="0" height="8.6pt" depth="3pt">
<mrow></mrow>
</mpadded>
<mstyle displaystyle="false" scriptlevel="0">
<mrow class="MJX-TeXAtom-ORD">
<mo stretchy="false">(</mo><mn>2</mn><mi>k</mi><mo>+</mo><mn>1</mn><mo stretchy="false">)</mo><mo>!</mo>
</mrow>
</mstyle>
</mrow>
</mfrac>
</mrow>
<mo>=</mo>
<mn>1</mn>
<mo>+</mo>
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mn>1</mn>
<mn>3</mn>
</mfrac>
</mrow>
<mrow>
<mo>(</mo>
<mrow>
<mn>1</mn><mo>+</mo>
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mn>2</mn>
<mn>5</mn>
</mfrac>
</mrow>
<mrow>
<mo>(</mo>
<mrow>
<mn>1</mn><mo>+</mo>
<mrow class="MJX-TeXAtom-ORD">
<mfrac>
<mn>3</mn>
<mn>7</mn>
</mfrac>
</mrow>
<mrow>
<mo>(</mo>
<mrow>
<mn>1</mn><mo>+</mo><mo>⋯</mo>
</mrow>
<mo>)</mo>
</mrow>
</mrow>
<mo>)</mo>
</mrow>
</mrow>
<mo>)</mo>
</mrow>
</mstyle>
</mrow>
</semantics>
</math>
</figure>
<p>(Where <em>k!!</em> is the odd-only factorial of <em>k</em>)</p>
<p>Now, we <strong>could</strong> implement the sequence discretely according to probably the
second summation (this <em>k!!</em> seems a bit of a pain to implement), but if you
squint a bit at the right hand sequence, it looks a bit like:</p>
<figure>
<math>
<semantics>
<mstyle displaystyle="true">
<mrow>
<mfrac>
<mn>π</mn>
<mn>2</mn>
</mfrac>
<mo>=</mo>
<mrow>
<munderover>
<mo>∑</mo>
<mrow>
<mi>k</mi><mo>=</mo><mn>0</mn>
</mrow>
<mrow>
<mi mathvariant="normal">∞</mi>
</mrow>
</munderover>
<msub><mi>n</mi><mi>k</mi></msub>
</mrow>
<mo>where</mo>
<mrow>
<msub><mi>n</mi><mn>0</mn></msub><mo>=</mo><mn>1</mn>
</mrow>
<mo>; </mo>
<mrow>
<msub><mi>n</mi><mi>k</mi></msub>
<mo>=</mo>
<msub><mi>n</mi><mrow><mi>k</mi><mo>-</mo><mn>1</mn></mrow></msub>
<mo>·</mo>
<mfrac>
<mi>k</mi>
<mrow><mn>2</mn><mi>k</mi><mo>+</mo><mn>1</mn></mrow>
</mfrac>
</mrow>
</mrow>
</mstyle>
</semantics>
</math>
</figure>
<p>Which saves us some calculation as during a gather / take we already know what
<em>n<sub>k-1</sub></em> is. Throw it together, sum the results, multiply by two, and
Bob’s yer uncle.</p>
<p>While we’re at it, we’ll use FatRats to be away with those pesky IEE754 floats
and their imprecision.</p>
<p>Through some analysis I found that 608 iterations was enough to give me 177
correct significant digits, which is the final size of the script; that’s quite
a lot better than 1M iterations to get 12…</p>
<h3 id="algorithmic-script-size">Algorithmic Script Size</h3>
<p>‘Enough tedious maths!’ you say, ‘tell us something about programming!’</p>
<p>Cool feature of Perl 6 – you can find the size of the current script using
<code class="highlighter-rouge">$?FILE.IO.s</code>. To round out the script I used that (+ 1 for the decimal point)
as the end of the substring of my calculated π.</p>
<p>See my solution at
<a href="https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-004/fjwhittle/perl6/ch-1.p6">https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-004/fjwhittle/perl6/ch-1.p6</a></p>
<h2 id="letters-in-words-in-list-in--">Letters in words in list in … ?</h2>
<p>Part 2 of the week 4 challenge starts with “You are given a file containing a
list of words (case insensitive 1 word per line) and a list of letters” however
doesn’t provide any such data. I have to admit I’m not a big fan of challenges
with undefined data sets, although I can also certainly appreciate that
providing data for testing is potentially not something challenge authors have
time for. The challenge itself is fairly similar to an anagram generator I had a
go at making for my own fun some time back (around the release of 6.c).</p>
<h3 id="finding-data">Finding data.</h3>
<p>Hmm. Where would I find a list of words lying around waiting to be used?</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ time perl6 -e 'put + "/usr/share/dict/british-english".IO.lines.unique'
101921
real 0m0.593s
user 0m0.671s
sys 0m0.065s
</code></pre></div></div>
<p>Seems reasonable. Next step is to get a list of letters… Oh, let’s just use
command line arguments.</p>
<p>One of my favourite things about Perl 6 is how easy it is to get arguments from
the command line. I normally make my main script file <code class="highlighter-rouge">unit sub MAIN();</code>, which
lets me treat it otherwise like a script. I can then use Pod to document the
options as well.</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="c1">#| Find words in a file that contain only the given letters</span>
<span class="nv">unit</span> <span class="k">sub </span><span class="nf">MAIN</span><span class="err">(</span>
<span class="nf">Str</span> <span class="err">$</span><span class="nf">file</span> <span class="err">#=</span> <span class="nf">file</span> <span class="nf">containing</span> <span class="nf">list</span> <span class="nf">of</span> <span class="nf">words</span>
<span class="nf">where</span> <span class="p">{</span> <span class="nv">given</span> <span class="o">.</span><span class="nv">IO</span> <span class="p">{</span> <span class="o">.</span><span class="nv">r</span> <span class="o">&&</span> <span class="p">(</span> <span class="o">.</span><span class="nv">l</span> <span class="o">||</span> <span class="o">.</span><span class="nv">f</span><span class="p">)</span> <span class="ow">or</span> <span class="nb">die</span> <span class="s">"Cannot read from $_"</span> <span class="p">}</span> <span class="p">}</span> <span class="c1">#= file containing list of words</span>
<span class="o">*</span><span class="nv">@letters</span> <span class="c1">#= list of letters to search for</span>
<span class="p">);</span></code></pre></figure>
<p>We’ll approach the problem using Bags and the subsets thereof. We use Bags
instead of Sets to count duplicate letters.</p>
<p>We’ll comb through each of the gobbled arguments for letters, which will allow
the user to enter words on the command line, so map the @letters array to
lower-case, comb each argument for letters, flatten, and convert to a bag..</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="k">my</span> <span class="nv">$letter</span><span class="o">-</span><span class="nv">bag</span> <span class="p">:</span><span class="o">=</span> <span class="nv">@letters</span><span class="o">.</span><span class="nv">hyper</span><span class="o">.</span><span class="nb">map</span><span class="p">(</span><span class="o">*.</span><span class="nb">lc</span><span class="o">.</span><span class="nv">comb</span><span class="p">(</span><span class="sr">/ \w /</span><span class="p">))</span><span class="o">.</span><span class="nv">flat</span><span class="o">.</span><span class="nv">Bag</span><span class="p">;</span></code></pre></figure>
<p><code class="highlighter-rouge">.hyper</code> makes the <code class="highlighter-rouge">.map</code> happen in parallel. Maybe not a huge deal for this
step, however when now building a list of word bags out of a 102000 line file it
makes the 30 second process take 10 (on my system – an old Athlon II. It
doesn’t even have SSEv4, but it <strong>does</strong> have 4 cores).</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="k">my</span> <span class="nv">@words</span> <span class="o">=</span> <span class="nv">$file</span><span class="o">.</span><span class="nv">IO</span><span class="o">.</span><span class="nv">lines</span><span class="o">.</span><span class="nv">unique</span><span class="o">.</span><span class="nv">hyper</span><span class="o">.</span><span class="nb">grep</span><span class="p">(</span><span class="o">*.</span><span class="nv">chars</span> <span class="o">></span> <span class="mi">2</span><span class="p">)</span>
<span class="o">.</span><span class="nb">map</span><span class="p">:</span> <span class="p">{</span> <span class="o">.</span><span class="nb">lc</span><span class="o">.</span><span class="nv">comb</span><span class="p">(</span><span class="sr">/ \w /</span><span class="p">)</span><span class="o">.</span><span class="nv">Bag</span> <span class="o">=></span> <span class="nv">$_</span> <span class="p">};</span></code></pre></figure>
<p>Use of a pair here allows us to map a word bag to the unmodified word
preserving order, punctuation, and case.
It’s now a pretty simple task to find our words; we just grep the <code class="highlighter-rouge">@words</code> list
for a key that’s a subset of or equal to the <code class="highlighter-rouge">$letter-bag</code> using the <code class="highlighter-rouge">⊆</code>
operator, and print out the corresponding values.</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ perl6 ch-2.p6 /usr/share/dict/british-english hello dolly
Dell, Doe, Dole, Dolly, Dooley, Doyle, Eloy, Hell, Holley, Holly, Hood, Hoyle,
Hyde, Leo, Lloyd, Loyd, Lyell, Lyle, Odell, doll, dye, ell, he'd, he'll, held,
hello, hey, hod, hoe, hoed, hold, hole, holed, holy, hooey, led, lode, loll,
lolled, lye, ode, oho, old, oleo, yell, yodel
</code></pre></div></div>
<p>Looks good to me 😊. See the full solution at <a href="https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-004/fjwhittle/perl6/ch-2.p6">https://github.com/fjwhittle/perlweeklychallenge-club/blob/master/challenge-004/fjwhittle/perl6/ch-2.p6</a></p>
<script type="text/javascript" async="" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=MML_HTMLorMML">
</script>FrancisA new challenge appears!Hamming it up in Perl 6 – the weekly challenge, week 32019-04-12T12:08:00+00:002019-04-12T12:08:00+00:00https://rage.powered.ninja/2019/04/12/hamming-it-up-in-perl-6-weekly<p>Okay, so first up, I started with the <a href="https://perlweeklychallenge.org/">Perl weekly challenge</a> in its <a href="https://perlweeklychallenge.org/blog/perl-weekly-challenge-003/">third week</a>; I hadn’t heard of it in week 1, and wasn’t sure if I should submit in week 2 (also as noted on the official blog, week 2 was absurdly simple Perl 6, and I’ll be focusing on that).</p>
<h2 id="generating-some-5-smooth-numbers">Generating some 5-smooth numbers</h2>
<p>My first thought was to naïvely loop through integers, starting from 1, until we found the appropriate numbers. Pretty simple:</p>
<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="p">(</span><span class="nv">gather</span> <span class="k">for</span> <span class="mi">1</span><span class="o">..*</span> <span class="o">-></span> <span class="nv">$k</span> <span class="p">{</span> <span class="k">my</span> <span class="nv">$n</span> <span class="o">=</span> <span class="nv">$k</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">-></span> <span class="nv">$x</span> <span class="p">{</span> <span class="nv">$n</span> <span class="o">/=</span> <span class="nv">$x</span> <span class="k">until</span> <span class="nv">$n</span> <span class="nv">%</span> <span class="err">$</span><span class="nv">x</span> <span class="p">}</span>
<span class="nv">$n</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="nv">take</span> <span class="nv">$k</span>
<span class="p">})[</span><span class="nv">$i</span><span class="p">]</span><span class="o">.</span><span class="nv">put</span></code></pre></figure>
<p>(Yes, that’s an implicit lazy list 😊)
This is pretty effective, and actually reasonably fast for small numbers; It finds the 52nd (<code class="highlighter-rouge">$i = 51 / 256</code>) in .037s on my aging Athlon II (not counting compile time).</p>
<p>However, as a solution, it does not scale well. Up to around the index mentioned above, it beats the algorithmic solution I’m about to talk about, but as the density of 5-smooth numbers decreases, the efficiency decreases, and the naïve loop gets exponentially slower; Finding the 104th number takes ~1.6s; The 208th, nearly 16. The 1691st which the Rosetta Code page specifies because it’s the highest 5-smooth number below 2³¹ takes so long that I gave up in timing it. The millionth… who knows?</p>
<h2 id="enter-hammings-problem">Enter Hamming’s problem</h2>
<p>The Wikipedia page on 5-smooth / Regular / Hamming numbers <a href="https://en.wikipedia.org/wiki/Regular_number#Algorithms">describes an Algorithm</a> postulated by Dijkstra to (of course) describe the set of Hamming numbers as a recursive sequence made up of three smaller sequences – essentially you start with the first Hamming number (1), then for each of 2 3 and 5, multiply by the known set of Hamming numbers ad nauseam to increase the length of those sub-sequences.</p>
<p>I’ll note now that there’s actually a slightly different <a href="https://rosettacode.org/wiki/Hamming_numbers#Perl_6">Perl6 solution on Rosetta Code</a>, which calculates the powers of 2, 3, and 5 up to a predefined limit and merge sorts the three sequences together. The results are correct, but will always calculate the same sized sequence, which may be either too small or larger than you need. Maybe when a challenge winner is determined we can update Rosetta Code with that solution.</p>
<p>On the other hand, Perl 6 makes this pretty easy with gather / take because you can reference a list inside its own gather block, allowing you to to just keep multiplying by 2 3 and 5 for as long as you need (<code class="highlighter-rouge">@results = @results X* (2,3,5)</code>). Unfortunately, even at the second iteration, the results will come out of order and with duplicates (1 2 3 5 4 6 10 6 9 10 15 25). We can deal with the duplicates to an extent, but ultimately need to sort and uniquify the entire list every iteration. This ⓐ forces the list to be eager, ⓑ is as slow if not slower than the sieve we implemented earlier, and ⓒ is somewhat inelegant. I actually went through a couple of iterations of this anyway and found that a pure Perl 6 merge algorithm that sliced the next three results into the appropriate array indices was much faster than calling sort (also a merge sort) on the entire array. Still not fast enough though.</p>
<p>I had a look at implementing a solution with competing Channels or Supplies, but as Wikipedia said, “explicitly concurrent generative solutions might be non-trivial,” and I kept hitting dead ends along the lines of ‘how do I make it deliver the correct next result and stop at the right time.’</p>
<p>So then I sort-of cheated and had a peek and the Python implementations mentioned by Wikipedia for inspiration.
It took me a while to figure out what it was doing, but after a while (I won’t lie, it was something like 6 hours – I’m not great at Python), I figured out it was merging the results of three generators to yield the minimum result of those.</p>
<p>Perl 6 does’t really do generators the way that Python does, and I decided quickly that nesting gathers wasn’t likely to work well. The solution was to have concurrent iterators on the list being gathered, and pick the smallest result. A couple of variations on if/elsif on three dimensions, I came to the realisation that I could just .min the three next results and advance the iterators that matched this result.</p>
<p>With some added benchmarking, we can see it’s pretty quick:</p>
<div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 0.034s 26: 60
0.040s 52: 256
0.061s 104: 1800
0.068s 208: 18750
0.105s 1691: 2125764000
17.552s 1000000: 519312780448388736089589843750000000000000000000000000000000000000000000000000000000
</code></pre></div></div>
<p><strong>This</strong> takes a little longer to calculate the millionth number than our sieve took to find the 208th. That’s performance progress.</p>
<p>See the full solution at <a href="https://github.com/manwar/perlweeklychallenge-club/blob/master/challenge-003/fjwhittle/perl6/ch-1.p6">https://github.com/manwar/perlweeklychallenge-club/blob/master/challenge-003/fjwhittle/perl6/ch-1.p6</a></p>
<h2 id="generating-pascals-triangle">Generating Pascal’s Triangle</h2>
<p>This took much less effort. There’s a lot of algorithms for generating Pascal’s Triangle. I chose one that allowed me to not inspect the previous row:</p>
<figure>
<math>
<semantics>
<mrow>
<mstyle displaystyle="true" scriptlevel="0">
<mrow><mrow><mo>(</mo></mrow>
<mfrac linethickness="0px"><mi>n</mi><mi>k</mi></mfrac>
<mrow><mo>)</mo></mrow>
</mrow>
<mo>=</mo>
<mrow>
<mrow><mo>(</mo></mrow>
<mfrac linethickness="0px">
<mi>n</mi>
<mrow><mi>k</mi><mo>−</mo><mn>1</mn></mrow>
</mfrac>
<mrow><mo>)</mo></mrow>
</mrow>
<mo>×</mo>
<mrow>
<mfrac>
<mrow><mi>n</mi><mo>+</mo><mn>1</mn><mo>−</mo><mi>k</mi></mrow>
<mi>k</mi>
</mfrac>
</mrow>
</mstyle>
</mrow>
</semantics>
</math>
</figure>
<p>… and made it into Perl6. See <a href="https://github.com/manwar/perlweeklychallenge-club/blob/master/challenge-003/fjwhittle/perl6/ch-2.p6">https://github.com/manwar/perlweeklychallenge-club/blob/master/challenge-003/fjwhittle/perl6/ch-2.p6</a>, noting that the formula is 1-indexed and Perl6 is 0-indexed (except I made $n 1-indexed).</p>
<script type="text/javascript" async="" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=MML_HTMLorMML">
</script>FrancisOkay, so first up, I started with the Perl weekly challenge in its third week; I hadn’t heard of it in week 1, and wasn’t sure if I should submit in week 2 (also as noted on the official blog, week 2 was absurdly simple Perl 6, and I’ll be focusing on that).Introducing….2014-06-04T10:27:00+00:002014-06-04T10:27:00+00:00https://rage.powered.ninja/2014/06/04/introducingVexed, the wind blows,<br />
an age to become one;<br />
Run out of ideas,<br />
more later with awesome.FrancisVexed, the wind blows, an age to become one; Run out of ideas, more later with awesome.