Keep It Simple, Stupid PerlMonks

### Sieve of Eratosthenes with closures

by thinker (Parson)
 on Jul 20, 2003 at 22:26 UTC Need Help??
 Category: Fun Stuff Author/Contact Info thinker Description: A Sieve of Eratosthenes implemented with closures. I was looking over an old Java project for Uni where I had implemented a Sieve as Objects connected by pipelines. It occurred to me closures could do a similar job far more concisely. Here was my effort. I hope this is the correct section to post in. I found it fun, anyway. :-) #!/usr/bin/perl # prints all prime numbers up to the value of the command line argumen +t, or 500 if no args; use strict; use warnings; use POSIX; # for ceil() my @primes = ( 1, 2 ); # prime the list of primes :-) my \$max = shift || 500; # initialise \$max if no command line argument my \$enough = ceil ( \$max ** 0.5 ); # we only need to filter until we have >= the sqrt sub sieve { # create a new closure to act as a part of the sieve # the number that this closure will filter out multiples of my \$divisor = shift; my \$sieve_ref; return sub { my \$number = shift; # Does \$number divide by divisor? # If so, it's not prime, so bail out; return unless \$number % \$divisor; # if we have already created a next sieve, # then let it deal with the number # pass \$number to it, and return; if ( \$sieve_ref ){ &\$sieve_ref( \$number ); return }; #if we get this far, we have a new prime # if we need to make a new sieve to filter,do so \$sieve_ref = sieve( \$number ) if \$number < \$enough; # push the prime onto the array push @primes, \$number; }; }; # the main program my \$sieve = sieve( 2 ); # initialise &\$sieve( \$_ ) for 3 .. \$max; print "The primes less than or equal to \$max are @primes\n";
