aboutsummaryrefslogtreecommitdiff
path: root/challenge-168/james-smith
diff options
context:
space:
mode:
authorMohammad S Anwar <mohammad.anwar@yahoo.com>2022-06-06 05:57:53 +0100
committerMohammad S Anwar <mohammad.anwar@yahoo.com>2022-06-06 05:57:53 +0100
commita085a39666de424cb247641c3c73e4c0d1c1bae4 (patch)
tree044bb44186992cf98791b019d7d594e1df2a901a /challenge-168/james-smith
parent4b569aef972ae1d1b9b6811ca336cd77a03b4998 (diff)
downloadperlweeklychallenge-club-a085a39666de424cb247641c3c73e4c0d1c1bae4.tar.gz
perlweeklychallenge-club-a085a39666de424cb247641c3c73e4c0d1c1bae4.tar.bz2
perlweeklychallenge-club-a085a39666de424cb247641c3c73e4c0d1c1bae4.zip
- Added template for week 168.
Diffstat (limited to 'challenge-168/james-smith')
-rw-r--r--challenge-168/james-smith/README.md100
1 files changed, 100 insertions, 0 deletions
diff --git a/challenge-168/james-smith/README.md b/challenge-168/james-smith/README.md
new file mode 100644
index 0000000000..5c08199913
--- /dev/null
+++ b/challenge-168/james-smith/README.md
@@ -0,0 +1,100 @@
+[< Previous 166](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-166/james-smith) |
+[Next 168 >](https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-168/james-smith)
+
+# The Weekly Challenge 167
+
+You can find more information about this weeks, and previous weeks challenges at:
+
+ https://theweeklychallenge.org/
+
+If you are not already doing the challenge - it is a good place to practise your
+**perl** or **raku**. If it is not **perl** or **raku** you develop in - you can
+submit solutions in whichever language you feel comfortable with.
+
+You can find the solutions here on github at:
+
+https://github.com/drbaggy/perlweeklychallenge-club/tree/master/challenge-167/james-smith
+
+# Challenge 1 - Circular Prime
+
+***Write a script to find out first 10 circular primes having at least 3 digits (base 10). A circular prime is a prime number with the property that the number generated at each intermediate step when cyclically permuting its (base 10) digits will also be prime.***
+
+## Solution
+
+*We are going to slightly extend this to find the first 19 circular primes - includes 4 1-digit primes and 5 2-digit circular primes and the 10 3+-digit ciruclar primes < one-million - After the largest 6-digit circular prime the next circular prime is the 19 digit prime - 1,111,111,111,111,111,111*
+
+We use `Math::Prime::Util`s `next_prime` function to loop through the primes. Before we check for primality of each of the permutations we can remove trivial cases:
+
+ * We know all 1-digit primes are circular so we take these out first `#1` - in fact the remaining logic does not work as we assume there are other rotations - and the regex we see next would remove `2` & `5` the only primes that contain either of these digits;
+ * We then remove numbers containing `0`, `2`, `4`, `5`, `6` or `8` as at least one rotation would end in this digit and therefore the number sould not be prime;
+ * As we are looking for an exemplar for each rotation we take the lowest one - we just check that the supplied prime is less than any of the rotations.
+
+ **Note** we use next here to short cut the map and jump to the next loop element.
+
+ To rotate the digits we use the 4 parameter version of `substr` - `substr $string, $start, $length, $replacement` returns the substring from `$start`, but replaces the section returned with the contents of the fourth parameter...
+
+ In this case we do `substr $a, 0, 0, substr $a, -1, 1, ''`. Firstly the right hand `substr` is evaluated - which takes the last element of `$a` and returns it, and replaces this with an empty string so if `$a` was `1234` then it has becomes `123` and returns `4`. We now then evaluate the first `substr` which returns the `0` character string from the start of `$a` (*i.e.* an empty string) and replaces it with the `4` from before so we end up with `4123` and sunsequent class give `3412` and `2341`.
+
+ * Now we look to see if we have any non-primes in the rotation using `is_prime`.. If we do then we skip the loop
+
+ * Finally if we have got through all the filters we push the prime `$p` on to the results array.>
+
+```perl
+use Math::Prime::Util qw(next_prime is_prime);
+my( $p, $N, @q, @res ) = ( 1, 19 );
+
+while( @res < $N ) {
+ ( ( $t = $p = next_prime $p ) < 10
+ || $p !~ /[024568]/
+ && ( ! grep { !is_prime( $_ ) && (next) }
+ map { ( substr$t,0,0,substr$t,-1,1,'' ) || $t < $p ? (next) : $t }
+ 2 .. length $p )
+ ) && ( push @res, $p )
+}
+
+say for @res;
+```
+
+Now some notes on efficiency.
+
+ * To generate the 19 exemplars - we loop through 17,981 primes. The regex filter (and the <10) filters out 18,422 of these to leave just 559 primes
+ that go through the rotation code.
+ * This filters our another 347 primes leaving just 212 sets of primes to check for primality.
+ * We then just do 346 prime checks on these sets to rule in/rule out the number
+
+# Challenge 2 - Gamma function
+
+***Implement subroutine gamma() using the Lanczos approximation method.***
+
+## Solution
+
+The gamma function is the genaralisation of the factorial function `Gamma(n) = (n-1)!` for positive integers.
+
+We will use Lanczos approximation...
+
+ * If z is an integer and less than or equal to 0 - we return the special string 'inf' as the value is infinite.
+ * If z is less than 0.5 - we use the calulation beased on `gamma(1-z)` multiplied the the factor `PI/sin(PI * z)`
+ * Finally we use the lanczos approximation.
+ * This starts by computing the sum in the map, then computing the value based on this sum
+ * we use `( map( {} @PV ), fn(z,x) )[-1]` to put this all in one line, we also re-use `$i` after the loop, to store the value of `$z+@PV-1.5` which is used twice AND again to store the final value - so we can decide to round it back down to an integer if we are close to integer value. This I agree is nasty!!!
+ * `$RP` is `sqrt(2*$PI)` but evaluated for speed
+
+```perl
+const my $PI => 3.1415926535897932384626433832;
+const my $RP => 2.5066282746310002416123552393;
+const my $EP => 0.000000000001;
+const my $X => 0.99999999999980993;
+const my @PV => (
+ 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059,
+ 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7,
+);
+
+sub gamma {
+ my($i,$x,$z)=(0,$X,$_[0]);
+ ( $z<=0 && abs($z-int$z) < $EP ) ? 'inf'
+ : $z < 0.5 ? $PI / sin($PI*$z) * gamma(1-$z)
+ : ( map( {$x+=$_/($z+$i++)} @PV ),
+ abs( ( $i = $RP*( $i = $z+@PV-1.5 )**($z-0.5) * $x / exp $i ) - int $i ) < $EP ? int $i : $i
+ )[-1]
+}
+```