Fixing an Old Task

Abigail read my blog post and pointed out that the given first correct answer of #148 Task 2, `(2,1,5)` was not showing up, and when he ran my `test_ardano` function against it, it didn’t return `1`, but rather `1.00000000000000000011`.

I think the only time I regularly do math like this is in the Challenge, which is good because it makes me use ideas I don’t touch regularly, but it does get me in places I can’t negotiate out of. It’s a known thing that IEEE 754 math has hairy edge cases, and I’m guessing that I’m hanging up on that. I changed my `cuberoot` function to limit the number of significant digits —

``````sub cuberoot (\$n ) { return sprintf '%.06f', \$n**( 1 / 3 ) }
``````

— but that seems like a hackish “just make it work!” solution rather than really understanding where the problem is and fixing that. I admit that. When the Reviews come around, I’ll have to read to see the better Cardano Triplets solutions.

Thanks to Abigail for pointing out the problem.

TASK #1 › Fibonacci Digit Sum

Submitted by: Roger Bell_West Given an input \$N, generate the first \$N numbers for which the sum of their digits is a Fibonacci number.

I got this to an acceptable point fairly quickly. I use `split` to separate the numbers into digits, `sum0` from List::Util because I am always paranoid about empty strings and a function that finds the Fibonacci number that is not less than the given number, which would be the `sum0` of the digits. If the function returns a number that is sum, then there we go! We append it to an array, and stop looking when the array is big enough.

Show Me The Code!

``````#!/usr/bin/env perl

use strict;
use warnings;
use feature qw{ say postderef signatures state };
no warnings qw{ experimental };

use Getopt::Long;
use List::Util qw{ sum0 max };

my \$N = 20;
GetOptions( 'n=i' => \\$N, );

my @fib = first_60_fib();
my %fib = map { \$_ => 1 } @fib;
my @x;

my \$n = 0;
while ( scalar @x < \$N ) {
my \$sd = sum_of_digits(\$n);
my \$f  = \$fib{\$sd} || 0;
push @x, \$n if \$f;
\$n++;
}
say join ' ', @x;

sub first_60_fib() {
my @n;
push @n, 0;
push @n, 1;
while ( scalar @n < 60 ) {
push @n, \$n[-1] + \$n[-2];
}
return @n;
}

sub sum_of_digits ( \$n ) {
return sum0 split //, \$n;
}
``````
``````\$ ./ch-1.pl -n 30
0 1 2 3 5 8 10 11 12 14 17 20 21 23 26 30 32 35 41 44 49 50 53 58 62 67 71 76 80 85
``````

TASK #2 › Largest Square

Submitted by: Roger Bell_West Given a number base, derive the largest perfect square with no repeated digits and return it as a string. (For base>10, use ‘A’..‘Z’.)

This is giving me problems.

There are parts that are fairly simple. You get the 36 characters we can get with `my @range = ( 0 .. 9, 'A' .. 'Z' )`, and you can get the right characters for any base with `my @range_by_base = @range[0..\$base-1]`. The highest possible correct number becomes `join '', reverse @range_by_base`.

I found that many of the Base Conversion modules convert from and to common CS-related bases — 2, 4, 8, 16, 32, etc. — but for this task, we want to convert into and out of any base, and I found that Math::BaseCalc does that well.

Using `state`, I made functions that convert back and forth, and hold onto the numbers, so we don’t have to re-generate `100` in base 19 twice. (In writing this, I’m second-guessing the utility of that, but I’ve done the cool thing, so eh?)

But there’s still going from 9,876,543,210 to 1, in the case of base 10. I am doing it with a for loop and an implicit 10-million-entry list, and that’s killing me. I think that a while loop instead, like `while (\$n > 1) { \$n-- }` might be it. In fact, it’s looking promising (and not segfaulting) as I write this. When and if I solve it, I’ll blog it separately.