aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMohammad S Anwar <Mohammad.Anwar@yahoo.com>2022-02-06 18:36:56 +0000
committerGitHub <noreply@github.com>2022-02-06 18:36:56 +0000
commit289b0c07c28cd602e3bab4eea7b1085bc1e14a7f (patch)
tree964bd24ad6187aa983a485348780a7f166b765be
parentd55e50d4d09c3d6c3c69df54cab107e7c82772f2 (diff)
parent17373a7f627f6e03a8b9c07b21ff0a90896ad230 (diff)
downloadperlweeklychallenge-club-289b0c07c28cd602e3bab4eea7b1085bc1e14a7f.tar.gz
perlweeklychallenge-club-289b0c07c28cd602e3bab4eea7b1085bc1e14a7f.tar.bz2
perlweeklychallenge-club-289b0c07c28cd602e3bab4eea7b1085bc1e14a7f.zip
Merge pull request #5616 from drbaggy/master
Neater (faster and more readable) code for example 2
-rw-r--r--challenge-150/james-smith/README.md31
-rw-r--r--challenge-150/james-smith/perl/ch-2.pl52
2 files changed, 79 insertions, 4 deletions
diff --git a/challenge-150/james-smith/README.md b/challenge-150/james-smith/README.md
index 1e048774b7..552d615280 100644
--- a/challenge-150/james-smith/README.md
+++ b/challenge-150/james-smith/README.md
@@ -70,3 +70,34 @@ say for grep{my$t=$_;!grep{!($t%$_)}@p2}1..$N;
```
**Note** `say` without any parameters - outputs the contents of `$_` and then sends a carriage return. so `say for @A;` outputs all elements of the array `@A` on separate lines.
+
+## Follow up
+
+We can re-write the inefficient double `grep` more elegantly with nested `for`*each* loops. The new code becomes:
+
+```perl
+my ( $N, @p2 ) = ( @ARGV ? $ARGV[0] : 500 , 4 );
+
+P: for ( my $c = 3; $c*$c <= $N; $c += 2 ) {
+ $_ > $c ? last : $c*$c % $_ || next P for @p2;
+ push @p2, $c*$c;
+}
+
+O: for my $t ( 1 .. $N ) {
+ $_ > $t ? last : $t % $_ || next O for @p2;
+ say $t;
+}
+```
+
+### Notes:
+ * We optimize the inner loop by allowing it to finish early if:
+ * We have a prime^2 value greater than `$t`
+ * We have a square factor
+
+ * The difference in these two cases are:
+ * We end the inner loop and output the number as a square-free int (`last`)
+ * We skip to the next iteration of the outer loop (`next O`) without doing anything
+
+ * The optimized version gives anywhere between 75% and 90% speed up... (values of `$N` between 100 and 1,000,000)
+
+ * We have also re-written the prime generator to use the same `next {label}` trick, and this leads to a certain symmetry between the two loops.
diff --git a/challenge-150/james-smith/perl/ch-2.pl b/challenge-150/james-smith/perl/ch-2.pl
index a6af28ddcd..95b4f2464c 100644
--- a/challenge-150/james-smith/perl/ch-2.pl
+++ b/challenge-150/james-smith/perl/ch-2.pl
@@ -4,11 +4,55 @@ use strict;
use warnings;
use feature qw(say);
+use Test::More;
+use Benchmark qw(cmpthese timethis);
+use Data::Dumper qw(Dumper);
-my($N,@p2) = (@ARGV?$ARGV[0]:500,4);
+cmpthese( 500_000, {
+ 'grep' => sub { non_square( 100 ) },
+ 'for' => sub { non_square_x( 100 ) },
+});
-for(my$c=3;$c*$c<$N;$c+=2){
- ($_>$c)?((push@p2,$c*$c),last):$c*$c%$_||last for@p2;
+cmpthese( 50_000, {
+ 'grep' => sub { non_square( 1000 ) },
+ 'for' => sub { non_square_x( 1000 ) },
+});
+
+cmpthese( 5_000, {
+ 'grep' => sub { non_square( 10_000 ) },
+ 'for' => sub { non_square_x( 10_000 ) },
+});
+
+cmpthese( 500, {
+ 'grep' => sub { non_square( 100_000 ) },
+ 'for' => sub { non_square_x( 100_000 ) },
+});
+
+cmpthese( 100, {
+ 'grep' => sub { non_square( 1_000_000 ) },
+ 'for' => sub { non_square_x( 1_000_000 ) },
+});
+
+sub non_square {
+ my($N,@p2) = ($_[0],4);
+ for(my$c=3;$c*$c<$N;$c+=2){
+ ($_>$c)?((push@p2,$c*$c),last):$c*$c%$_||last for@p2;
+ }
+ return grep{my$t=$_;!grep{!($t%$_)}@p2}1..$N;
}
-say for grep{my$t=$_;!grep{!($t%$_)}@p2}1..$N;
+sub non_square_x {
+ my ( $N, @p2, @r ) = ( @ARGV ? $ARGV[0] : 500 , 4 );
+
+ P: for ( my $c = 3; $c*$c <= $N; $c += 2 ) {
+ $_ > $c ? last : $c*$c % $_ || next P for @p2;
+ push @p2, $c*$c;
+ }
+
+ O: for my $t ( 1 .. $N ) {
+ $_ > $t ? last : $t % $_ || next O for @p2;
+ push @r, $t;
+ }
+
+ @r;
+}