diff options
| author | dcw <d.white@imperial.ac.uk> | 2021-11-14 21:31:27 +0000 |
|---|---|---|
| committer | dcw <d.white@imperial.ac.uk> | 2021-11-14 21:31:27 +0000 |
| commit | f84996081084e0bfe1700ef3c01038c62b8aeb5d (patch) | |
| tree | 8e01c1514eb24790e99ccf579ec4cfe92f025fa5 | |
| parent | aba7146dd4fd0fe732848021cad6ed3f53af7528 (diff) | |
| download | perlweeklychallenge-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/README | 81 | ||||
| -rwxr-xr-x | challenge-138/duncan-c-white/perl/ch-1.pl | 45 | ||||
| -rwxr-xr-x | challenge-138/duncan-c-white/perl/ch-2.pl | 107 | ||||
| -rwxr-xr-x | challenge-138/duncan-c-white/perl/tabulate-split-sums | 107 |
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; +} + + |
