(To first answer the question: yes, you are doing something wrong. As BLUEPIXY mentions, you need to put require 'prime'
somewhere above the line that calls Prime.prime?
. Typically on line 1.)
Now, a lot of answers have been given that don't use Prime.prime?
, and I thought it might be interesting to benchmark some of them, along with a possible improvement of my own that I had in mind.
###TL;DR
I benchmarked several solutions, including a couple of my own; using a while
loop and skipping even numbers performs best.
Methods tested
Here are the methods I used from the answers:
require 'prime'
def prime1?(num)
return if num <= 1
(2..Math.sqrt(num)).none? { |i| (num % i).zero? }
end
def prime2?(num)
return false if num <= 1
Math.sqrt(num).to_i.downto(2) {|i| return false if num % i == 0}
true
end
def prime3?(num)
Prime.prime?(num)
end
def prime4?(num)
('1' * num) !~ /^1?$|^(11+?)\1+$/
end
prime1?
is AndreiMotinga's updated version. prime2?
is his original version (with the superfluous each
method removed). prime3?
is Reboot's, using prime
library. prime4?
is Saurabh's regex version (minus the Fixnum
monkey-patch).
A couple more methods to test
The improvement I had in mind was to leverage the fact that even numbers can't be prime, and leave them out of the iteration loop. So, this method uses the #step
method to iterate over only odd numbers, starting with 3:
def prime5?(num)
return true if num == 2
return false if num <= 1 || num.even?
3.step(Math.sqrt(num).floor, 2) { |i| return false if (num % i).zero? }
true
end
I thought as well that it might be interesting to see how a "primitive" implementation of the same algorithm, using a while
loop, might perform. So, here's one:
def prime6?(num)
return true if num == 2
return false if num <= 1 || num.even?
i = 3
top = Math.sqrt(num).floor
loop do
return false if (num % i).zero?
i += 2
break if i > top
end
true
end
Benchmarks
I did a simple benchmark on each of these, timing a call to each method with the prime number 67,280,421,310,721. For example:
start = Time.now
prime1? 67280421310721
puts "prime1? #{Time.now - start}"
start = Time.now
prime2? 67280421310721
puts "prime2? #{Time.now - start}"
# etc.
As I suspected I would have to do, I canceled prime4?
after about 60 seconds. Presumably, it takes quite a bit longer than 60 seconds to assign north of 6.7 trillion '1'
's to memory, and then apply a regex filter to the result — assuming it's possible on a given machine to allocate the necessary memory in the first place. (On mine, it would seem that there isn't: I went into irb
, put in '1' * 67280421310721
, made and ate dinner, came back to the computer, and found Killed: 9
as the response. That looks like a SignalException
raised when the process got killed.)
The other results are:
prime1? 3.085434
prime2? 1.149405
prime3? 1.236517
prime5? 0.748564
prime6? 0.377235
Some (tentative) conclusions
I suppose that isn't really surprising that the primitive solution with the while loop is fastest, since it's probably closer than the others to what's going on under the hood. It is a bit surprising that it's three times faster than Prime.prime?
, though. (After looking at the source code in the doc it is less so. There are lots of bells and whistles in the Prime
object.)
AndreiMotinga's updated version is nearly three times as slow as his original, which suggests that the #none?
method isn't much of a performer, at least in this context.
Finally, the regex version might be cool, but it certainly doesn't appear to have much practical value, and using it in a monkey-patch of a core class looks like something to avoid entirely.