【发布时间】:2014-05-11 06:33:58
【问题描述】:
我想选择匹配某个条件的数组的前 10 个元素,而不必遍历整个数组。我知道find 让我成为了第一个元素。例如,下面的代码给了我第一个大于 100 的素数:
require 'prime'
puts Prime.find {|p| p > 100 } #=> 101
有没有办法得到大于 100 的前 10 个素数?
【问题讨论】:
标签: ruby
我想选择匹配某个条件的数组的前 10 个元素,而不必遍历整个数组。我知道find 让我成为了第一个元素。例如,下面的代码给了我第一个大于 100 的素数:
require 'prime'
puts Prime.find {|p| p > 100 } #=> 101
有没有办法得到大于 100 的前 10 个素数?
【问题讨论】:
标签: ruby
在 Ruby 2.0+ 中,您可以编写:
require 'prime'
Prime.lazy.select{|p| p > 100 }.take(10).to_a #=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
【讨论】:
你可以像这样手动完成它
def check_for_primes(start_number, desired_size)
result = []
suspect = start_number
while result.size < desired_size do
result << suspect if suspect.prime?
suspect += 1
end
result
end
check_for_primes 100, 10
#=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
使用简单的 ruby 迭代。
适用于所有 ruby 版本。
我们可以添加 @cary-swoveland 的变体,而不是(无可争辩的非 ruby 之类的)while 循环,其中包含相当多的 ruby 优点。
check_enum_text(start_number, desired_size)
(start_number..1.0/0).each_with_object([]) do |n,arr|
if n.prime?
arr << n;
return arr if arr.size == desired_size
end
end
end
***********更新***********
以及一些性能基准
require 'benchmark'
a_gazillion = 10000000000
Benchmark.bm do |x|
x.report("lazy_select") { Prime.lazy.select{|p| p > (a_gazillion / 1000) }.take(10).to_a }
x.report("prime_each") { arr = []; Prime.each{|p| arr << p if p > a_gazillion / 1000; break if arr.count == 10 } }
x.report("while_loop") { check_for_primes a_gazillion, 10 }
x.report("enum_text") { check_enum_text a_gazillion, 10 }
end
user system total real
lazy_select 75.360000 0.240000 75.600000 (84.259781)
prime_each 6.100000 0.040000 6.140000 ( 6.730646)
while_loop 0.620000 0.000000 0.620000 ( 0.655504)
enum_text 0.610000 0.000000 0.610000 ( 0.770726)
从我们看到的两个最新解决方案是性能最好的解决方案。通过一些额外的基准测试(通过调整desired_size),我无法得出哪个更好的结论
def bench(start, length)
Benchmark.bm do |x|
x.report("enum_text") { check_enum_text start, length }
x.report("while_loop") { check_for_primes start, length}
end
end
bench a_gazillion, 100
user system total real
enum_text 6.350000 0.000000 6.350000 ( 6.974557)
while_loop 6.530000 0.000000 6.530000 ( 7.330884)
bench a_gazillion, 500
user system total real
enum_text 31.880000 0.110000 31.990000 ( 36.723209)
while_loop 32.850000 0.060000 32.910000 ( 38.569744)
性能相似(实际上 @cary-swoveland 的解决方案性能稍好一些),所以我将不得不采用该解决方案,因为它更像红宝石!
【讨论】:
> 100。它是another boolean check,其逻辑远不止于此。具体:class Integer def is_truncable? truncable = true str, str2= self.to_s, self.to_s if self == 2 || self == 3 || self == 5 || self == 7 then return false end while str.size > 0 if !Prime.prime?(str.to_i) || !Prime.prime?(str2.to_i) then truncable = false end str = str.chop str2 = str2[1..-1] end truncable end end别以为你的方法可以用在这里
You could spice it up a bit by replacing the body of the method with (start_number..1.0/0).each_with_object([]) do |n,arr| if n.prime?; arr << n; return arr if arr.size == desired_size; end; end; end.
enum_next。
arr = []
Prime.each{|p| arr << p if p > 100; break if arr.count == 10 }
puts arr
【讨论】:
一些答案的缺点是它们会多次枚举低于阈值的素数。这是避免这种情况的一种方法:
require 'prime'
def check_for_primes(start_number, desired_size)
return [] if desired_size.zero?
enum = Prime.each
[enum.find { |n| n >= start_number }] + enum.first(desired_size-1)
end
check_for_primes(100, 10)
#=> [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
或者,可以这样写:
def check_for_primes(start_number, desired_size)
return [] if desired_size.zero?
enum = Prime.each
(0..1.0/0).each_with_object([]) do |_,arr|
n = enum.next
if n >= start_number
arr << n
return arr if arr.size == desired_size
end
end
end
【讨论】:
each_with_object([]) do |_,arr|,因为您覆盖 n 而根本不使用它。
一种简单的迭代方式:
require 'prime'
initial = 100
list = []
10.times do |x|
initial = Prime.find {|p| p > initial}
list << initial
end
puts list
【讨论】:
您可以使用Prime.each(n) 来设置上限:
Prime.each(1000).drop_while { |p| p <= 100 }.take(10)
# => [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
或者,您可以计算有许多质数低于 100,然后取该数量 + 10:
Prime.take(Prime.take_while { |p| p <= 100 }.count + 10)[-10..-1]
# => [101, 103, 107, 109, 113, 127, 131, 137, 139, 149]
【讨论】: