Perl Weekly Challenge: Week 137
Challenge 1:
Long Year
Write a script to find all the years between
1900
and2100
which is aLong Year
.A year is Long if it has 53 weeks.
[UPDATED][2021-11-01 16:20:00]: For more information about Long Year
, please refer to wikipedia.
Expected Output
1903, 1908, 1914, 1920, 1925,
1931, 1936, 1942, 1948, 1953,
1959, 1964, 1970, 1976, 1981,
1987, 1992, 1998, 2004, 2009,
2015, 2020, 2026, 2032, 2037,
2043, 2048, 2054, 2060, 2065,
2071, 2076, 2082, 2088, 2093,
2099
Calendrical calculations are a keen interest of mine. In this case the solution was quite straightforward because the referenced Wikipedia page already has a formula you can use. So the code shown below is merely a translation of that without further embellishment.
These two functions represent the Wikipedia formula. One thing that was not readily apparent from reading that page was that the
formula uses integer division. In raku this means using the div
operator not the normal /
which I initially employed. /
uses
floating point which caused all kinds of puzzling off by one errors until I figured out that I was using the wrong operator.
sub p(Int $year) {
return (($year + ($year div 4) - ($year div 100) + ($year div 400)) % 7);
}
sub isLongYear(Int $year) {
return p($year) == 4 || p($year - 1) == 3;
}
Once I had the isLongYear()
function, all I had to do is .grep()
through all the years between 1900 to 2100 looking
for long years and then joining the list together with commas and printing it.
(1900 .. 2100)
.grep({ isLongYear($_); })
.join(q{, })
.say;
This is the Perl version. Perl doesn't have div
like Raku but we can simulate it with a combination of normal /
division and the
int()
function.
sub p {
my ($year) = @_;
return (($year + int($year / 4) - int($year / 100) + int($year / 400)) % 7);
}
sub isLongYear {
my ($year) = @_;
return (p($year) == 4 || p($year - 1) == 3);
}
say join q{, }, grep { isLongYear($_); } (1900 .. 2100);
Challenge 2:
Lychrel Number
You are given a number, 10 <=
$n
<= 1000.Write a script to find out if the given number is Lychrel number. To keep the task simple, we impose the following rules:
a. Stop if the number of iterations reached 500.
b. Stop if you end up with number >= 10_000_000.
[UPDATED][2021-11-01 16:20:00]: If you stop because of any of the above two rules then we expect 1
as an output.
According to wikipedia:
A Lychrel number is a natural number that cannot form a palindrome through the iterative process of repeatedly reversing its digits and adding the resulting numbers.
Example 1
Input: $n = 56
Output: 0
After 1 iteration, we found palindrome number.
56 + 65 = 121
Example 2
Input: $n = 57
Output: 0
After 2 iterations, we found palindrome number.
57 + 75 = 132
132 + 231 = 363
Example 3
Input: $n = 59
Output: 0
After 3 iterations, we found palindrome number.
59 + 95 = 154
154 + 451 = 605
605 + 506 = 1111
Here is the important part of my Raku solution:
sub lychrel(Int $n) {
my $i = $n;
Strictly speaking this should be an infinite loop but the spec says we can stop after 500 iterations. for 1 .. 500 {
We reverse the number by splitting it into a list of individual digits with .comb()
, reversing that list and then joining it
back up again. Too late, I've just remembered that Raku has .flip()
which would have reversed the number without all this extra work.
my $r = $i.comb.reverse.join;
This is added to I to form the number to be checked for palindromeness (palindromosity?)
$i = $i + $r;
Again I could have used .flip()
here. If the number is a palindrome (i.e. it is the same as its' reverse,) we are done and
0 is returned.
if $i == $i.comb.reverse.join {
return 0;
}
The spec says we can stop if the number is greater than 10,000,000. In this case we return 1.
if $i > 10_000_000 {
return 1;
}
}
If we have done more than 500 iterations without finding a palindrom we also return 1. These two conditions make our code a lot
easier but we get a lot of false positives as a result. This code detected 52 Lychrel numbers but most (perhaps all of these?) could
have ended up as palindromes if $i
was allowed to grow big enough or enough iterations were performed.
return 1;
}
This is the Perl version. There's no .flip()
in Perl so split, reverse and join was the right thing to do here.
sub lychrel {
my ($n) = @_;
my $i = $n;
for (1 .. 500) {
my $r = join q{}, (reverse split //, $i);
$i = $i + $r;
if ($i == join q{}, (reverse split //, $i)) {
return 0;
}
if ($i > 10_000_000) {
return 1;
}
}
return 1;
}