aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2021-10-29 06:36:50 +0100
committerGitHub <noreply@github.com>2021-10-29 06:36:50 +0100
commit2ec3aa58b76a128d165bbc2563c28b020ee2294f (patch)
tree19939b561716a99e1e54ba2c816e884e06d6fe84
parentdc82718186854e495ed16900b1c485d170042a18 (diff)
parent2748f963f573e11a7b2aaed1d27e9a83f7799aae (diff)
downloadperlweeklychallenge-club-2ec3aa58b76a128d165bbc2563c28b020ee2294f.tar.gz
perlweeklychallenge-club-2ec3aa58b76a128d165bbc2563c28b020ee2294f.tar.bz2
perlweeklychallenge-club-2ec3aa58b76a128d165bbc2563c28b020ee2294f.zip
Merge pull request #5115 from jo-37/contrib
Solutions to challenge 136
-rwxr-xr-xchallenge-136/jo-37/perl/ch-1.pl70
-rwxr-xr-xchallenge-136/jo-37/perl/ch-2.pl107
2 files changed, 177 insertions, 0 deletions
diff --git a/challenge-136/jo-37/perl/ch-1.pl b/challenge-136/jo-37/perl/ch-1.pl
new file mode 100755
index 0000000000..32293d1e8e
--- /dev/null
+++ b/challenge-136/jo-37/perl/ch-1.pl
@@ -0,0 +1,70 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Test2::V0;
+use Math::Prime::Util qw(gcd logint);
+use experimental 'signatures';
+
+our ($tests, $examples);
+
+run_tests() if $tests || $examples; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [M N ...]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+M N ...
+ Check if M, N, ... are "friendly".
+
+EOS
+
+
+### Input and Output
+
+say 0 + !!friendly(@ARGV);
+
+
+### Implementation
+
+# I couldn't find any reference to "two friendly". Maybe Mohammad
+# created this concept?
+# There's no need to restrict the definition to two numbers.
+
+sub friendly (@n) {
+ my ($gcd, $pot) = gcd @n;
+ # Calculate the integer binary logarithm of the GCD together with
+ # its (back-)exponentiation and return "false" for a zero logarithm.
+ logint $gcd, 2, \$pot or return;
+
+ # Check if the GCD is a full power of two.
+ $gcd == $pot;
+}
+
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ ok friendly(8, 24), 'example 1';
+ ok !friendly(26, 39), 'example 2';
+ ok friendly(4, 10), 'example 3';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ ok !friendly(2, 3), 'no common divisor';
+ ok friendly(12, 20, 28), 'three friendly';
+ ok !friendly(12, 36, 60), 'GCD not a power of two';
+ }
+
+ done_testing;
+ exit;
+}
diff --git a/challenge-136/jo-37/perl/ch-2.pl b/challenge-136/jo-37/perl/ch-2.pl
new file mode 100755
index 0000000000..25290da21b
--- /dev/null
+++ b/challenge-136/jo-37/perl/ch-2.pl
@@ -0,0 +1,107 @@
+#!/usr/bin/perl -s
+
+use v5.16;
+use Math::Prime::Util qw(todigits lucasu);
+use List::MoreUtils 'reduce_0';
+use Memoize qw(memoize flush_cache);
+use Benchmark 'cmpthese';
+use Test2::V0;
+use experimental 'signatures';
+
+our ($tests, $examples, $benchmark);
+memoize('count_fib_seq');
+
+run_tests() if $tests || $examples || $benchmark; # does not return
+
+die <<EOS unless @ARGV;
+usage: $0 [-examples] [-tests] [-benchmark] [N]
+
+-examples
+ run the examples from the challenge
+
+-tests
+ run some tests
+
+-benchmark
+ compare a recursive counting implementation with a brute force scan.
+
+N
+ Count Fibonacci subsequences that give a sum of N.
+
+EOS
+
+
+### Input and Output
+
+say count_fib_seq($ARGV[0]);
+
+
+### Implementation
+
+# Try Fibonacci numbers F(k) starting with the k-th element, utilizing
+# F(k) == lucas_u(1, -1, k):
+# - Return the count if if F(k) is larger than or equal to N.
+# - Add to the count the number of Fibonacci sequences that give a sum
+# of N - F(k), starting with F(k + 1) by recursion.
+# Note: The XS implementation of "lucasu" is much faster than its
+# memoizing counterpart.
+
+sub count_fib_seq ($n, $k = 2) {
+ my $count = 0;
+ while () {
+ my $fib = lucasu 1, -1, $k;
+ return $count + ($fib == $n) if $fib >= $n;
+ $count += count_fib_seq($n - $fib, ++$k);
+ }
+}
+
+# An alternative brute force approach:
+# Try all Fibonacci subsequences for a matching sum. Taking the binary
+# digits of the iterator variable as selectors for corresponding
+# Fibonacci numbers.
+# This was intended as a cross check for the counting implementation.
+
+sub scan_fib_seq ($n) {
+ my (@fib, $f) = (1, 1);
+ push @fib, $f while ($f = $fib[-2] + $fib[-1]) <= $n;
+ shift @fib;
+
+ scalar grep {
+ $n == reduce_0 {$a += $fib[$_] * $b} todigits($_, 2, @fib)
+ } 1 .. 2 ** @fib - 1;
+}
+
+### Examples and tests
+
+sub run_tests {
+ SKIP: {
+ skip "examples" unless $examples;
+
+ is count_fib_seq(16), 4, 'example 1';
+ is count_fib_seq(9), 2, 'example 2';
+ is count_fib_seq(15), 2, 'example 3';
+ }
+
+ SKIP: {
+ skip "tests" unless $tests;
+
+ grep {
+ count_fib_seq($_) != scan_fib_seq($_) and !fail "$_ failed";
+ } 0 .. 100 or pass 'cross check';
+ }
+
+ SKIP: {
+ skip "benchmark" unless $benchmark;
+
+ cmpthese(0, {
+ scan => sub {scan_fib_seq(1000)},
+ count => sub {
+ flush_cache('count_fib_seq');
+ count_fib_seq(1000);
+ }
+ });
+ }
+
+ done_testing;
+ exit;
+}