Wednesday, August 5, 2009

Project Euler #17, Take Three

So, this version starts with the last version and applies a bit of smarts. Essentially, I said to myself, "Hey! The number of letters in the numbers 100 through 199 are the same as the number of letters in 1 through 99 plus 100 times the number of letters in "one hundred". I reworked the code to take advantage of that. Then I realized that the NumberLength function only needed to work on numbers between 1 and 99 inclusive, and reworked that as a special case. And as soon as I did that, it was obvious that NumberLength only had two cases, one for numbers between 1 and 20, one for 21 to 99, and just made two loops each of which had the appropriate piece of NumberLength code inside.
use v6;
my %number_lengths = (
0 => 0, # special case -- we only hit this one as the $ones on a multiple of ten
1 => 3,
2 => 3,
3 => 5,
4 => 4,
5 => 4,
6 => 3,
7 => 5,
8 => 5,
9 => 4,
10 => 3,
11 => 6,
12 => 6,
13 => 8,
14 => 8,
15 => 7,
16 => 7,
17 => 9,
18 => 8,
19 => 8,
20 => 6,
30 => 6,
40 => 5,
50 => 5,
60 => 5,
70 => 7,
80 => 6,
90 => 6
);
my $one_to_ninety_nine = 0;
for 1..19 -> $number
{
$one_to_ninety_nine += %number_lengths{$number};
}
for 20..99 -> $number
{
my $ones = $number % 10;
$one_to_ninety_nine += %number_lengths{$number - $ones} + %number_lengths{$ones};
}
my $letter = 10 * $one_to_ninety_nine;
for 1..9 -> $hundreds
{
# 7 below is for "hundred"
$letter += (%number_lengths{$hundreds} + 7) * 100;
}
$letter += 11; # 11 is for "one thousand"
say $letter;
view raw euler_017_03.pl hosted with ❤ by GitHub

The resulting code runs in just a shade under two seconds on my MBP. That's a solid 90x improvement over my initial code. But Rakudo's overhead for starting the script is over one second. If you subtract that from all runs, this current version is under a second, and very close to be 200x faster than the original script.

No comments:

Post a Comment