aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad Sajid Anwar <Mohammad.Anwar@yahoo.com>2023-11-05 00:43:07 +0000
committerGitHub <noreply@github.com>2023-11-05 00:43:07 +0000
commit93c50a2cd01e59f678a23d4f84f99f70c8b24677 (patch)
tree6efe0ab2275bf40f055336e1e8f384192ceaea01
parent4b7f4bb6bad4573026f7f052e5b27510ac316568 (diff)
parent331981ef44321e04939ed96a027d1a9bacee6972 (diff)
downloadperlweeklychallenge-club-93c50a2cd01e59f678a23d4f84f99f70c8b24677.tar.gz
perlweeklychallenge-club-93c50a2cd01e59f678a23d4f84f99f70c8b24677.tar.bz2
perlweeklychallenge-club-93c50a2cd01e59f678a23d4f84f99f70c8b24677.zip
Merge pull request #8991 from ianrifkin/ianrifkin-challenge-241
Ianrifkin challenge 241
-rw-r--r--challenge-241/ianrifkin/README.md153
-rw-r--r--challenge-241/ianrifkin/blog.txt1
-rw-r--r--challenge-241/ianrifkin/perl/ch-1.pl37
-rw-r--r--challenge-241/ianrifkin/perl/ch-2.pl119
4 files changed, 262 insertions, 48 deletions
diff --git a/challenge-241/ianrifkin/README.md b/challenge-241/ianrifkin/README.md
index a3d7bf545f..ece05ca910 100644
--- a/challenge-241/ianrifkin/README.md
+++ b/challenge-241/ianrifkin/README.md
@@ -1,83 +1,140 @@
-# A.A.B.A. (Acronym And Build Array)
+# Math is hard?
-Challenge 240: https://theweeklychallenge.org/blog/perl-weekly-challenge-240/
+Challenge 241: https://theweeklychallenge.org/blog/perl-weekly-challenge-241/
-## Task 1: Acronym
+Some of The Weekly Challenges are more intimidating for me based on how math-heavy the task feels. The first task seemed overwhelming but was pretty quick to create a solution, yet the second task took me much longer.
-> You are given an array of strings and a check string.
+## Task 1: Arithmetic Triplets
-> Write a script to find out if the check string is the acronym of the words in the given array.
+```
+You are given an array (3 or more members) of integers in increasing order and a positive integer.
+
+Write a script to find out the number of unique Arithmetic Triplets satisfying the following rules:
+a) i < j < k
+b) nums[j] - nums[i] == diff
+c) nums[k] - nums[j] == diff
Example 1
-```
-Input: @str = ("Perl", "Python", "Pascal")
- $chk = "ppp"
-Output: true
-```
+Input: @nums = (0, 1, 4, 6, 7, 10)
+ $diff = 3
+Output: 2
+Index (1, 2, 4) is an arithmetic triplet because both 7 - 4 == 3 and 4 - 1 == 3.
+Index (2, 4, 5) is an arithmetic triplet because both 10 - 7 == 3 and 7 - 4 == 3.
Example 2
+Input: @nums = (4, 5, 6, 7, 8, 9)
+ $diff = 2
+Output: 2
+
+(0, 2, 4) is an arithmetic triplet because both 8 - 6 == 2 and 6 - 4 == 2.
+(1, 3, 5) is an arithmetic triplet because both 9 - 7 == 2 and 7 - 5 == 2.
```
-Input: @str = ("Perl", "Raku")
- $chk = "rp"
-Output: false
-```
-Example 3
+A quick read of this task was overwhelming to me because it sounded like it was going to be more math than I could handle, but upon closer inspection the answer is in the description.
+
+I kept it simple and just explicitely created the three loops with the three specified iterators. It's very helpful to match the incrementor names in my code with the task's description.
+
+I started by looping through the input array with an incrementer `$i`. We know that `$i` is needed for the first value so the max `$i` is going to be 2 less than the total array length: `for (my $i = 0; $i < $nums_length - 2; $i++)`
+
+Within this loop I just make the next loop with `$j` which is 1 more than `$i` to 1 less than the array length: `for (my $j = $i + 1; $j < $nums_length - 1; $j++)`
+
+Finally within that loop I create the loop with `$k` which is going to be 1 more than `$j` to the end of the array: `for (my $k = $j + 1; $k < $nums_length; $k++)`
+
+Now that I have my loops I go back to the instructions which stated:
```
-Input: @str = ("Oracle", "Awk", "C")
- $chk = "oac"
-Output: true
+nums[j] - nums[i] == diff
+and
+nums[k] - nums[j] == diff
```
-I am sure there are plenty of more interesting ways to solve this but the way my mind works is to create a real acronym by looping through each word in the in the input array to get the first letter.
+which in my Perl code is literally that same thing: `$nums[$j] - $nums[$i] == $diff && $nums[$k] - $nums[$j] == $diff`
+
+Anytime the above is true it increments a counter `$total_finds`. After the loops are complete it prints the `$total_finds` counter.
+
+The complete `sub find_triplets` is as follows:
```
-sub check_acronym {
- my ($acronym, $words) = @_;
- my $real_acronym;
- foreach (@{$words}) {
- $real_acronym .= substr($_, 0, 1);
+sub find_triplets {
+ my ($diff, $nums) = @_;
+ my $nums_length = scalar @{$nums};
+ my $total_finds = 0;
+ for (my $i = 0; $i < $nums_length - 2; $i++) {
+ for (my $j = $i + 1; $j < $nums_length - 1; $j++) {
+ for (my $k = $j + 1; $k < $nums_length; $k++) {
+ if (@{$nums}[$j] - @{$nums}[$i] eq $diff && @{$nums}[$k] - @{$nums}[$j] eq $diff) {
+ $total_finds++;
+ }
+ }
+ }
}
+ print "$total_finds\n";
+}
```
-With that in place just compare the strings, forcing them both into uppercase so that it's a case insensitive comparison. You could also do this with a simple RegEx compare but this feels simpler to write/read to my brain.
+Full script with comments available at https://github.com/ianrifkin/perlweeklychallenge-club/blob/ianrifkin-challenge-241/challenge-241/ianrifkin/perl/ch-1.pl
+
+## Task 2: Prime Order
```
-uc($acronym) eq uc($real_acronym) ? print "true\n" : print "false\n";
+You are given an array of unique positive integers greater than 2.
+
+Write a script to sort them in ascending order of the count of their prime factors, tie-breaking by ascending value.
+
+Example 1
+Input: @int = (11, 8, 27, 4)
+Output: (11, 4, 8, 27))
+
+Prime factors of 11 => 11
+Prime factors of 4 => 2, 2
+Prime factors of 8 => 2, 2, 2
+Prime factors of 27 => 3, 3, 3
```
-I did a similar approach with Raku and Python. With Perl the final print statement is a conditional so that it will print the words "true" or "false" instead of 0 or 1 but that's not needed with Python and Raku.
+This one sounded simple enough but I needed to write WAY more code to accomplish. Though to be fair, part of the length is a lot more code comments and documentation -- critial because this quickly got confusing for me! After I completed it and it was working, I wasn't happy with how long the code got and how I was handling the looping so I refactored it a bit. The main different is the first attempt I really started by trying to find the prime factors and then counting them up. This was useful because it allowed me to check as I went along to make sure I was counting the right things appropriately. But once I got a good sense of it I realized it could be done quicker -- though mostly it's to reduce what felt like messy extra loops in code.
-## Task 2: Build Array
+I split up the work into 2 subroutines.
-> You are given an array of integers.
-> Write a script to create an array such that new[i] = old[old[i]] where 0 <= i < new.length.
+The main sub, `prime_order()`, accepts an array of integers. It loops through the array to create a hash where the key is the input number from the array and the value is number of prime factors for that number. It generates the count of prime factors by calling a sub `prime_finder()`.
-Example 1
-```
-Input: @int = (0, 2, 1, 5, 3, 4)
-Output: (0, 1, 2, 4, 5, 3)
-```
-Example 2
+```prime_finder```
+
+After the loop is complete `prime_order()` will create and print a sorted array based on the hash results.
```
-Input: @int = (5, 0, 1, 2, 3, 4)
-Output: (4, 5, 0, 1, 2, 3)
+ foreach my $ordered_num (sort { $results{$a} <=> $results{$b} or $a <=> $b } keys %results) {
+ push(@sorted_output, $ordered_num);
+ }
+
+ # Finally, print the sorted output
+ say "(" . join(", ", @sorted_output) . ")";
```
-Doing `new[i] = old[old[i]]` seemed simple enough but I do have trouble mentally processing `where 0 <= i < new.length`. If `i` is a postion of the input array it is always going to be equal to or greater than 0 because of how arrays are numbered. Since each new[i] is mapped to a value calculated from the old array they should naturally end up the same length, so `i` should never be longer than new.length because `i` shouldn't be less than old.length either.
+The sub `prime_finder()` is what actually cacluates the number of prime factors for a given input number.
-With that out of the way, I did a simple for loop using the iterator variable `$i` -- within the loop I am literally just doing the exact mapping from the question: `new[i] = old[old[i]]`
-```
-for (my $i = 0; $i < @ints; $i++) {
- $new_ints[$i] = $ints[$ints[$i]];
-}
-```
+I start by having a while loop set to repeat endlessly with a manual condition flag. This will be important based on my approach.
+
+Within the while loop I have a for loop that will find the biggest prime factor for a given number. The loop starts at the square root of the number `$i = int(sqrt($num))` and checks if a prime is found by seeing if dividing the number by the iterator has a remainder: `if ($num % $i == 0)`
+
+Some examples:
+
+input is 11 --> int of square root is 3 --> first prime factor found is 1 (meaning 11 is a prime number). In this case it increments the counter by 1 then exits the parent while loop.
+
+input is 27 --> int of square root is 5 --> first prime factor found is 3. The counter gets incremented to 1. To determine how many other prime factors it would take, I set the `$num` variable to `27 / 3` which is 9 and I exit the `for` loop, restarting it with the new `$num`. In this next iteration the prime found is 3. I increment the counter. Then it goes one more time where it determines that 3 is a prime number and does the final increment.
-That's it! After the loop I print the new array in the desired format:
```
-print "(" . join(', ', @new_ints) . ")\n";
+ while ($calculating) {
+ for (my $i = int(sqrt($num)); $i >= 1; $i--) {
+ #looping backwards to find biggest prime factor first
+ if ($num % $i == 0) { #if prime factor found
+ $counter++; #increment that we found a prime factor
+ $calculating = 0 if ($i == 1); #if the prime factor is 1 stop the parent while loop
+ $num = $num / $i; #otherwise reset num lower to search for the next prime factor
+ last; #and restart for loop with new number
+ }
+
+ }
+ }
```
-This task sounded more complicated so I will be curious to see if others took a more interesting approach. \ No newline at end of file
+The full code with comments is available at https://github.com/ianrifkin/perlweeklychallenge-club/blob/ianrifkin-challenge-241/challenge-241/ianrifkin/perl/ch-2.pl \ No newline at end of file
diff --git a/challenge-241/ianrifkin/blog.txt b/challenge-241/ianrifkin/blog.txt
new file mode 100644
index 0000000000..409085ddee
--- /dev/null
+++ b/challenge-241/ianrifkin/blog.txt
@@ -0,0 +1 @@
+https://github.com/ianrifkin/perlweeklychallenge-club/blob/ianrifkin-challenge-240/challenge-241/ianrifkin/README.md
diff --git a/challenge-241/ianrifkin/perl/ch-1.pl b/challenge-241/ianrifkin/perl/ch-1.pl
new file mode 100644
index 0000000000..2b9d7a6d8e
--- /dev/null
+++ b/challenge-241/ianrifkin/perl/ch-1.pl
@@ -0,0 +1,37 @@
+use v5.30.3;
+use warnings;
+use strict;
+
+# Task 1: Arithmetic Triplets
+# You are given an array (3 or more members) of integers in increasing order and a positive integer.
+# Write a script to find out the number of unique Arithmetic Triplets satisfying the following rules:
+# a) i < j < k
+# b) nums[j] - nums[i] == diff
+# c) nums[k] - nums[j] == diff
+
+# Example 1
+my @nums = (0, 1, 4, 6, 7, 10);
+my $diff = 3;
+find_triplets($diff, \@nums);
+
+# Example 2
+@nums = (4, 5, 6, 7, 8, 9);
+$diff = 2;
+find_triplets($diff, \@nums);
+
+sub find_triplets {
+ my ($diff, $nums) = @_;
+ my $nums_length = scalar @{$nums};
+ my $total_finds = 0;
+ for (my $i = 0; $i < $nums_length - 2; $i++) {
+ for (my $j = $i + 1; $j < $nums_length - 1; $j++) {
+ for (my $k = $j + 1; $k < $nums_length; $k++) {
+ if (@{$nums}[$j] - @{$nums}[$i] == $diff && @{$nums}[$k] - @{$nums}[$j] == $diff) {
+ $total_finds++;
+ }
+ }
+ }
+ }
+ print "$total_finds\n";
+}
+
diff --git a/challenge-241/ianrifkin/perl/ch-2.pl b/challenge-241/ianrifkin/perl/ch-2.pl
new file mode 100644
index 0000000000..18c5c5a206
--- /dev/null
+++ b/challenge-241/ianrifkin/perl/ch-2.pl
@@ -0,0 +1,119 @@
+use v5.30.3;
+use warnings;
+use strict;
+use Getopt::Long;
+use Pod::Usage;
+
+#accept cmd line input
+my $man = 0;
+my $help = 0;
+my $str_input;
+GetOptions ('help|?' => \$help, man => \$man,
+ "int_input=s" => \$str_input)
+ or pod2usage(2);
+
+pod2usage(1) if $help;
+pod2usage(-exitstatus => 0, -verbose => 2) if $man;
+
+# Prepare input array
+my @int_input;
+# if values provided at cmd line split on comma
+if ( $str_input ) {
+ @int_input = (split(/,/, $str_input) );
+}
+# else set default values from example if no cmd line input
+else {
+ @int_input = (11, 8, 27, 4) unless @int_input;
+}
+
+# run the program
+prime_order(\@int_input);
+
+sub prime_order {
+ my ($int_input) = @_;
+
+ my %results;
+ # Let's calcuate how many prime factors of each input number
+ foreach (@{$int_input}) {
+ my $num = $_;
+ $num =~ s/^\s+//; #remove whitespace is provided at cmd line just for prettiness
+
+ die("Invalid input") if $num <= 2; #This shouldn't happen based on task instructions
+
+ # Get number of prime factors
+ $results{$num} = prime_finder($num);
+ }
+
+ # Prepare the ouptut in a nice sorted array
+ my @sorted_output;
+ # This should should by the count first ($results{$a} <=> $results{$b}) then the input numbers ($b cmp $a)
+ foreach my $ordered_num (sort { $results{$a} <=> $results{$b} or $a <=> $b } keys %results) {
+ push(@sorted_output, $ordered_num);
+ }
+
+ # Finally, print the sorted output
+ say "(" . join(", ", @sorted_output) . ")";
+}
+
+sub prime_finder {
+ # This sub finds the number of prime factors for a given number
+ my ($num) = @_;
+ my $counter = 0; #the number of prime factors
+ my $calculating = 1;
+ while ($calculating) {
+ for (my $i = int(sqrt($num)); $i >= 1; $i--) {
+ #looping backwards to find biggest prime factor first
+ if ($num % $i == 0) { #if prime factor found
+ $counter++; #increment that we found a prime factor
+ $calculating = 0 if ($i == 1); #if the prime factor is 1 stop the parent while loop
+ $num = $num / $i; #otherwise reset num lower to search for the next prime factor
+ last; #and restart for loop with new number
+ }
+
+ }
+ }
+ return $counter;
+}
+
+
+__END__
+
+=head1 Challenge 241, Task 2, by IanRifkin: Prime Order
+
+See https://theweeklychallenge.org/blog/perl-weekly-challenge-241/#TASK2 for more information on this challenge
+
+=head1 SYNOPSIS
+
+perl ./ch-2.pl [options]
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-help>
+
+Print a brief help message and exits.
+
+=item B<-man>
+
+Prints the manual page and exits.
+
+=item B<-int_input>
+
+An optional comma separated list of numbers (else defaults to values from example 1)
+
+=back
+
+=head1 DESCRIPTION
+
+Task 2 in Challenge 241 states:
+
+ - You are given an array of unique positive integers greater than 2.
+
+ - Write a script to sort them in ascending order of the count of their prime factors, tie-breaking by ascending value.
+
+This program accepts an optional comma separated list of numbers else defaults to the provided numbers from example 1.
+
+Note: There is an assumption that "tie-breaking by ascending value" means the value of the input number (vs. the value of their prime factor)
+
+=cut