diff options
| author | Mohammad S Anwar <mohammad.anwar@yahoo.com> | 2022-06-06 05:57:53 +0100 |
|---|---|---|
| committer | Mohammad S Anwar <mohammad.anwar@yahoo.com> | 2022-06-06 05:57:53 +0100 |
| commit | a085a39666de424cb247641c3c73e4c0d1c1bae4 (patch) | |
| tree | 044bb44186992cf98791b019d7d594e1df2a901a /challenge-168/james-smith | |
| parent | 4b569aef972ae1d1b9b6811ca336cd77a03b4998 (diff) | |
| download | perlweeklychallenge-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.md | 100 |
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] +} +``` |
