如何在数组中找到始终小于元素的元素(注意NOT最接近,严格小于)?

时间:2017-01-09 17:09:32

标签: arrays ruby

这不是这个问题 - how to find best matching element in array of numbers?,因为链接的问题涉及"最近的"即使该元素大于有问题的元素,也应该使用元素。我在下面的问题涉及的元素仅比所讨论的变量小。这是我的问题:

我使用的是Ruby 2.4。我有一系列数字。它们总是有序且独特的

[1, 5, 8, 12, 16, 17]

给定一个变量中的另一个数字,如何从最接近我的变量的数组中找出元素而不进行转换?例如,如果我的变量包含" 11"并且在上面的例子中,答案是" 8"。

5 个答案:

答案 0 :(得分:3)

我使用:

n = 10
[1, 5, 8, 12, 16, 17].select { |i| i <= n }.last # => 8

考虑到这一点,最快的方法是使用bsearch_index,因为输入已经排序。

n = 11 
ary = [1, 5, 8, 12, 16, 17]
ary[ary.bsearch_index { |i| n < i } - 1] # => 8
如果命中发生在ary的早期,则bsearch会变慢,但是在大数组中,与查看每个元素相比,它会快速前进。

require 'fruity'

n = 11 
ary = [1, 5, 8, 12, 16, 17]

compare do 
  last { ary.select { |i| i <= n }.last }
  bsearch_index { ary[ary.bsearch_index { |i| n < i } - 1] }
  Bustikiller { ary.reverse.find { |i| i <= n } }
  engineerDave1 { ary.take_while {|x| x <= n}[-1] }
  engineerDave2 { ary.reduce(nil) {|a,x| a = x if x <= n; a} }
end

# >> Running each test 8192 times. Test will take about 1 second.
# >> bsearch_index is faster than engineerDave1 by 10.000000000000009% ± 10.0%
# >> engineerDave1 is faster than last by 2x ± 0.1
# >> last is similar to engineerDave2
# >> engineerDave2 is similar to Bustikiller

增加数组的大小:

require 'fruity'

n = 999 
ary = (0..1000).to_a

compare do 
  last { ary.select { |i| i <= n }.last }
  bsearch_index { ary[ary.bsearch_index { |i| n < i } - 1] }
  Bustikiller { ary.reverse.find { |i| i <= n } }
  engineerDave1 { ary.take_while {|x| x <= n}[-1] }
  engineerDave2 { ary.reduce(nil) {|a,x| a = x if x <= n; a} }
end

# >> Running each test 4096 times. Test will take about 17 seconds.
# >> bsearch_index is faster than Bustikiller by 3x ± 1.0
# >> Bustikiller is faster than engineerDave1 by 21x ± 1.0
# >> engineerDave1 is faster than last by 30.000000000000004% ± 10.0%
# >> last is faster than engineerDave2 by 10.000000000000009% ± 10.0%

以下评论中提到了简单使用bsearch_index的问题。我认为值得研究使用它,但用一些逻辑来包装它来解决上面提到的问题。我将此作为社区答案,以便任何提出其余代码的人都可以添加它。

答案 1 :(得分:1)

[1, 5, 8, 12, 16, 17].reverse.find{|n| n <= 11}
# => 8

答案 2 :(得分:1)

您可以使用Ruby 2.0中引入的Array#bsearch方法进行二分查找。

唉,这个电话看起来有点难看,因为我们需要进行反向搜索,但二进制搜索的复杂度O(log n)是最快的,你可以获得最快的速度:

arr = [1, 5, 8, 12, 16, 17]
index = (1..arr.size).bsearch { |i| arr[-i] < 11 }
index ? arr[-index] : nil
# => 8

如果您的阵列很短,请使用Bustikiller建议的find

[1, 5, 8, 12, 16, 17].reverse_each.find { |n| n <= 11 }

为什么呢?对于较小的nO(n)O(log n)之间没有区别,并且在早期优化之前始终可读。

答案 3 :(得分:0)

reversereverse_each 1

def biggest_but_do_not_go_over(arr, nbr_to_not_go_over)
  return nil if arr.empty? || arr.first > nbr_to_not_go_over
  enum = arr.to_enum
  n = nil # anything
  loop do
    n = enum.next
    return n if nbr_to_not_go_over < enum.peek
  end
  n
end

arr = [1, 5, 8, 12, 16, 17]

biggest_but_do_not_go_over(arr,  0) #=> nil
biggest_but_do_not_go_over(arr,  1) #=>  1
biggest_but_do_not_go_over(arr,  7) #=>  5
biggest_but_do_not_go_over(arr, 16) #=> 16
biggest_but_do_not_go_over(arr, 17) #=> 17
biggest_but_do_not_go_over(arr, 18) #=> 17

当枚举器StopIteration生成enum的所有元素并执行arr时,会引发enum.peek异常。 Kernel#loop通过突破循环来处理该异常。

1。不是更好,只是不同。

答案 4 :(得分:-1)

始终有序和独特意味着您可能面临大型阵列的潜力,因此尽可能少地操作可能是最佳选择。

[1, 5, 8, 12, 16, 17].take_while {|x| x <= 11}[-1]

或带累加器的reduce函数

[1, 5, 8, 12, 16, 17].reduce {|a,x| x <= 11 ? (a = x) : (break a)}

当搜索的数字是500000的10000000时的基准。还添加了锡人的基本barches_index方法的基准将是要走的路。显然,我没有做足够的优化,只是简单的下来和肮脏的快速代码。他应该是IMO接受的答案

   Rehearsal -------------------------------------------------
reverse         0.200000   0.000000   0.200000 (  0.209630)
take_while      0.010000   0.000000   0.010000 (  0.007428)
reduce          0.010000   0.000000   0.010000 (  0.012950)
bsearch_index   0.000000   0.000000   0.000000 (  0.001008)
---------------------------------------- total: 0.220000sec

                    user     system      total        real
reverse         0.200000   0.000000   0.200000 (  0.199569)
take_while      0.010000   0.000000   0.010000 (  0.007247)
reduce          0.010000   0.000000   0.010000 (  0.012440)
bsearch_index   0.000000   0.000000   0.000000 (  0.000006)

搜索号码的内存基准是500000的10000000

Calculating -------------------------------------
             reverse    26.667M memsize (     0.000  retained)
                         1.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
          take_while    40.000  memsize (     0.000  retained)
                         1.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
              reduce     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)
       bsearch_index     0.000  memsize (     0.000  retained)
                         0.000  objects (     0.000  retained)
                         0.000  strings (     0.000  retained)