perl logo Perl logo (Thanks to Olaf Alders)

The weekly challenge 379 - Task 2: Armstrong Number

 1 #!/usr/bin/env perl
 2 # https://theweeklychallenge.org/blog/perl-weekly-challenge-379/#TASK2
 3 #
 4 # Task 2: Armstrong Number
 5 # ========================
 6 #
 7 # You are given two integers, $base and $limit.
 8 #
 9 # Write a script to find all Armstrong numbers in base $base that are less than
10 # $limit.
11 #
12 ##     If raising each of the digits of a nonnegative integer to the power of
13 ##     the total number of digits, then taking the sum, equals the original
14 ##     number, it is an Armstrong number.
15 #
16 ## Example 1
17 ##
18 ## Input: $base = 10, $limit = 1000
19 ## Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 153, 370, 371, 407)
20 #
21 ## Example 2
22 ##
23 ## Input: $base = 7, $limit = 1000
24 ## Output: (0, 1, 2, 3, 4, 5, 6, 10, 25, 32, 45, 133, 134, 152, 250)
25 #
26 ## Example 3
27 ##
28 ## Input: $base = 16, $limit = 1000
29 ## Output: (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 342, 371, 520, 584, 645)
30 #
31 ############################################################
32 ##
33 ## discussion
34 ##
35 ############################################################
36 #
37 # It's not necessarily obvious, but $limit seems to be in base 10 as
38 # per the examples. Also the output is in base 10 as well.
39 # With that in place, we just need to collect all armstrong numbers
40 # until we hit the limit, so a simple loop will do that.
41 # In that loop, we check if the given number is an armstrong number
42 # in the given base:
43 # - rebase to the given base
44 # - take the appropriate power of the digits and sum them up
45 # - if the result equals the original number we can return 1, otherwise 0
46 # The rest is just helper functions to translate between bases.
47 
48 use v5.36;
49 
50 my @base_digits = (0..9, 'a'..'z');
51 
52 armstrong_numbers(10, 1000);
53 armstrong_numbers(7, 1000);
54 armstrong_numbers(16, 1000);
55 
56 sub translated_to_base10($digit) {
57    if($digit =~ m/\d/) {
58       return $digit;
59    }
60    # ord('a') = 97, but we need to start counting at 10 for 'a'.
61    return ord($digit) - 87;
62 }
63 
64 sub armstrong_numbers($base, $limit) {
65    say "Input: base = $base, limit = $limit";
66    my @output = ();
67    foreach my $number (0..$limit) {
68       push @output, $number if is_amstrong_number($number, $base);
69    }
70    say "Output: (" . join(", ", @output) . ")";
71 }
72 
73 sub is_amstrong_number($number, $base) {
74    my $rebased_number = rebase($number, $base);
75    my $len = length($rebased_number);
76    my $r = $rebased_number;
77    my $result = 0;
78    while(length($r)) {
79       $r =~ s/(.)//;
80       my $d = $1;
81       $result += translated_to_base10($d) ** $len;
82    }
83    return $result == $number;
84 }
85 
86 sub rebase($number, $base) {
87    return "0" if $number == 0;
88    my $result = "";
89    while($number > 0) {
90       my $d = $number % $base;
91       $result = $base_digits[$d] . $result;
92       $number = int($number / $base);
93    }
94    return $result;
95 }