Skip to content

Instantly share code, notes, and snippets.

@damien
Created April 14, 2015 15:20
Show Gist options
  • Save damien/27d42030eeed11665233 to your computer and use it in GitHub Desktop.
Save damien/27d42030eeed11665233 to your computer and use it in GitHub Desktop.
Lazily generate primes in ruby
# Functional implementation of the Sieve of Eratosthenes
#
# Once initialized, an instance of this class may be used to generate an
# infinite number of primes.
#
# @see https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
# @see http://raganwald.com/2013/02/23/sieve.html
#
# Example:
# @sieve = EratosthenesIncrementalSieve.new
# @sieve.take(1000) # First 1000 primes
class EratosthenesIncrementalSieve
include Enumerable
attr_reader :numbers
attr_reader :composites
def initialize
@candidate_primes = {}
@next_candidate = 2
@candidate_primes[@next_candidate] = @next_candidate
end
# Find and return the next prime number
def succ
loop do
@candidate_primes[@next_candidate] = @next_candidate
new_prime = @next_candidate if sieve!
@next_candidate += 1
return new_prime if new_prime
end
end
# Enumerate over all prime numbers returned by #succ
def each
return dup unless block_given?
loop { yield succ }
end
private
# Mark all candidate primes that are multiples of `number` as non-prime
#
# @return [Boolean] indicates if a prime number has been found given the
# current list of candidate primes
def sieve!
@candidate_primes
.reject { |_, v| v == false } # Ignore preveiously sieved candidates
.each do |n, _|
# Don't factor past the square root of the current
# candidate prime; we've already checked those factors
break if n * n > @next_candidate
@candidate_primes[@next_candidate] = false if @next_candidate % n == 0
end
@candidate_primes[@next_candidate] ? true : false
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment