这不是这个问题 - how to find best matching element in array of numbers?,因为链接的问题涉及"最近的"即使该元素大于有问题的元素,也应该使用元素。我在下面的问题涉及的元素仅比所讨论的变量小。这是我的问题:
我使用的是Ruby 2.4。我有一系列数字。它们总是有序且独特的
[1, 5, 8, 12, 16, 17]
给定一个变量中的另一个数字,如何从最接近我的变量的数组中找出元素而不进行转换?例如,如果我的变量包含" 11"并且在上面的例子中,答案是" 8"。
答案 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 }
为什么呢?对于较小的n
,O(n)
和O(log n)
之间没有区别,并且在早期优化之前始终可读。
答案 3 :(得分:0)
否reverse
或reverse_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)