diff options
| author | Ian Goodnight <goodnight.ian@gmail.com> | 2021-10-09 14:55:44 -0400 |
|---|---|---|
| committer | Ian Goodnight <goodnight.ian@gmail.com> | 2021-10-09 14:55:44 -0400 |
| commit | c0428c3468203840d5cd0948d298fa86a72f1c9c (patch) | |
| tree | c1b13cc2490ebf075763b5730b97f752a945445e /challenge-133 | |
| parent | 7e31b242f4012d100b2949c29f00a3597a9bb80f (diff) | |
| download | perlweeklychallenge-club-c0428c3468203840d5cd0948d298fa86a72f1c9c.tar.gz perlweeklychallenge-club-c0428c3468203840d5cd0948d298fa86a72f1c9c.tar.bz2 perlweeklychallenge-club-c0428c3468203840d5cd0948d298fa86a72f1c9c.zip | |
solutions 133
Diffstat (limited to 'challenge-133')
| -rw-r--r-- | challenge-133/iangoodnight/javascript/README.md | 196 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/javascript/ch-1.js | 121 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/javascript/ch-2.js | 105 | ||||
| -rw-r--r-- | challenge-133/iangoodnight/perl/README.md | 233 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/perl/ch-1.pl | 140 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/perl/ch-2.pl | 143 | ||||
| -rw-r--r-- | challenge-133/iangoodnight/ruby/README.md | 146 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/ruby/ch-1.rb | 79 | ||||
| -rwxr-xr-x | challenge-133/iangoodnight/ruby/ch-2.rb | 81 |
9 files changed, 1244 insertions, 0 deletions
diff --git a/challenge-133/iangoodnight/javascript/README.md b/challenge-133/iangoodnight/javascript/README.md new file mode 100644 index 0000000000..6091157137 --- /dev/null +++ b/challenge-133/iangoodnight/javascript/README.md @@ -0,0 +1,196 @@ +# [Perl Weekly Challenge - 133] _JavaScript Edition_ + + +**Trigger warning:** There's lots of math in this week's challenges. Recovering +math addicts may want to avoid these solutions. Personally, I've always been +more the "hooked on phonics" type, so it was fun brushing up on some of these +mathematical concepts as I worked through my solutions. + +## Task 1 > Integer Square Root + +You are given a positive integer `$N`. + +Write a script to calculate the integer square root of the given number. + +Please avoid using built-in function. Find out more about it [here]. + +**Examples** + +``` +Input: $N = 10 +Output: 3 + +Input: $N = 27 +Output: 5 + +Input: $N = 85 +Output: 9 + +Input: $N = 101 +Output: 10 +``` + +### Solution + +I wasn't familiar with the idea of integer square roots, but after checking out +the linked Wikipedia reference ([here]), I found that the term is equivalent to +the largest integer square root that is less than or equal to our given input +(a square root floor, so to speak. There are likely more elegant, mathematical +approaches to this problem, but my approach was to just start at 0 and check +incrementing squares until I exceeded the input. + +```javascript + +function returnIntSqrRoot(input) { + // Validate input + if (!Number.isInteger(+input) || +input < 0) { + throw new Error( + 'returnIntSqrRoot expects a positive integer as an argument', + ); + } + // Crawl through squares starting with 0 + let i = 0; + while (i * i <= +input) { + i += 1; + } + // return the highest passing number + i -= 1; + return i; +} + +``` + +#### `ch-1.js` + +Generally, I try to write a little test runner and some test cases, but in this +case, it felt like overkill, so instead running `./ch-1.pl` initiates a mini +REPL. Writing a REPL with Node is a lot of fun. Instead of the perlish +`while (<>)` style of handling user input, Node's `readline` is event-driven. +The REPL prompts for input and returns the integer square root. Sample output +is shown below: + +``` +$> ./ch-1.js + +============================== +Integer Square Root Calculator +============================== + +Enter a positive integer (or, type "exit" to quit)> 10 +Integer square root: 3 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 27 +Integer square root: 5 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 85 +Integer square root: 9 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 101 +Integer square root: 10 +Try again? (y/n)> n +Goodbye. + +``` + +## Task 2 > Smith Number + +Write a script to generate the first 10 `Smith Numbers` in base 10. + +According to Wikipedia: + +> In number theory, a Smith number is a composite number for which, in a given +> number base, the sum of its digits is equal to the sum of the digits in its +> prime factorization in the given number base. + +### Solution + +At first, the language of this challenge had me feeling a little out of my +depths. But, breaking down the problem, I realized that it wasn't as +complicated as I had initially believed. It was clear that I needed a +subroutine to reduce a given number to its prime factors, a subroutine to +reduce a number to the sum of its digits, and that between those two functions, +I could find these "Smith Numbers." Admittedly, my first solutions were all +incorrect as I glazed right over that phrase "composite number," which, I've +since learned, is a fancy way of saying "ain't prime." + +```javascript + +// First, we need a utility function to return our prime factors +function primeFactors(number) { + let n = parseInt(number, 10); // Input validation + if (number === 'NaN') { + // Fail fast if arg is not a positive integer + throw new Error( + 'function `primeFactors` expects a positive integer as an argument.', + ); + } + const factors = []; + + let divisor = 2; // Starting with 2, check for remainder + while (n >= 2) { + if (n % divisor === 0) { + // If modulo is 0, push to factors + factors.push(divisor); + n /= divisor; + } else { + divisor += 1; // Else, increment the divisor and try again + } + } + return factors; +} + +// Reduce numbers to the sum of their digits +function sumDigits(number) { + return number + .toString() + .split('') + .reduce((sum, digit) => sum + parseInt(digit, 10), 0); +} + +// Find our actual smith numbers with the help of `primeFactors` and `sumDigits` +function identifySmithNumbers(limit = 10) { + const smithNumbers = []; + + let test = 4; // Smith numbers are composite numbers, so skip the primes + while (smithNumbers.length < limit) { + const primeFactorsArr = primeFactors(test); + + const factorSum = primeFactorsArr.reduce( + (sum, factor) => sum + sumDigits(factor), + 0, + ); + + const digitSum = sumDigits(test); + + if (factorSum === digitSum && primeFactorsArr.length !== 1) { + smithNumbers.push(test); + } + test += 1; + } + return smithNumbers; +} + +``` + +### `ch-2.js` + +Again, this didn't feel like a situation for writing a bunch of tests, as I +wasn't sure the solution even worked until I compared the output against a list +of `Smith Numbers` I found online, so running `./ch-2.js` will output the first +10 `Smith Numbers` without much in the way of pomp or circumstance. Optionally, +you can run `./ch-2.js` with a number argument to display more or less `Smith +Numbers` (i.e., `./ch-2.js 100` to return the first 100 `Smith Numbers`). +Sample output is shown below: + +``` +$> ./ch-2.js 10 +The first 10 Smith Numbers are 4, 22, 27, 58, 85, 94, 121, 166, 202, and 265. +``` + +## Closing + +I had a lot of fun with these challenges and picked up a little bit of math that +I might have otherwise avoided like the plague. Thanks again, PWC. + +[Perl Weekly Challenge - 133]: https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ +[here]: https://en.wikipedia.org/wiki/Integer_square_root diff --git a/challenge-133/iangoodnight/javascript/ch-1.js b/challenge-133/iangoodnight/javascript/ch-1.js new file mode 100755 index 0000000000..c136092774 --- /dev/null +++ b/challenge-133/iangoodnight/javascript/ch-1.js @@ -0,0 +1,121 @@ +#!/usr/bin/env node +// ch-1.js + +/** + * https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ + * + * Task 1 > Integer Square Root + * ============================ + * + * You are given a positive integer `$N`. + * + * Write a script to calculate the integer square root of the given number. + * + * Please avoid using built-in function. Find out more about it here + * (https://en.wikipedia.org/wiki/Integer_square_root). + * + * Examples + * -------- + * + * Input: $N = 10 + * Output: 3 + * + * Input: $N = 27 + * Output: 5 + * + * Input: $N = 85 + * Output: 9 + * + * Input: $N = 101 + * Output: 10 + * + **/ + +'use strict'; + +/** + * Node built-in dependencies + **/ + +const readline = require('readline'); + +/** + * Our PWC solution + **/ + +function returnIntSqrRoot(input) { + // Validate input + if (!Number.isInteger(+input) || +input < 0) { + throw new Error( + 'returnIntSqrRoot expects a positive integer as an argument', + ); + } + // Crawl through squares starting with 0 + let i = 0; + while (i * i <= +input) { + i += 1; + } + // return the highest passing number + i -= 1; + return i; +} + +/** + * Utilities + **/ + +function repl() { + return new Promise((resolve) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.setPrompt( + 'Enter a positive integer (or, type "\x1b[33mexit\x1b[0m" to quit)> ', + ); + rl.prompt(); + rl.on('line', (line) => { + const trimmed = line.trim(); + + if (trimmed === 'exit' || trimmed === 'n') { + return rl.close(); + } + if (trimmed === 'y') return rl.prompt(); + if (Number.isInteger(+trimmed)) { + console.log('Integer square root:', returnIntSqrRoot(+trimmed)); + } else { + console.log('Arguments must be positive integers only.'); + } + return process.stdout.write( + 'Try again? (\x1b[33my\x1b[0m/\x1b[33mn\x1b[0m)> ', + ); + }).on('close', () => { + console.log('Goodbye.'); + resolve(); + }); + }); +} + +function printBanner() { + const message = 'Integer Square Root Calculator'; + + const outline = '='.repeat(message.length); + + const fgCyan = '\x1b[36m'; + + const reset = '\x1b[0m'; + + const banner = `\n${outline}\n${message}\n${outline}\n`; + + console.log(fgCyan + banner + reset); +} + +(async function main() { + try { + printBanner(); + await repl(); + } catch (error) { + console.log(error); + process.exit(1); + } +})(); diff --git a/challenge-133/iangoodnight/javascript/ch-2.js b/challenge-133/iangoodnight/javascript/ch-2.js new file mode 100755 index 0000000000..5f229008ec --- /dev/null +++ b/challenge-133/iangoodnight/javascript/ch-2.js @@ -0,0 +1,105 @@ +#!/usr/bin/env node +// ch-2.js + +/** + * https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ + * + * Task 2 > Smith Numbers + * ====================== + * + * Write a script to generate the first 10 `Smith Numbers` in base 10. + * + * According to Wikipedia: + * + * > In number theory, a Smith number is a composite number for which, in a + * > given number base, the sum of its digits is equal to the sum of the digits + * > in its prime factorization in the given number base. + * + **/ + +'use strict'; + +/** + * Our PWC solution and some helper functions + **/ + +// First, we need a utility function to return our prime factors +function primeFactors(number) { + let n = parseInt(number, 10); // Input validation + if (number === 'NaN') { + // Fail fast if arg is not a positive integer + throw new Error( + 'function `primeFactors` expects a positive integer as an argument.', + ); + } + const factors = []; + + let divisor = 2; // Starting with 2, check for remainder + while (n >= 2) { + if (n % divisor === 0) { + // If modulo is 0, push to factors + factors.push(divisor); + n /= divisor; + } else { + divisor += 1; // Else, increment the divisor and try again + } + } + return factors; +} + +// Reduce numbers to the sum of their digits +function sumDigits(number) { + return number + .toString() + .split('') + .reduce((sum, digit) => sum + parseInt(digit, 10), 0); +} + +// Find our actual smith numbers with the help of `primeFactors` and `sumDigits` +function identifySmithNumbers(limit = 10) { + const smithNumbers = []; + + let test = 4; // Smith numbers are composite numbers, so skip the primes + while (smithNumbers.length < limit) { + const primeFactorsArr = primeFactors(test); + + const factorSum = primeFactorsArr.reduce( + (sum, factor) => sum + sumDigits(factor), + 0, + ); + + const digitSum = sumDigits(test); + + if (factorSum === digitSum && primeFactorsArr.length !== 1) { + smithNumbers.push(test); + } + test += 1; + } + return smithNumbers; +} + +/** + * General utilities + **/ + +// Takes an array and returns it as human-readable comma separated list +function stringWithOxfordComma(arr = []) { + const reversed = [...arr].reverse(); + + const [last, ...rest] = reversed; + + const first = [...rest].reverse().join(', '); + + return `${first}, and ${last}`; +} + +// IIFE to handle args and print results +(function main() { + const limit = process.argv[2] || 10; + + const smithNumbers = identifySmithNumbers(limit); + + const stringified = stringWithOxfordComma(smithNumbers); + + console.log(`The first ${limit} Smith Numbers are ${stringified}.`); +})(); diff --git a/challenge-133/iangoodnight/perl/README.md b/challenge-133/iangoodnight/perl/README.md new file mode 100644 index 0000000000..a010ab26e6 --- /dev/null +++ b/challenge-133/iangoodnight/perl/README.md @@ -0,0 +1,233 @@ +# [Perl Weekly Challenge - 133] + +**Trigger warning:** There's lots of math in this week's challenges. Recovering +math addicts may want to avoid these solutions. Personally, I've always been +more the "hooked on phonics" type, so it was fun brushing up on some of these +mathematical concepts as I worked through my solutions. + +## Task 1 > Integer Square Root + +You are given a positive integer `$N`. + +Write a script to calculate the integer square root of the given number. + +Please avoid using built-in function. Find out more about it [here]. + +**Examples** + +``` +Input: $N = 10 +Output: 3 + +Input: $N = 27 +Output: 5 + +Input: $N = 85 +Output: 9 + +Input: $N = 101 +Output: 10 +``` + +### Solution + +I wasn't familiar with the idea of integer square roots, but after checking out +the linked Wikipedia reference ([here]), I found that the term is equivalent to +the largest integer square root that is less than or equal to our given input +(a square root floor, so to speak. There are likely more elegant, mathematical +approaches to this problem, but my approach was to just start at 0 and check +incrementing squares until I exceeded the input. + +```perl + +sub return_int_sqr_root { + my $input = shift; + chomp $input; + + # Validate input + $input =~ s{ + ^\s+ # Leading whitespace + | + \s+$ # Trailing whitespace + }{}gmx; + if ( !$input =~ /^\d+$/m ) { + return; + } + + # Crawl through squares starting with 0 + my $i = 0; + while ( $i * $i <= $input ) { + $i++; + } + + # Return the highest passing number + return --$i; +} + +``` + +#### `ch-1.pl` + +Generally, I try to write a little test runner and some test cases, but in this +case, it felt like overkill, so instead running `./ch-1.pl` initiates a mini +REPL. The REPL prompts for input and returns the integer square root. Sample +output is shown below: + +``` +$> ./ch-1.pl + +============================== +Integer Square Root Calculator +============================== + +Enter a positive integer (or, type "exit" to quit)> 10 +Integer square root: 3 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 27 +Integer square root: 5 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 85 +Integer square root: 9 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 101 +Integer square root: 10 +Try again? (y/n)> n +Goodbye. + +``` + +## Task 2 > Smith Number + +Write a script to generate the first 10 `Smith Numbers` in base 10. + +According to Wikipedia: + +> In number theory, a Smith number is a composite number for which, in a given +> number base, the sum of its digits is equal to the sum of the digits in its +> prime factorization in the given number base. + +### Solution + +At first, the language of this challenge had me feeling a little out of my +depths. But, breaking down the problem, I realized that it wasn't as +complicated as I had initially believed. It was clear that I needed a +subroutine to reduce a given number to its prime factors, a subroutine to +reduce a number to the sum of its digits, and that between those two functions, +I could find these "Smith Numbers." Admittedly, my first solutions were all +incorrect as I glazed right over that phrase "composite number," which, I've +since learned, is a fancy way of saying "ain't prime." + +```perl + +# First, we need a utility function to find and return our prime factors +sub prime_factors { + my $number = shift; + + $number =~ s{ # Trim whitespace, probably unnecessary, but it won't hurt + \A # Start of the line + \s+ # Leading whitespace + | # Alternating with + \s+ # Trailing whitespace + \z # End of line + }{}gx; + + # Validate our input is an integer + if ( !$number =~ m/\A\d+\z/ ) { + + # Bail + return 0; + } + my @factors; + my $divisor = 2; # Starting with 2, we'll divide and check for modulo + + while ( $number >= 2 ) { + if ( $number % $divisor == 0 ) { + + # If modulo is zero, push $divisor to @factors + push @factors, $divisor; + $number /= $divisor; + } + else { + # Else, increment $divisor and try again + $divisor += 1; + } + } + return \@factors; +} + +# Helper to reduce number to sum of its digits +sub sum_digits { + my $number = shift; + + # Trim + $number =~ s{ \A \s+ | \s+ \z }{}gx; + + # Split digits + my @digits = split //, $number; + + # Reduce + my $sum = 0; + foreach my $digit (@digits) { + $sum += $digit; + } + return $sum; +} + +# Find `Smith Numbers` with the help of our two subroutines, `prime_factors` +# and `sum_digits` +sub find_smith_numbers { + + # We need to find the first ten, but we might as well write it to find more + my $limit = shift // '10'; + my @smith_numbers; + + # Smith Numbers are not prime numbers, so we might as well start at 4 + my $test = '4'; + + # Search until we hit our limit + while ( scalar @smith_numbers < $limit ) { + my @primes = @{ prime_factors($test) }; + my $factor_sum = 0; + + # Reduce @primes to sum of its digits + foreach my $prime (@primes) { + $factor_sum += sum_digits($prime); + } + + my $digit_sum = sum_digits($test); + + # Check for matching sums and for prime number (if scalar @primes == 1, + # $test is a prime number) + if ( $factor_sum == $digit_sum && scalar @primes != 1 ) { + push @smith_numbers, $test; + } + + $test += 1; + } + return \@smith_numbers; +} + +``` + +### `ch-2.pl` + +Again, this didn't feel like a situation for writing a bunch of tests, as I +wasn't sure the solution even worked until I compared the output against a list +of `Smith Numbers` I found online, so running `./ch-2.pl` will output the first +10 `Smith Numbers` without much in the way of pomp or circumstance. Optionally, +you can run `./ch-2.pl` with a number argument to display more or less `Smith +Numbers` (i.e., `./ch-2.pl 100` to return the first 100 `Smith Numbers`). +Sample output is shown below: + +``` +$> ./ch-2.pl 10 +The first 10 Smith Numbers are 4, 22, 27, 58, 85, 94, 121, 166, 202, and 265. +``` + +## Closing + +I had a lot of fun with these challenges and picked up a little bit of math that +I might have otherwise avoided like the plague. Thanks again, PWC. + +[Perl Weekly Challenge - 133]: https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ +[here]: https://en.wikipedia.org/wiki/Integer_square_root diff --git a/challenge-133/iangoodnight/perl/ch-1.pl b/challenge-133/iangoodnight/perl/ch-1.pl new file mode 100755 index 0000000000..4a2fa4e2fe --- /dev/null +++ b/challenge-133/iangoodnight/perl/ch-1.pl @@ -0,0 +1,140 @@ +#!/usr/bin/perl +# ch-1.pl + +=begin comment + + * https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ + * + * Task 1 > Integer Square Root + * ============================ + * + * You are given a positive integer `$N`. + * + * Write a script to calculate the integer square root of the given number. + * + * Please avoid using built-in function. Find out more about it here + * (https://en.wikipedia.org/wiki/Integer_square_root). + * + * Examples + * -------- + * + * Input: $N = 10 + * Output: 3 + * + * Input: $N = 27 + * Output: 5 + * + * Input: $N = 85 + * Output: 9 + * + * Input: $N = 101 + * Output: 10 + +=end comment +=cut + +use strict; +use warnings; +use utf8; +use open ':std', ':encoding(UTF-8)'; +use Term::ANSIColor; + +################################################################################ +# Our PWC Solution +################################################################################ + +sub return_int_sqr_root { + my $input = shift; + chomp $input; + + # Validate input + $input =~ s{ + ^\s+ # Leading whitespace + | + \s+$ # Trailing whitespace + }{}gmx; + if ( !$input =~ /^\d+$/m ) { + return; + } + + # Crawl through squares starting with 0 + my $i = 0; + while ( $i * $i <= $input ) { + $i++; + } + + # Return the highest passing number + return --$i; +} + +################################################################################ +# Utilities +################################################################################ + +sub print_banner { + my $message = 'Integer Square Root Calculator'; + my $border = q{=} x length $message; + my $empty = q{}; + my @elements = ( $empty, $border, $message, $border, $empty, $empty ); + + return print color('cyan'), join( "\n", @elements ), color('reset'); +} + +################################################################################ +# REPL +################################################################################ + +sub main { + my $prompt = + 'Enter a positive integer (or, type "' + . color('yellow') . 'exit' + . color('reset') + . '" to quit)> '; + + my $retry = + 'Try again? (' + . color('yellow') . 'y' + . color('reset') . q{/} + . color('yellow') . 'n' + . color('reset') . ')> '; + + print_banner(); + print $prompt; + + my $complete = 0; + + while ( my $line = <> ) { + chomp $line; + $line =~ s{ + ^\s+ # Remove leading whitespace + | + \s+$ # Remove trailing whitespace + }{}gmx; + + if ( $line eq 'exit' || $line eq 'n' ) { + print "Goodbye.\n"; + return 1; + } + if ( $line eq 'y' ) { + $complete = 0; + print $prompt; + next; + } + if ( $line =~ m/^\d+$/m && !$complete ) { + print 'Integer square root: '; + print color('yellow'), return_int_sqr_root($line), color('reset'), "\n"; + } + elsif ($complete) { + print "\n", $retry; + } + else { + print "Arguments must be positive integers only.\n"; + } + + $complete = 1; + print $retry; + } + return 1; +} + +main(); diff --git a/challenge-133/iangoodnight/perl/ch-2.pl b/challenge-133/iangoodnight/perl/ch-2.pl new file mode 100755 index 0000000000..0dfcdaf1c7 --- /dev/null +++ b/challenge-133/iangoodnight/perl/ch-2.pl @@ -0,0 +1,143 @@ +#!/usr/bin/perl +# ch-2.pl + +=begin comment + + * https://theweeklychallenge.org/blog/perl-weekly-challenge-133/ + * + * Task 2 > Smith Numbers + * ====================== + * + * Write a script to generate the first 10 `Smith Numbers` in base 10. + * + * According to Wikipedia: + * + * > In number theory, a Smith number is a composite number for which, in a + * > given number base, the sum of its digits is equal to the sum of the digits + * > in its prime factorization in the given number base. + +=end comment +=cut + +use strict; +use warnings; +use utf8; +use Data::Dumper; + +################################################################################ +# Our PWC solution, along with some help subroutines +################################################################################ + +# First, we need a utility function to find and return our prime factors +sub prime_factors { + my $number = shift; + + $number =~ s{ # Trim whitespace, probably unnecessary, but it won't hurt + \A # Start of the line + \s+ # Leading whitespace + | # Alternating with + \s+ # Trailing whitespace + \z # End of line + }{}gx; + + # Validate our input is an integer + if ( !$number =~ m/\A\d+\z/ ) { + + # Bail + return 0; + } + my @factors; + my $divisor = 2; # Starting with 2, we'll divide and check for modulo + + while ( $number >= 2 ) { + if ( $number % $divisor == 0 ) { + + # If modulo is zero, push $divisor to @factors + push @factors, $divisor; + $number /= $divisor; + } + else { + # Else, increment $divisor and try again + $divisor += 1; + } + } + return \@factors; +} + +# Helper to reduce number to sum of its digits +sub sum_digits { + my $number = shift; + + # Trim + $number =~ s{ \A \s+ | \s+ \z }{}gx; + + # Split digits + my @digits = split //, $number; + + # Reduce + my $sum = 0; + foreach my $digit (@digits) { + $sum += $digit; + } + return $sum; +} + +# Find `Smith Numbers` with the help of our two subroutines, `prime_factors` +# and `sum_digits` +sub find_smith_numbers { + + # We need to find the first ten, but we might as well write it to find more + my $limit = shift // '10'; + my @smith_numbers; + + # Smith Numbers are not prime numbers, so we might as well start at 4 + my $test = '4'; + + # Search until we hit our limit + while ( scalar @smith_numbers < $limit ) { + my @primes = @{ prime_factors($test) }; + my $factor_sum = 0; + + # Reduce @primes to sum of its digits + foreach my $prime (@primes) { + $factor_sum += sum_digits($prime); + } + + my $digit_sum = sum_digits($test); + + # Check for matching sums and for prime number (if scalar @primes == 1, + # $test is a prime number) + if ( $factor_sum == $digit_sum && scalar @primes != 1 ) { + push @smith_numbers, $test; + } + + $test += 1; + } + return \@smith_numbers; +} + +################################################################################ +# Utilities +################################################################################ + +sub list_with_oxford { + my @list = @{ +shift }; + my $last_number = $list[-1]; + + return join( ', ', @list[ 0 .. $#list - 1 ] ) . ', and ' . $last_number; +} + +################################################################################ +# Main +################################################################################ + +sub main { + my $limit = shift @ARGV // '10'; + my $smith_numbers = find_smith_numbers $limit; + my $result_string = list_with_oxford $smith_numbers; + + print "The first $limit Smith Numbers are $result_string.\n"; + return 1; +} + +main(); diff --git a/challenge-133/iangoodnight/ruby/README.md b/challenge-133/iangoodnight/ruby/README.md new file mode 100644 index 0000000000..8854a2dfb4 --- /dev/null +++ b/challenge-133/iangoodnight/ruby/README.md @@ -0,0 +1,146 @@ +# [Perl Weekly Challenge - 133] _Ruby Edition_ + +This is my first crack at a Ruby submission for the PWC. I'm still pretty new +to the language, and porting my Perl to Ruby is a fun way to explore the syntax. + +## Task 1 > Integer Square Root + +You are given a positive integer `$N`. + +Write a script to calculate the integer square root of the given number. + +Please avoid using built-in function. Find out more about it [here]. + +**Examples** + +``` +Input: $N = 10 +Output: 3 + +Input: $N = 27 +Output: 5 + +Input: $N = 85 +Output: 9 + +Input: $N = 101 +Output: 10 +``` + +### Solution + +This is pretty much a direct translation of my Perl solution, so it may be that +I am missing some opportunities here. + +```ruby + +def int_sqr_root(input) + if !input.integer? || !input.positive? + puts 'Input must be a positive integer' + return + end + # Crawl through squares starting with 0 + i = 0 + (i += 1) while i * i <= input + # Return the highest passing number + i -= 1 +end + +``` + +#### `ch-1.rb` + +Running `./ch-1.rb` initiates a mini REPL. The REPL prompts for input and +returns the integer square root. Sample output is shown below: + +``` +$> ./ch-1.rb + +============================== +Integer Square Root Calculator +============================== + +Enter a positive integer (or, type "exit" to quit)> 10 +Integer square root: 3 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 27 +Integer square root: 5 +Try again? (y/n)> 85 +Integer square root: 9 +Try again? (y/n)> y +Enter a positive integer (or, type "exit" to quit)> 101 +Integer square root: 10 +Try again? (y/n)> n +Goodbye. + +``` + +## Task 2 > Smith Number + +Write a script to generate the first 10 `Smith Numbers` in base 10. + +According to Wikipedia: + +> In number theory, a Smith number is a composite number for which, in a given +> number base, the sum of its digits is equal to the sum of the digits in its +> prime factorization in the given number base. + +### Solution + +```ruby + +# First, a utility method to find and return our prime factors +def prime_factors(number) + factors = [] + while number >= 2 # Starting with 2, divide and check modulo + if (number % divisor ||= 2).zero? # If modulo is zero, push to `factors` + factors.push(divisor) + number /= divisor + else + divisor += 1 # Else, increment divisor and try again + end + end + factors +end + +# Helper method to reduce number to sum of it digits +def sum_digits(number) + number.to_s.split(//).map(&:to_i).inject(0, :+) +end + +# Method to reduce our primes to the sum of their digits +def sum_primes(primes) + primes.reduce(0) { |sum, prime| sum + sum_digits(prime) } +end + +# Find `Smith Numbers` with our methods, `prime_factors`, `sum_digits`, +# `sum_primes` +def find_smith_numbers(limit: 10) + smith_numbers = [] + test = 4 + while smith_numbers.length < limit.to_i + primes = prime_factors(test) + prime_sum = sum_primes(primes) + digit_sum = sum_digits(test) + smith_numbers.push(test) if pr |
