mecrazycoder has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, Is there any efficient way to find the given prime N is prime or not. N as its range from 1<N<1000000. Output must be generate within a timespan of 1sec. Can anyone tel me the most efficient way of finding the prime number within this time slot.

Replies are listed 'Best First'.
Re: Find prime number between 1 to 1000000
by Limbic~Region (Chancellor) on Jul 29, 2009 at 14:18 UTC
    mecrazycoder,
    Yes, determining if a number is prime can be done in polynomial time. If you want to know all the prime numbers up to a certain value, the best way is probably the sieve of Eratosthenes (or a variation). There are also websites that list all prime numbers up to certain values that you could just create a lookup table for such a small number (1 million isn't very big). Given how limited your post is, it is hard to tell what you really want. Math::Pari has functions for determining primeness.

    Cheers - L~R

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Find prime number between 1 to 1000000
by JavaFan (Canon) on Jul 29, 2009 at 14:23 UTC
    A number is prime if it doesn't have any divisors other than 1 and itself. Note that if such a divisor exists, one exists which is at most the square of the number. Given that N is less than 1000000, you only have to check less than 1000 numbers. And after testing 2, you can exclude all the other even numbers. Which leaves with at most 500 numbers to check.

    Think you could write a program that checks the outcome of at most 500 divisions in less than a second?

      JavaFan,
      And after testing 2, you can exclude all the other even numbers. Which leaves with at most 500 numbers to check.

      After testing 3 and 5, you can use the 2/4 trick (see Re^3: a close prime number) to skip all multiples of 3 leaving you with about 333 numbers to check. I agree that for a single number under 1 million a pure perl function can be created that does the job without resorting to anything more advanced. I have yet to find a simple (by my own subjective definition) method though that does better than testing 1/3 sqrt (N) numbers to determine if a number is prime. I guess it could be argued that using Math::Pari's primality test function is simple but I meant my own implementation.

      Cheers - L~R

        For Project Euler problems I already needed an iterator that iterates over primes. (Under the hood it uses the sieve of Eratosthenes to calculate blocks of primes and then returns them.)

        If you already have that iterator, dividing by the primes up to and including the square root is quite easy.

        Well, you could create a simple sieve that finds all primes between less than 500 (or just use brute force - square root of 500 is less than 23, and you'll know the primes up to 23 I presume), and use them.

        But that sounds like too much work to me. It's only worthwhile to use really efficient prime searching algorithms if the numbers involved are much bigger than what a Perl integer can hold.

Re: Find prime number between 1 to 1000000
by tilly (Archbishop) on Jul 31, 2009 at 14:55 UTC
    Back when I was doing project Euler problems I created a library for myself that took care of a number of common needs. They aren't particularly fast, but they were good enough for that.

    In it I had 2 prime testing algorithms. The first, is_prime, generates all of the primes up to a given one then does a binary search. On my laptop it generates all of the primes up to a million in under half a second, and after that I can do about 70,0000 primality tests/second on numbers 1..1_000_000. The other one, is_prime2, generates the primes up to the square root of a given one and does trial division. It is avoids the up front load but can only do about 25,000 primality tests per second on numbers 1..1,000,000. My laptop may be faster or slower than your computer, but either should be more than fast enough for your purposes.

    I use integer so neither primality test will work past 2 billion. If you want to test primality for larger numbers, I highly recommend Math::Pari which pushes it down to a highly optimized C library. I sometimes find it amusing to watch it factor random 100 digit numbers in the blink of an eye.

    Anyways here is my Perl library. Be warned it has a lot of irrelevant stuff in it and was only meant for personal use. (Hence no documentation.) However it has a lot of goodies for anyone trying to do project Euler problems.

    package library; use strict; our @EXPORT_OK = qw( factor factors generate_stream generate_memoryless_stream is_prime i +s_prime2 prime_iterator ith_prime gcd n_pow_m_mod_k minimal_divisible_repunit pythagorean_triple_iterator ); use Exporter qw(import); sub pythagorean_triple_iterator { my $comp = shift || sub { my ($a, $b) = @_; return $a->[0] <=> $b->[0] || $a->[1] <=> $b->[1]; }; my $heap = Heap->new($comp); my $initial_iterator = primitive_pythagorean_triple_iterator(); $heap->append( [$initial_iterator->(), 1, $initial_iterator] ); my $max_k = 1; return sub { my $next = $heap->top(); my ($x, $y, $z, $k, $it) = @$next; $heap->swap_top( [(map $k*$_, $it->()), $k, $it] ); if ($max_k == $k) { $max_k++; # print "Appending $max_k\n"; my $iter = primitive_pythagorean_triple_iterator(); $iter->(); $heap->append( [3*$max_k, 4*$max_k, 5*$max_k, $max_k, $iter] ); } return $x, $y, $z; } } sub primitive_pythagorean_triple_iterator { my $comp = shift || sub { my ($a, $b) = @_; return $a->[0] <=> $b->[0] or $a->[1] <=> $b->[1]; }; my $heap = Heap->new($comp); $heap->append( [3, 4, 5, 1, 2] ); my $generate_triple = sub { my ($m, $n) = @_; # We only want primitives. return() unless 1 == $m%2 + $n%2; return() unless 1 == gcd($m, $n); my $x = 2*$m*$n; my $y = $m*$m - $n*$n; $y = - $y if $y < 0; my $z = $m*$m + $n*$n; # print "Generated ($x, $y, $z) from ($m, $n)\n"; if ($x < $y) { return [$x, $y, $z, $m, $n]; } else { return [$y, $x, $z, $m, $n]; } }; return sub { my $next = $heap->extract(); my ($m, $n) = @$next[3, 4]; if (1 == $m) { $heap->append( map $generate_triple->($_, $n+1), 1..$n ); $heap->append( map $generate_triple->($_, $n+2), 1..($n+1) ); } return @$next[0, 1, 2]; }; } sub minimal_divisible_repunit { my $i = shift; return 0 if 0 == $i%2 or 0 == $i%5; $i *= 9 if 0 == $i%3; my %factored = factor($i); my $euler = $i; for my $p (keys %factored) { $euler /= $p; $euler *= $p-1; } my %factor_euler = factor($euler); for my $p (keys %factor_euler) { while (0 == $euler%$p and 1 == n_pow_m_mod_k(10, $euler/$p, $i)) { $euler /= $p; } } return $euler; } sub gcd { my ($n, $m) = @_; while ($m) { ($n, $m) = ($m, $n%$m); } return $n; } my @primes = (2, 3, 5, 7, 11); # None have been tested sub more_primes { # This adds to the list of primes until it reaches $max # or the square of the largest current prime (assumed odd) my $base = shift || $primes[-1]+1; my $max = shift || $base + 10_000; my $square = $primes[-1] * $primes[-1]; $max = $square if $square < $max; # Determine what to find prime +s to $base++ unless $base % 2; # Make the base odd $max-- if $max %2; # Make the max odd $max = ($max - $base)/2; # Make $max into a count of od +ds return if $max < 0; # Sanity check my $more = ""; # Initialize vector of 0's for + the # odd numbers in our range shift @primes; # Remove 2 foreach my $p (@primes) { my $start; if ($base < $p * $p) { $start = ($p * $p - $base)/2; # Start at the square if ($max < $start) { # Rest of primes don't matter! last; } } else { # Start at first odd it divide +s $start = $base % $p; # Find remainder $start = $p - $start if $start; # Distance to first thing it d +ivides $start += $p if $start %2; # Distance to first odd it div +ides $start = $start/2; # Reindex for counting over od +d! } for (my $i = $start; $i <= $max; $i += $p) { vec($more, $i, 1) = 1; } } unshift @primes, 2; # Replace 2 # Read off list of primes push @primes, map {$_ + $_ + $base} grep {vec($more, $_, 1) == 0} 0. +.$max; } sub ith_prime { my $i = shift; while ($i > $#primes) { more_primes(); } return $primes[$i]; } sub prime_iterator { my $i = -1; return sub { $i++; if ($i > $#primes) { more_primes(); } return $primes[$i]; }; } # This generates the primes up to the one of interest. sub is_prime { my $n = shift; return if $n < 2; return 1 if $n == 2; while ($n >= $primes[-1]) { more_primes(); } # Now we only need to find our prime. my $i = 0; my $j = $#primes; while ($i+1 < $j) { my $k = int($i + $j)/2; if ($primes[$k] == $n) { # We found it! return 1; } elsif ($primes[$k] < $n) { $i = $k; } else { $j = $k; } } # We have it sandwiched between two consecutive primes return 0; } # This only goes to the square root of the one of interest. sub is_prime2 { my $n = shift; my %factor = factor($n); return exists $factor{$n}; } sub factor { my $n = shift; my %divides; my $i = 0; my $p = $primes[$i]; my $limit = sqrt($n); until ($limit < $p) { my $k = $n/$p; while ($k == int($k)) { $n = $k; $limit = sqrt($n); $divides{$p}++; $k = $n/$p; } $i++; if ($i > $#primes) { more_primes(); } $p = $primes[$i]; } if (1 < $n) { $divides{$n}++; } return %divides; } sub factors { my $n = shift; my %factor = factor($n); my @list = 1; for my $p (keys %factor) { my $pow = 1; my @temp_list; for my $c (0..$factor{$p}) { push @temp_list, map $pow*$_, @list; $pow *= $p; } @list = @temp_list; } return sort {$a <=> $b} @list; } sub generate_memoryless_stream { my $promise = shift; my @seen; my $is_finished; return sub { if (@seen) { return shift @seen; } elsif ($is_finished) { return; } else { @seen = $promise->(); if (@seen) { return shift @seen; } else { $is_finished = 1; return; } } }; } sub generate_stream { my $promise = shift; my @seen; my $is_finished; return sub { my $n = shift; while ($n > $#seen) { if ($is_finished) { return; } my @next = $promise->(); if (not @next) { $is_finished = 1; return; } else { push @seen, @next; } } return $seen[$n]; }; } sub n_pow_m_mod_k { my ($n, $m, $k) = @_; my $answer = 1; while ($m) { if ($m%2) { $answer = ($answer * $n) % $k; } $m = int($m/2); $n = ($n * $n) % $k; } return $answer; } package Heap; sub new { my $class = shift; my $comp = shift || sub {#print "Comparing @_\n"; $_[0] cmp $_[1]}; bless { comp => $comp, data => [], }, $class; } sub top { my $self = shift; return $self->{data}[0]; } sub append { my $self = shift; my $data = $self->{data}; my $comp = $self->{comp}; for my $e (@_) { push @$data, $e; my $i = $#$data; my $j = int(($i-1)/2); #print "Adding $e, ($i, $j)\n"; while ($i and $comp->(@$data[$i, $j]) < 0) { #print "Swapping $i, $j\n"; @$data[$i, $j] = @$data[$j, $i]; $i = $j; $j = int(($i-1)/2); } #print "Comp: ", $comp->(@$data->[$i, $j]), $/; } } sub swap_top { my $self = shift; my $data = $self->{data}; my $comp = $self->{comp}; my $i = 0; $data->[0] = shift; while (1) { last if 2*$i + 1 > $#$data; if ($#$data == 2*$i + 1) { # print "$i: (@$data) - end is 2*$i+2\n"; if ($comp->(@$data[$i, 2*$i+1]) > 0) { # print "$i: (@$data) - $i > 2*$i+1\n"; @$data[$i, 2*$i+1] = @$data[2*$i+1, $i]; } last; } if ($comp->(@$data[2*$i+1, 2*$i+2]) < 0) { # print "$i: (@$data) - 2*$i+1 < 2*$i+2\n"; if ($comp->(@$data[$i, 2*$i+1]) > 0) { # print "$i: (@$data) - $i > 2*$i+1\n"; @$data[$i, 2*$i+1] = @$data[2*$i+1, $i]; $i = 2*$i+1; next; } } else { # print "$i: (@$data) - 2*$i+1 >= 2*$i+2\n"; if ($comp->(@$data[$i, 2*$i+2]) > 0) { # print "$i: (@$data) - $i > 2*$i+2\n"; @$data[$i, 2*$i+2] = @$data[2*$i+2, $i]; $i = 2*$i+2; next; } } last; }; return $data->[0]; } sub extract { my $self = shift; my $data = $self->{data}; # print "Extract: @$data\n"; my $top = shift @$data; if (@$data) { unshift @$data, pop @$data; $self->swap_top($data->[0]); } return $top; } 1;
Re: Find prime number between 1 to 1000000
by jettero (Monsignor) on Jul 29, 2009 at 14:14 UTC
Re: Find prime number between 1 to 1000000
by ELISHEVA (Prior) on Jul 29, 2009 at 14:45 UTC

    There was a Perl Monks node a while back that discussed several different methods of calculating large numbers of primes. The one that seemed to be most efficient (based on my own trials) was an implementation of the Sieve of Eratosthenes using vec attributed to merlyn. See Re: To Findout Prime number.

    Best, beth

      That's a rather inefficent sieve. Inefficient in the sense that it wastes a lot of memory - it uses a single bit for every number, including all the even ones. That's half the memory usage for numbers you know cannot be prime. I've done sieves based on vec as well, and I've found that the limit on how far you can feasibly go is the memory consumption. Note that any prime number (except the first few) are of the form 30 * n + k with k one of 1, 7, 11, 13, 17, 19, 23, 29. (All other numbers are either divisible by 2, 3 or 5). Which means you need only 8 bits for every 30 numbers. Of course, more efficient packings exist as well.

        That was also my first reaction, but merlyn's algorithm is lightening fast. As is often the case, one trades speed for memory. On modern computers the amount of memory needed to find primes between 1 and 1 million is rarely an issue. The OP is satisfied with the primes < 1 million. If you are looking for very, very large primes (10's of digits) I'm sure other slower algorithms might be better.

        Best, beth

Re: Find prime number between 1 to 1000000
by kyle (Abbot) on Jul 29, 2009 at 14:47 UTC
Re: Find prime number between 1 to 1000000
by BrowserUk (Patriarch) on Jul 29, 2009 at 17:29 UTC

    Don't calculate when you can lookup! This will test almost a million numbers per second:

    c:\test>perl Math\Primes\First_1e6.pm Found 122571 primes from 1000000 tries in 1.145 seconds (873362/s)
Re: Find prime number between 1 to 1000000
by JavaFan (Canon) on Jul 29, 2009 at 15:17 UTC
    The simplest way is to download the first million primes, unzip the file and just query the result. The Primes Pages has many information about primes, and the first 50 million are available as files to download.
Re: Find prime number between 1 to 1000000
by si_lence (Deacon) on Jul 29, 2009 at 14:18 UTC
    If you need to check a lot of numbers it might be efficient to precalculate all primes and load them in memory. Then check against this list.
    cheers, si_lence