Challenge 153 Task #2 - Even more factorials and what the fuck are factorions?

Task #2 - Factorions

Description

Original Description

Submitted by: Mohammad S Anwar

You are given an integer, $n.

Write a script to figure out if the given integer is factorion.

A factorion is a natural number that equals the sum of the factorials of its digits.

Example 1:

Input: $n = 145
Output: 1

    Since 1! + 4! + 5! => 1 + 24 + 120 = 145

Example 2:

Input: $n = 123
Output: 0

    Since 1! + 2! + 3! => 1 + 2 + 6 <> 123

Solution

Full Source

Similar to the first task of this week, this is about factorials. I first solved it just with what is given in the description. Split the number into its digits, calculate the factorial for each digit and sum that up. If the sum is equal to the input we have a factorion.

First the same fac routine as in task 1.

sub fac($n) {
    product( 1 .. $n );
}

Here the code for the is_factorion routine.

sub is_factorion($n) {
    my @digits                      = split( m//, $n );
    my $sum_of_factorials_of_digits = sum0( map { fac() } @digits );

    return $n == $sum_of_factorials_of_digits;
}

Afterwards I wrote some drive code to run this for numbers up to 100000:

sub run() {
    my $max = 100000;
    say "Factorions <= $max:";
    for ( 1 .. $max ) {
        say 
          if is_factorion();
    }
}

Output:

1
2
145
40585

With this little of output I started doing some online research for factorions and found Sequence A014080. Surprisingly the list above is all factorions that exist.

I then also wrote a routine that uses this list to check if $n is a factorion.

sub is_factorion_a014080($n) {
    ## complete list of all factorians - see https://oeis.org/A014080
    state @A014080 = ( 1, 2, 145, 40585 );
    first { $n ==  } @A014080;
}

And adapted my driver code a little to chose randomly between the two solutions for each number that it checks.

sub run() {
    my $max = 100000;
    say "Factorions <= $max:";
    for ( 1 .. $max ) {
        my $fn = ( \&is_factorion, \&is_factorion_a014080 )[ int( rand(2) ) ];
        say  if $fn->();
    }
}

Haskell Solution

Full Source

I also solved this task in Haskell by pattern matching on n and comparing it to the four possible results:

isFactorion :: Integer -> Bool
isFactorion 1     = True
isFactorion 2     = True
isFactorion 145   = True
isFactorion 40585 = True
isFactorion _     = False

Raku Solution

Full Source

In raku I used a unit main to make the whole script the main routine. This main routine will also handle command line arguments for us. I use the utf-8 elem operator to check if the given number is a member of the A014080 sequence. I struggled a little with what looks like some typing errors. I had to multiply the argument $n with 1 to coerce it into a number, otherwise the code wouldn’t work.

unit sub MAIN(Int $n);
my @A014080 = 1, 2, 145, 40585;

# Seems we have to coerce the argument $n into a number first by multiplying by
# 1. Didn't expect that.
say $n*1@A014080;