lib/rubysl/prime/prime.rb in rubysl-prime-2.0.0 vs lib/rubysl/prime/prime.rb in rubysl-prime-2.0.1

- old
+ new

@@ -69,24 +69,24 @@ # # == Generators # # A "generator" provides an implementation of enumerating pseudo-prime # numbers and it remembers the position of enumeration and upper bound. -# Furthermore, it is a external iterator of prime enumeration which is -# compatible to an Enumerator. +# Furthermore, it is an external iterator of prime enumeration which is +# compatible with an Enumerator. # # +Prime+::+PseudoPrimeGenerator+ is the base class for generators. # There are few implementations of generator. # # [+Prime+::+EratosthenesGenerator+] -# Uses eratosthenes's sieve. +# Uses eratosthenes' sieve. # [+Prime+::+TrialDivisionGenerator+] # Uses the trial division method. # [+Prime+::+Generator23+] -# Generates all positive integers which is not divided by 2 nor 3. +# Generates all positive integers which are not divisible by either 2 or 3. # This sequence is very bad as a pseudo-prime sequence. But this -# is faster and uses much less memory than other generators. So, +# is faster and uses much less memory than the other generators. So, # it is suitable for factorizing an integer which is not large but # has many prime factors. e.g. for Prime#prime? . class Prime include Enumerable @@ -131,17 +131,17 @@ # # Calls +block+ once for each prime number, passing the prime as # a parameter. # # +ubound+:: - # Upper bound of prime numbers. The iterator stops after + # Upper bound of prime numbers. The iterator stops after it # yields all prime numbers p <= +ubound+. # # == Note # - # +Prime+.+new+ returns a object extended by +Prime+::+OldCompatibility+ - # in order to compatibility to Ruby 1.8, and +Prime+#each is overwritten + # +Prime+.+new+ returns an object extended by +Prime+::+OldCompatibility+ + # in order to be compatible with Ruby 1.8, and +Prime+#each is overwritten # by +Prime+::+OldCompatibility+#+each+. # # +Prime+.+new+ is now obsolete. Use +Prime+.+instance+.+each+ or simply # +Prime+.+each+. def each(ubound = nil, generator = EratosthenesGenerator.new, &block) @@ -189,13 +189,13 @@ # # == Parameters # +value+:: An arbitrary integer. # +generator+:: Optional. A pseudo-prime generator. # +generator+.succ must return the next - # pseudo-prime number in the ascendent + # pseudo-prime number in the ascending # order. It must generate all prime numbers, - # but may generate non prime numbers. + # but may also generate non prime numbers too. # # === Exceptions # +ZeroDivisionError+:: when +value+ is zero. # # == Example @@ -207,11 +207,11 @@ # # [[p_1, e_1], [p_2, e_2], ...., [p_n, e_n]]. # # Prime.prime_division(12) #=> [[2,2], [3,1]] # - def prime_division(value, generator= Prime::Generator23.new) + def prime_division(value, generator = Prime::Generator23.new) raise ZeroDivisionError if value == 0 if value < 0 value = -value pv = [[-1, 1]] else @@ -270,11 +270,11 @@ # See +Enumerator+#rewind. def rewind raise NotImplementedError, "need to define `rewind'" end - # Iterates the given block for each prime numbers. + # Iterates the given block for each prime number. def each(&block) return self.dup unless block if @ubound last_value = nil loop do @@ -304,16 +304,17 @@ # An implementation of +PseudoPrimeGenerator+. # # Uses +EratosthenesSieve+. class EratosthenesGenerator < PseudoPrimeGenerator def initialize - @last_prime = nil + @last_prime_index = -1 super end def succ - @last_prime = @last_prime ? EratosthenesSieve.instance.next_to(@last_prime) : 2 + @last_prime_index += 1 + EratosthenesSieve.instance.get_nth_prime(@last_prime_index) end def rewind initialize end alias next succ @@ -334,15 +335,15 @@ initialize end alias next succ end - # Generates all integer which are greater than 2 and - # are not divided by 2 nor 3. + # Generates all integers which are greater than 2 and + # are not divisible by either 2 or 3. # # This is a pseudo-prime generator, suitable on - # checking primality of a integer by brute force + # checking primality of an integer by brute force # method. class Generator23<PseudoPrimeGenerator def initialize @prime = 1 @step = nil @@ -416,75 +417,55 @@ end return @primes[index] end end - # Internal use. An implementation of eratosthenes's sieve + # Internal use. An implementation of eratosthenes' sieve class EratosthenesSieve include Singleton - BITS_PER_ENTRY = 16 # each entry is a set of 16-bits in a Fixnum - NUMS_PER_ENTRY = BITS_PER_ENTRY * 2 # twiced because even numbers are omitted - ENTRIES_PER_TABLE = 8 - NUMS_PER_TABLE = NUMS_PER_ENTRY * ENTRIES_PER_TABLE - FILLED_ENTRY = (1 << NUMS_PER_ENTRY) - 1 - - def initialize # :nodoc: - # bitmap for odd prime numbers less than 256. - # For an arbitrary odd number n, @tables[i][j][k] is - # * 1 if n is prime, - # * 0 if n is composite, - # where i,j,k = indices(n) - @tables = [[0xcb6e, 0x64b4, 0x129a, 0x816d, 0x4c32, 0x864a, 0x820d, 0x2196].freeze] + def initialize + @primes = [2, 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] + # @max_checked must be an even number + @max_checked = @primes.last + 1 end - # returns the least odd prime number which is greater than +n+. - def next_to(n) - n = (n-1).div(2)*2+3 # the next odd number to given n - table_index, integer_index, bit_index = indices(n) - loop do - extend_table until @tables.length > table_index - for j in integer_index...ENTRIES_PER_TABLE - if !@tables[table_index][j].zero? - for k in bit_index...BITS_PER_ENTRY - return NUMS_PER_TABLE*table_index + NUMS_PER_ENTRY*j + 2*k+1 if !@tables[table_index][j][k].zero? - end - end - bit_index = 0 - end - table_index += 1; integer_index = 0 - end + def get_nth_prime(n) + compute_primes while @primes.size <= n + @primes[n] end private - # for an odd number +n+, returns (i, j, k) such that @tables[i][j][k] represents primarity of the number - def indices(n) - # binary digits of n: |0|1|2|3|4|5|6|7|8|9|10|11|.... - # indices: |-| k | j | i - # because of NUMS_PER_ENTRY, NUMS_PER_TABLE + def compute_primes + # max_segment_size must be an even number + max_segment_size = 1e6.to_i + max_cached_prime = @primes.last + # do not double count primes if #compute_primes is interrupted + # by Timeout.timeout + @max_checked = max_cached_prime + 1 if max_cached_prime > @max_checked - k = (n & 0b00011111) >> 1 - j = (n & 0b11100000) >> 5 - i = n >> 8 - return i, j, k - end + segment_min = @max_checked + segment_max = [segment_min + max_segment_size, max_cached_prime * 2].min + root = Integer(Math.sqrt(segment_max).floor) - def extend_table - lbound = NUMS_PER_TABLE * @tables.length - ubound = lbound + NUMS_PER_TABLE - new_table = [FILLED_ENTRY] * ENTRIES_PER_TABLE # which represents primarity in lbound...ubound - (3..Integer(Math.sqrt(ubound))).step(2) do |p| - i, j, k = indices(p) - next if @tables[i][j][k].zero? + sieving_primes = @primes[1 .. -1].take_while { |prime| prime <= root } + offsets = Array.new(sieving_primes.size) do |i| + (-(segment_min + 1 + sieving_primes[i]) / 2) % sieving_primes[i] + end - start = (lbound.div(p)+1)*p # least multiple of p which is >= lbound - start += p if start.even? - (start...ubound).step(2*p) do |n| - _, j, k = indices(n) - new_table[j] &= FILLED_ENTRY^(1<<k) + segment = ((segment_min + 1) .. segment_max).step(2).to_a + sieving_primes.each_with_index do |prime, index| + composite_index = offsets[index] + while composite_index < segment.size do + segment[composite_index] = nil + composite_index += prime end end - @tables << new_table.freeze + + segment.each do |prime| + @primes.push prime unless prime.nil? + end + @max_checked = segment_max end end # Provides a +Prime+ object with compatibility to Ruby 1.8 when instantiated via +Prime+.+new+. module OldCompatibility