Replies are listed 'Best First'.
•Re: Sieve of Eratosthenes with closures
by merlyn (Sage) on Jul 20, 2003 at 23:07 UTC
Here's a slightly more direct implementation of that:
use strict; \$|++; my \$generator = do { my \$num = 1; sub { ++\$num } }; while (1) { my \$prime = \$generator->(); print "\$prime\n"; ## don't let any multiple of these escape: my \$oldgenerator = \$generator; \$generator = sub { { my \$next_prime = \$oldgenerator->(); redo if \$next_prime % \$prime == 0; return \$next_prime; } }; }

-- Randal L. Schwartz, Perl hacker
Be sure to read my standard disclaimer if this is a reply.

Re: Sieve of Eratosthenes with closures
by tilly (Archbishop) on Jul 21, 2003 at 00:14 UTC
Not all prime number sieves are the Sieve of Eratosthenes.

The key insight that makes the Sieve of Eratosthenes so nice is that for each prime you only have to do an operation on multiples of that prime. This causes the running time to be just slightly supralinear (IIRC it is O(n*log(log(n))) to get all the primes up to n) rather than being close to O(n**1.5) (yours should be O(n**1.5/log(n)).

Here is an actual Sieve of Eratosthenes implemented with closures. It isn't that fast, but it scales right. Memory usage should scale O(sqrt(n)).

#! /usr/bin/perl -w use strict; sub build_sieve { my \$n = 0; my @upcoming_factors = (); my (\$next_small_p, \$sub_iter); my \$gives_next_square = 5; return sub { LOOP: { if (not \$n++) { return 2; # Special case } if (not defined \$upcoming_factors[0]) { if (\$n == \$gives_next_square) { if (not defined (\$sub_iter)) { # Be lazy to avoid an infinite loop... \$sub_iter = build_sieve(); \$sub_iter->(); # Throw away 2 \$next_small_p = \$sub_iter->(); } push @{\$upcoming_factors[\$next_small_p]}, \$next_small_p; \$next_small_p = \$sub_iter->(); my \$next_p2 = \$next_small_p * \$next_small_p; \$gives_next_square = (\$next_p2 + 1)/2; shift @upcoming_factors; redo LOOP; } else { shift @upcoming_factors; return 2*\$n-1; } } else { foreach my \$i (@{\$upcoming_factors[0]}) { push @{\$upcoming_factors[\$i]}, \$i; } shift @upcoming_factors; redo LOOP; } } } } # Produce as many primes as are asked for, or 100. my \$sieve = build_sieve(); print \$sieve->(), "\n" for 1..(shift || 100);
Wow, this looks like some complex stuff. How exactly would this be benneficial in a program? Or is it basically just concept and application?
Closures allow you to organize code differently. The resulting style is as distinct from OO and procedural as they are from each other.

Right now Dominus is writing a book on the subject. You can find some information in this article on iterators, a number of my nodes talk about closures a bit, see Why I like functional programming for one of the better ones. Or you can search for more information.

Of course in this case the technique is just for fun. A somewhat shorter implementation of a sieve of Eratosthenes is:

sub sieve { grep{@_[map\$a*\$_,\$_..@_/(\$a=\$_)]=0if\$_[\$_]>1}@_=0..pop } print join " ", sieve(100);
(Implementation due to tye at (tye)Re3: (Golf): Sieve of Eratosthenes.)
Re: Sieve of Eratosthenes with closures
by Anonymous Monk on Jul 21, 2003 at 12:51 UTC

Of course if you want fast, say 1 million primes in a second you would never use Perl. Perl is great for high level stuff. For raw algorithms.....

#include <stdio.h> #include <malloc.h> #include <time.h> #define TEST(f,x) (*(f+(x)/16)&(1<<(((x)%16L)/2))) #define SET(f,x) *(f+(x)/16)|=1<<(((x)%16L)/2) int main(int argc, char *argv[]) { unsigned char *ptr=NULL; unsigned long max, mom, s=0, e=1; register unsigned long test=1; register unsigned long count=1; time_t begin; max = ( argc>1 ) ? atol(argv[1]) : 0xFFFFFFL; ptr = (unsigned char *) calloc( ((max>>4)+1L), sizeof(char) ); if ( ! ptr ) { printf( "Can't allocate enough memory!\n" ); system("pause"); return 1; } printf( "Searching prime numbers to : %ld\n", max ); begin = time (NULL); while ( (test+=2) < max) if (!TEST(ptr, test) ) { if ( ++count%0xFFL==0 ) { printf ( "%ld prime number\x0d", count ); fflush(stdout); } for ( mom=3L*test; mom<max; mom+=test<<1 ) SET (ptr, mom); } printf( " %ld prime numbers found in %ld secs.\n\n", count, time(N +ULL)-begin); printf( "Show prime numbers ( Enter end < start to exit )" ); while ( s<e ) { printf ("\n\nStart of Area : "); fflush (stdout); scanf ("%ld", &s); printf ("End of Area : "); fflush (stdout); scanf ("%ld", &e); count = s-2; if ( s%2==0 ) count++; while ((count+=2)<e) if (!TEST(ptr,count)) printf ("%ld\t", co +unt); } free(ptr); return 0; } /* Searching prime numbers to : 16777215 1077871 prime numbers found in 1 secs. Show prime numbers ( Enter end < start to exit ) Start of Area : 1 End of Area : 1000 1 3 5 7 11 13 17 19 23 + 29 31 37 41 43 47 53 59 61 67 + 71 73 79 83 89 97 101 103 107 109 + 113 127 131 137 139 149 151 157 163 167 + 173 179 181 191 193 197 199 211 223 227 + 229 233 239 241 251 257 263 269 271 277 + 281 283 293 307 311 313 317 331 337 347 + 349 353 359 367 373 379 383 389 397 401 + 409 419 421 431 433 439 443 449 457 461 + 463 467 479 487 491 499 503 509 521 523 + 541 547 557 563 569 571 577 587 593 599 + 601 607 613 617 619 631 641 643 647 653 + 659 661 673 677 683 691 701 709 719 727 + 733 739 743 751 757 761 769 773 787 797 + 809 811 821 823 827 829 839 853 857 859 + 863 877 881 883 887 907 911 919 929 937 + 941 947 953 967 971 977 983 991 997 Start of Area : */

Create A New User
Node Status?
node history
Node Type: sourcecode [id://276103]
help
Chatterbox?
and a soft breeze sighs...

How do I use this? | Other CB clients
Other Users?
Others meditating upon the Monastery: (3)
As of 2017-12-17 14:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
What programming language do you hate the most?

Results (464 votes). Check out past polls.

Notices?