【问题标题】:Find first n elements from array that match a condition从数组中查找匹配条件的前 n 个元素
【发布时间】:2014-05-11 06:33:58
【问题描述】:

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

require 'prime'

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

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

【问题讨论】:

    标签: ruby


    【解决方案1】:

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

    【讨论】:

    【解决方案2】:

    你可以像这样手动完成它

    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 的解决方案性能稍好一些),所以我将不得不采用该解决方案,因为它更像红宝石!

    【讨论】:

    • 不错。感谢您的基准!点是我正在检查的条件不是&gt; 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 &gt; 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 &lt;&lt; n; return arr if arr.size == desired_size; end; end; end.
    • 如果您能将我建议的方法添加到您的基准测试中,我将不胜感激。或许可以称之为enum_next
    • @CarySwoveland 我做了基准测试,检查结果!顺便说一句,那里的代码不错!
    【解决方案3】:
    arr = []
    Prime.each{|p| arr << p if p > 100; break if arr.count == 10 }
    puts arr
    

    【讨论】:

    • 感谢也有效。可能比惰性实现更快。
    • 这效率不高,因为您还要检查所有低于下限的数字。它不会执行更高的下限。
    【解决方案4】:

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

    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 而根本不使用它。
    • 好点,xlembouras。解决了这个问题。对两者使用相同的变量也可能令人困惑。并感谢基准。看起来风格胜过实质。
    【解决方案5】:

    一种简单的迭代方式:

    require 'prime'
    
    initial = 100
    list = []
    
    10.times do |x|
        initial = Prime.find {|p| p > initial}
        list << initial
    end
    
    puts list
    

    【讨论】:

      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-05-23
        • 1970-01-01
        • 2017-03-15
        • 2016-02-21
        • 2014-06-28
        相关资源
        最近更新 更多