Happy numbers - An natural introspection with Raku

in StemSocial2 years ago

I happened to stumble upon another blog by Arne Sommer https://raku-musings.com/primarily-happy.html. I liked the second task of the challenge. Which was
https://theweeklychallenge.org/blog/perl-weekly-challenge-164/#TASK2

Task 2: Happy Numbers
Submitted by: Robert DiCicco

Write a script to find the first 8 Happy Numbers in base 10. For more information, please check out Wikipedia.

Starting with any positive integer, replace the number by the sum of the squares of its digits, repeat the process until the number equals 1 (where it will stay), or loop endlessly in a cycle that does not include 1.

Those numbers for which this process ends in 1 are happy numbers, while those numbers that do not end in 1 are unhappy numbers or just sad.
Example

19 is a Happy Number in base 10, as shown:

19 => 1^2 + 9^2
   => 1   + 81
   => 82 => 8^2 + 2^2
         => 64  + 4
         => 68 => 6^2 + 8^2
               => 36  + 64
               => 100 => 1^2 + 0^2 + 0^2
                      => 1 + 0 + 0
                      => 1

My first natural instinct was to use an iterator like data structure, where each entry would hold:

  1. if it was happy
  2. next number is generated by adding the squares of its digits
  3. a number of iterations it should take to reach happiness or an inescapable cycle of sadness.

But in this blog, the author used loop limits. This Saturday I ventured to mod his script, and it was fun to dabble in Raku as always.

Hash to store the happiness

1 is an invariantly happy number because it does not need any iterations to prove itself.
Hence, initializing the Hash as:

my %happiness = 1 => %(next=>1,iter=>0,:happy);

happiness is a pattern that does not depend on the number of 0's you have, nor does it depend on a particular arrangement. just on the digits, you have. So, to distill a number to that soup of ordered digits:
my $normalize-num = *.base($base).comb.grep(*!~~0).sort.join.parse-base($base);

Then create a sub happy to introspect the happiness of a given number recursively, by passing its normalized value. A number with 1 and any number of 0's is happy in the next step. No other kinds of numbers have 1 as their neighbor. Any number pointing to those numbers is happy in two more steps. And so on. Thus in the recursion, we can use this method to count the number of iterations it takes to know its happiness.

sub happy($n){
  my sub is-happy {
    my $next = $normalize-num($n.base($base).comb.map( *.parse-base($base) ** $pow).sum);
    %happiness{$n} = %(iter=>1);
    return %happiness{$n} = %(|%($_),:$next,iter=>$_<iter>+1) with happy($next);
  }
  return $n == 1 ?? %(|%happiness<1>, iter=>2) !! %happiness{$n} || is-happy()
}

You can look at my complete script here, where you can get n happy numbers for a given power and base.

I only wish to visualize the chain of numbers in a graph.

Thanks for reading..
And would love for feedback