aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordcw <d.white@imperial.ac.uk>2021-11-14 21:31:27 +0000
committerdcw <d.white@imperial.ac.uk>2021-11-14 21:31:27 +0000
commitf84996081084e0bfe1700ef3c01038c62b8aeb5d (patch)
tree8e01c1514eb24790e99ccf579ec4cfe92f025fa5
parentaba7146dd4fd0fe732848021cad6ed3f53af7528 (diff)
downloadperlweeklychallenge-club-f84996081084e0bfe1700ef3c01038c62b8aeb5d.tar.gz
perlweeklychallenge-club-f84996081084e0bfe1700ef3c01038c62b8aeb5d.tar.bz2
perlweeklychallenge-club-f84996081084e0bfe1700ef3c01038c62b8aeb5d.zip
imported my solutions to this week's questions, plus a bonus "tabulate-square-sums" which investigates our split sums more systematically
-rw-r--r--challenge-138/duncan-c-white/README81
-rwxr-xr-xchallenge-138/duncan-c-white/perl/ch-1.pl45
-rwxr-xr-xchallenge-138/duncan-c-white/perl/ch-2.pl107
-rwxr-xr-xchallenge-138/duncan-c-white/perl/tabulate-split-sums107
4 files changed, 292 insertions, 48 deletions
diff --git a/challenge-138/duncan-c-white/README b/challenge-138/duncan-c-white/README
index a9b09917c6..168bfb1a4f 100644
--- a/challenge-138/duncan-c-white/README
+++ b/challenge-138/duncan-c-white/README
@@ -1,73 +1,58 @@
-TASK #1 - Long Year
+TASK #1 - Workdays
-Write a script to find all the years between 1900 and 2100 which is a Long Year.
+You are given a year, $year in 4-digits form.
- A year is Long if it has 53 weeks.
+Write a script to calculate the total number of workdays in the given year.
+For the task, we consider, Monday - Friday as workdays.
-For more information about Long Year, please refer to
-
-https://en.wikipedia.org/wiki/ISO_week_date#Weeks_per_year
-
-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
+Example 1
-MY NOTES: Pretty easy. Especially using the p(year) and weeks(year) functions given,
-won't even need a date manipulation module..
+ Input: $year = 2021
+ Output: 261
+Example 2
-TASK #2 - Lychrel Number
+ Input: $year = 2020
+ Output: 262
-You are given a number, 10 <= $n <= 1000.
+MY NOTES: Pretty easy. Essentially: iterate over all days in $year, inc
+count if day_of_week(that day) is a week day (Mon..Fri). Date::Simple
+looks like it can do everything we need.
-Write a script to find out if the given number is Lychrel number;
-output 1 if it is, and zero if it is not. To keep the task simple, we impose the following rules:
-a. Stop and return 1 if the number of iterations reached 500.
-b. Stop and return 1 if you end up with number >= 10_000_000.
+TASK #2 - Split Number
-According to wikipedia:
+You are given a perfect square.
- 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.
+Write a script to figure out if the square root the given number is same
+as sum of 2 or more splits of the given number.
Example 1
- Input: $n = 56
- Output: 0
+ Input: $n = 81
+ Output: 1
- After 1 iteration, we found palindrome number.
- 56 + 65 = 121
+ Since, sqrt(81) = 8 + 1
Example 2
- Input: $n = 57
- Output: 0
+ Input: $n = 9801
+ Output: 1
- After 2 iterations, we found palindrome number.
- 57 + 75 = 132
- 132 + 231 = 363
+ Since, sqrt(9801) = 98 + 0 + 1
Example 3
- Input: $n = 59
+ Input: $n = 36
Output: 0
- After 3 iterations, we found palindrome number.
- 59 + 95 = 154
- 154 + 451 = 605
- 605 + 506 = 1111
+ Since, sqrt(36) != 3 + 6
+
+MY NOTES: Sounds pretty easy. The only tricky part is identifying all
+distinct "splits" of the number. Is there a "binary counting" method,
+where bit i being true means: split the number between digit i and i+1?
-MY NOTES: Sounds pretty easy. The strangest thing about this question
-is that the Wikipedia page has that (without the artificial constraints)
-noone found a definite Lychrel base 10 number. So any "Output: 1" entries
-we can find (such as 89) are caused by sequences violating one or other
-of the constraints.
+BONUS: I was interested in how many perfect squares do have this "split
+summing to the sqrt" property, so I added tabulate-split-sums which
+tries the first N perfect squares, and prints out those that do have
+this property. 81 is the first, 100 the second, 1296 the third etc..
diff --git a/challenge-138/duncan-c-white/perl/ch-1.pl b/challenge-138/duncan-c-white/perl/ch-1.pl
new file mode 100755
index 0000000000..509dc1d541
--- /dev/null
+++ b/challenge-138/duncan-c-white/perl/ch-1.pl
@@ -0,0 +1,45 @@
+#!/usr/bin/perl
+#
+# TASK #1 - Workdays
+#
+# You are given a year, $year in 4-digits form.
+#
+# Write a script to calculate the total number of workdays in the given year.
+# For the task, we consider, Monday - Friday as workdays.
+#
+# Example 1
+#
+# Input: $year = 2021
+# Output: 261
+#
+# Example 2
+#
+# Input: $year = 2020
+# Output: 262
+#
+# MY NOTES: Pretty easy. Essentially: iterate over all days in $year, inc
+# count if day_of_week(that day) is a week day (Mon..Fri). Date::Simple
+# looks like it can do everything we need.
+#
+
+use strict;
+use warnings;
+use feature 'say';
+use Getopt::Long;
+use Date::Simple (':all');
+#use Data::Dumper;
+
+
+my $debug=0;
+die "Usage: work-days Year\n" unless
+ GetOptions( "debug"=>\$debug ) && @ARGV==1;
+
+my $year = shift;
+
+my $weekdays = 0;
+for( my $day = date("$year-01-01"); $day->year == $year; $day++ )
+{
+ my $dow = $day->day_of_week;
+ $weekdays++ if $dow >= 1 && $dow <= 5;
+}
+say $weekdays;
diff --git a/challenge-138/duncan-c-white/perl/ch-2.pl b/challenge-138/duncan-c-white/perl/ch-2.pl
new file mode 100755
index 0000000000..15ef29182f
--- /dev/null
+++ b/challenge-138/duncan-c-white/perl/ch-2.pl
@@ -0,0 +1,107 @@
+#!/usr/bin/perl
+#
+# TASK #2 - Split Number
+#
+# You are given a perfect square.
+#
+# Write a script to figure out if the square root the given number is same
+# as sum of 2 or more splits of the given number.
+#
+# Example 1
+#
+# Input: $n = 81
+# Output: 1
+#
+# Since, sqrt(81) = 8 + 1
+#
+# Example 2
+#
+# Input: $n = 9801
+# Output: 1
+#
+# Since, sqrt(9801) = 98 + 0 + 1
+#
+# Example 3
+#
+# Input: $n = 36
+# Output: 0
+#
+# Since, sqrt(36) != 3 + 6
+#
+# MY NOTES: Sounds pretty easy. The only tricky part is identifying all
+# distinct "splits" of the number. Is there a "binary counting" method,
+# where bit i being true means: split the number between digit i and i+1?
+#
+
+use strict;
+use warnings;
+use feature 'say';
+use Function::Parameters;
+use Getopt::Long;
+use Data::Dumper;
+use List::Util qw(sum);
+
+my $debug = 0;
+
+die "Usage: split-numbers [-d|--debug] N\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==1;
+my $n = shift @ARGV;
+my $sq = int(sqrt($n));
+die "$n is not a perfect square\n" if $n != $sq*$sq;
+
+
+#
+# my @pieces = BinarySplit( $str, $count );
+# Split $str using the binary count $count
+# and return the list of split pieces. Each bit of
+# the count $count determines whether to split the
+# string at the corresponding letter - or
+# equivalently, whether to insert a ',' before the letter.
+#
+fun BinarySplit( $str, $count )
+{
+ $str =~ s/^(.)//; # always add the first letter
+ my $result = $1;
+
+ while( $str =~ s/^(.)// )
+ {
+ my $letter = $1;
+ $result .= ',' if $count&1;
+ $result .= $letter;
+ $count >>= 1;
+ }
+ return split(/,/,$result);
+}
+
+
+#die Dumper BinarySplit( "hello", 0b100 );
+
+
+#
+# my $issplit = SplitSum( $n, $sum );
+# Return 1 iff the digits of $n can be split at
+# any position set of positions st the sum of those
+# split values is $sum. Return 0 otherwise.
+# For example SplitSum(81,9) is true as 9 = 8 + 1
+#
+fun SplitSum( $n, $sum )
+{
+ my $bits = length($n)-1;
+ my $twopower = 2**$bits;
+ say "debug: twopower=$twopower" if $debug;
+ # ignore i=0, that doesn't split $n at all, and sum($n)==$n
+ # which is always > $sqrt($n) so can't be an answer
+ for( my $i=1; $i<$twopower; $i++ )
+ {
+ my @pieces = BinarySplit( $n, $i );
+ my $actualsum = sum(@pieces);
+ say "debug: i=$i, want sum=$sum, ".join(',',@pieces).
+ ", got sum=$actualsum, want $sum" if $debug;
+ return 1 if $actualsum == $sum;
+ }
+ return 0;
+}
+
+
+my $issplit = SplitSum( $n, $sq );
+say $issplit;
diff --git a/challenge-138/duncan-c-white/perl/tabulate-split-sums b/challenge-138/duncan-c-white/perl/tabulate-split-sums
new file mode 100755
index 0000000000..529934c589
--- /dev/null
+++ b/challenge-138/duncan-c-white/perl/tabulate-split-sums
@@ -0,0 +1,107 @@
+#!/usr/bin/perl
+#
+# TASK #2 - Split Number
+#
+# Variation that Tabulates the first N perfect squares and whether or
+# not they have a split whose sum is sqrt(that square).
+#
+# Example 1
+#
+# Input: $n = 81
+# Output: 1
+#
+# Since, sqrt(81) = 8 + 1
+#
+# Example 2
+#
+# Input: $n = 9801
+# Output: 1
+#
+# Since, sqrt(9801) = 98 + 0 + 1
+#
+# Example 3
+#
+# Input: $n = 36
+# Output: 0
+#
+# Since, sqrt(36) != 3 + 6
+#
+# MY NOTES: Sounds pretty easy. The only tricky part is identifying all
+# distinct "splits" of the number. Is there a "binary counting" method,
+# where bit i being true means: split the number between digit i and i+1?
+#
+
+use strict;
+use warnings;
+use feature 'say';
+use Function::Parameters;
+use Getopt::Long;
+use Data::Dumper;
+use List::Util qw(sum);
+
+my $debug = 0;
+
+die "Usage: split-numbers [-d|--debug] MAXN\n"
+ unless GetOptions( "debug"=>\$debug ) && @ARGV==1;
+my $maxsq = shift @ARGV;
+#
+# my @pieces = BinarySplit( $str, $count );
+# Split $str using the binary count $count
+# and return the list of split pieces. Each bit of
+# the count $count determines whether to split the
+# string at the corresponding letter - or
+# equivalently, whether to insert a ',' before the letter.
+#
+fun BinarySplit( $str, $count )
+{
+ $str =~ s/^(.)//; # always add the first letter
+ my $result = $1;
+
+ while( $str =~ s/^(.)// )
+ {
+ my $letter = $1;
+ $result .= ',' if $count&1;
+ $result .= $letter;
+ $count >>= 1;
+ }
+ return split(/,/,$result);
+}
+
+
+#die Dumper BinarySplit( "hello", 0b100 );
+
+
+#
+# my $issplit = SplitSum( $n, $sum );
+# Return 1 iff the digits of $n can be split at
+# any position set of positions st the sum of those
+# split values is $sum. Return 0 otherwise.
+# For example SplitSum(81,9) is true as 9 = 8 + 1
+#
+fun SplitSum( $n, $sum )
+{
+ my $bits = length($n)-1;
+ my $twopower = 2**$bits;
+ say "debug: twopower=$twopower" if $debug;
+ # ignore i=0, that doesn't split $n at all, and sum($n)==$n
+ # which is always > $sqrt($n) so can't be an answer
+ for( my $i=1; $i<$twopower; $i++ )
+ {
+ my @pieces = BinarySplit( $n, $i );
+ my $actualsum = sum(@pieces);
+ say "debug: i=$i, want sum=$sum, ".join(',',@pieces).
+ ", got sum=$actualsum, want $sum" if $debug;
+ return 1 if $actualsum == $sum;
+ }
+ return 0;
+}
+
+
+foreach my $sq (1..$maxsq)
+{
+ my $n = $sq*$sq;
+ my $issplit = SplitSum( $n, $sq );
+ say "$n - $sq" if $issplit;
+}
+
+