以下两个函数,用于检查数字是否为素数:
def prime1?(prime_candidate)
return true if [1, 2].include? prime_candidate
range = 2.upto(Math.sqrt(prime_candidate).ceil).to_a
i = 0
while i < range.count
return false if prime_candidate % range[i] == 0
range = range.reject { |j| j % range[i] == 0 }
end
true
end
def prime2?(prime_candidate)
return true if [1, 2].include? prime_candidate
range = 2.upto(Math.sqrt(prime_candidate).ceil).to_a
range.each do |i|
return false if prime_candidate % i == 0
range = range.reject { |j| j % i == 0 }
end
true
end
当使用非常大的素数(5915587277)进行测试时,产生以下的benchamrking结果:
user system total real
prime1: 2.500000 0.010000 2.510000 ( 2.499585)
prime2: 20.700000 0.030000 20.730000 ( 20.717267)
为什么?是因为在第二个函数range
中没有被reject
修改,所以each
正在迭代原始的长range
?
答案 0 :(得分:2)
当你执行range=range.reject {..}
时,你不会修改父范围(你不应该这样做,因为它会弄乱迭代 - 你需要reject!
才能做到这一点)而是构造一个临时数组,只在迭代结束时分配给父范围变量。
each
中的prime2
次迭代在整个原始范围内运行,而不是缩短,在循环结束之前,只在块中存在。
while版本修改了原始数组,因此更快(顺便说一句,你意识到我仍然为零,而range.count
在那条状中改变(减少),而拒绝再次遍历整个数组 - - 即使是可能无法拒绝任何更多非主要内容的开头。
如果您改进代码的逻辑,您将获得更快的结果。这种数组操作代价很高,为此,您甚至不需要数组:
def prime3?(prime_candidate)
return false if prime_candidate==1
return true if prime_candidate==2
range = 2..(Math.sqrt(prime_candidate).ceil)
range.all? {|x| prime_candidate % x !=0 }
end #about 300× times as fast for your example as prime1? on my PC