PWC 164 › Happy Numbers

This post is part of a series on Mohammad Anwar’s excellent Weekly Challenge, where hackers submit solutions in Perl, Raku, or any other language, to two different challenges every week. (It’s a lot of fun, if you’re into that sort of thing.)

Task #2 this week comes to us from Robert DiCicco, and Robert demands happiness! Or at least numbers that are happy. Either way, I could use some right about now.

We are to find the first 8 Happy Numbers in base 10. To test whether a number is Happy:

replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.

Those numbers for which this process end in 1 are happy numbers, while those numbers that do not end in 1 are unhappy numbers.

Weekly Challenge #164

If You’re Happy and You Know It return 1

Using the algorithm exactly as described above, the Perl code is straightforward:

sub is_happy(_) {
    my $n = shift;

    my %seen;
    for (my $c = $n; $c != 1; $c = sum map { $_*$_ } split //, $c) {
        return if $seen{$c}++;
    }

    return 1
}

$_ * $_ is an optimization I make reflexively. Computing powers (i.e., $_ ** 2), even for integers, is relatively slow (~15% on my VM), so for squaring a number, I often write out the multiplication by hand. It’s faster and does not suffer any real lack of clarity.

I achieve the loop detection with the %seen hash. If a number comes up more than once, we are in a loop. We keep computing the sum of squares of the digits and stop when we detect a loop (Not Happy), or when we reach 1 (Happy).

Outputting the First $count Happy Numbers

Our job would be a little easier if we were asked to output numbers below a given limit. But outputting the first n is not much harder. We’ll just loop until we’ve seen $count Happy numbers:

my $count = pop // 8;

# Output the first $count Happy Numbers
for (local $_ = 1; $count > 0 ; $_++) {
    next unless is_happy;
    say;
    $count--;
}

The highlighted line gives us an increasing number ($_), but the loop termination condition depends on another variable ($count). As much as we love our foreach-style loops, sometimes the good old fashioned C-style for ( ; ; ) loop is best!

I decided to prototype is_happy(_) for a bit of syntactic sugar. This was completely unnecessary, of course.

My complete solutions are on GitHub.

That’s it for Week #164! Tune in next week for some fun with graphics…

Leave a Reply

Your email address will not be published. Required fields are marked *