E
erlercw
In mathn.rb (ruby 1.8.1), Integer#gcd2 and the Prime class are
unacceptably slow.
(The Integer#gcd2 replacement below is also faster than both Integer#gcd and
Integer#gcd2 in rational.rb, at least on this machine, and might be a nice
replacement for Integer#gcd there.)
Try the following test with the standard mathn and then with the method and class
replacements below it :
require 'mathn'
puts "Finding the GCDs of 10,000 pairs starting at #{Time.now}...."
10000.times { rand(10000).gcd2(rand(10000)) }
puts "Finished with that, now finding the first 10,000 primes at #{Time.now}...."
primes = Prime.new
10000.times { primes.succ }
puts "Finished with that at #{Time.now}...."
Here are replacements for Integer#gcd2 and the Prime class which are effectively
identical (with Prime.cache and Prime#primes_so_far added), but are thousands of
times faster for any kind of heavy use :
# Use a Euclidean GCD algorithm
def gcd2(other)
min, max = [self.abs, other.abs].sort
min, max = max % min, min while min > 0
max
end
class Prime
include Enumerable
# These are included as class variables to cache them for later uses. If memory
# usage is a problem, they can be put in Prime#initialize as instance variables.
# There must be no primes between @@primes[-1] and @@next_to_check.
@@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]
# @@next_to_check % 6 must be 1.
@@next_to_check = 103 # @@primes[-1] - @@primes[-1] % 6 + 7
@@ulticheck_index = 3 # @@primes.index(@@primes.reverse.find {|n|
# n < Math.sqrt(@@next_to_check) })
@@ulticheck_next_squared = 121 # @@primes[@@ulticheck_index + 1] ** 2
class << self
# Return the prime cache.
def cache
return @@primes
end
alias primes cache
alias primes_so_far cache
end
def initialize
@index = -1
end
# Return primes given by this instance so far.
def primes
return @@primes[0, @index + 1]
end
alias primes_so_far primes
def succ
@index += 1
while @index >= @@primes.length
# Only check for prime factors up to the square root of the potential primes,
# but without the performance hit of an actual square root calculation.
if @@next_to_check + 4 > @@ulticheck_next_squared
@@ulticheck_index += 1
@@ulticheck_next_squared = @@primes.at(@@ulticheck_index + 1) ** 2
end
# Only check numbers congruent to one and five, modulo six. All others
# are divisible by two or three. This also allows us to skip checking against
# two and three.
@@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {
|prime| @@next_to_check % prime == 0 }.nil?
@@next_to_check += 4
@@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {
|prime| @@next_to_check % prime == 0 }.nil?
@@next_to_check += 2
end
return @@primes[@index]
end
alias next succ
def each
loop do
yield succ
end
end
end
There are other methods from number theory which are a lot faster than mine, but
these are an easy and quick change with a large speed benefit. Plus, I don't know
how to do those
unacceptably slow.
(The Integer#gcd2 replacement below is also faster than both Integer#gcd and
Integer#gcd2 in rational.rb, at least on this machine, and might be a nice
replacement for Integer#gcd there.)
Try the following test with the standard mathn and then with the method and class
replacements below it :
require 'mathn'
puts "Finding the GCDs of 10,000 pairs starting at #{Time.now}...."
10000.times { rand(10000).gcd2(rand(10000)) }
puts "Finished with that, now finding the first 10,000 primes at #{Time.now}...."
primes = Prime.new
10000.times { primes.succ }
puts "Finished with that at #{Time.now}...."
Here are replacements for Integer#gcd2 and the Prime class which are effectively
identical (with Prime.cache and Prime#primes_so_far added), but are thousands of
times faster for any kind of heavy use :
# Use a Euclidean GCD algorithm
def gcd2(other)
min, max = [self.abs, other.abs].sort
min, max = max % min, min while min > 0
max
end
class Prime
include Enumerable
# These are included as class variables to cache them for later uses. If memory
# usage is a problem, they can be put in Prime#initialize as instance variables.
# There must be no primes between @@primes[-1] and @@next_to_check.
@@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]
# @@next_to_check % 6 must be 1.
@@next_to_check = 103 # @@primes[-1] - @@primes[-1] % 6 + 7
@@ulticheck_index = 3 # @@primes.index(@@primes.reverse.find {|n|
# n < Math.sqrt(@@next_to_check) })
@@ulticheck_next_squared = 121 # @@primes[@@ulticheck_index + 1] ** 2
class << self
# Return the prime cache.
def cache
return @@primes
end
alias primes cache
alias primes_so_far cache
end
def initialize
@index = -1
end
# Return primes given by this instance so far.
def primes
return @@primes[0, @index + 1]
end
alias primes_so_far primes
def succ
@index += 1
while @index >= @@primes.length
# Only check for prime factors up to the square root of the potential primes,
# but without the performance hit of an actual square root calculation.
if @@next_to_check + 4 > @@ulticheck_next_squared
@@ulticheck_index += 1
@@ulticheck_next_squared = @@primes.at(@@ulticheck_index + 1) ** 2
end
# Only check numbers congruent to one and five, modulo six. All others
# are divisible by two or three. This also allows us to skip checking against
# two and three.
@@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {
|prime| @@next_to_check % prime == 0 }.nil?
@@next_to_check += 4
@@primes.push @@next_to_check if @@primes[2..@@ulticheck_index].find {
|prime| @@next_to_check % prime == 0 }.nil?
@@next_to_check += 2
end
return @@primes[@index]
end
alias next succ
def each
loop do
yield succ
end
end
end
There are other methods from number theory which are a lot faster than mine, but
these are an easy and quick change with a large speed benefit. Plus, I don't know
how to do those