从数组中找到与条件匹配的前n个元素

时间:2014-05-10 12:05:43

标签: ruby

我想选择匹配某个条件的数组的前10个元素,而不必遍历整个数组。我知道find让我成为第一个元素。例如,下面的代码给出了第一个大于100的素数:

require 'prime'

puts Prime.find {|p| p > 100 } #=> 101

有没有办法让前10个大于100的素数?

6 个答案:

答案 0 :(得分:7)

在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]

答案 1 :(得分:3)

您可以手动执行此操作,例如

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版本。

而不是(无可争议的非ruby之类)while循环,我们可以添加 @ cary-swoveland 的变体,其中有相当一些红宝石的好处。

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

<强> *********** UPDATE ***********

和一些性能基准

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 的解决方案表现稍好一些),所以我必须使用该解决方案,因为它更像红宝石!

答案 2 :(得分:2)

arr = []
Prime.each{|p| arr << p if p > 100; break if arr.count == 10 }
puts arr

答案 3 :(得分:2)

一些答案​​的缺点是它们多次枚举低于阈值的素数。这是避免这种情况的一种方法:

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

答案 4 :(得分:1)

一种简单的迭代方式:

require 'prime'

initial = 100
list = []

10.times do |x|
    initial = Prime.find {|p| p > initial}
    list << initial
end

puts list

答案 5 :(得分:1)

您可以使用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]