从数组中获取最接近的数字值

Che*_*kar 1 ruby

我有一个数组:

["7", "8", "11", "13", "14"]
Run Code Online (Sandbox Code Playgroud)

我想要最接近的次等数(11如果有的话8),或者如果这样的数字不存在,那么最接近的优等数就是11(即13).

saw*_*awa 13

h = ["7", "8", "11", "13", "14"].map(&:to_i).sort.group_by{|e| e <=> 11}
h[-1].last || h[1].first # => 8
Run Code Online (Sandbox Code Playgroud)

  • 好的,sawa! (2认同)

Vis*_*hal 12

尝试使用以下最短的方法获取最接近的值

n = 40
a = [20, 30, 45, 50, 56, 60, 64, 80]
a.min_by{|x| (n-x).abs}
Run Code Online (Sandbox Code Playgroud)


Ste*_*fan 6

解决这个问题的另一种方法:

a = arr.map(&:to_i).sort      #=> [7, 8, 11, 13, 14]
a.reverse.find { |e| e < 11 } #=> 8
a.find { |e| e > 11 }         #=> 13
Run Code Online (Sandbox Code Playgroud)

由于如果没有对象匹配则find返回nil,因此可以通过以下方式组合最后两行:

a.reverse.find { |e| e < 11 } || a.find { |e| e > 11 }
Run Code Online (Sandbox Code Playgroud)

  • ...或`a.reverse_each.find ..` 以避免创建临时数组`a.reverse`。 (2认同)

Car*_*and 5

下面的方法不对数组进行排序,时间复杂度为 O(n),n 是数组的大小。相比之下,对于第一步对数组进行排序的方法,时间复杂度最多为 O(nlog(n))。参考号

为了避免模糊问题的中心元素,我将假设数组包含整数而不是整数的字符串表示形式。在提出解决方案后,我将简要讨论这一点。

def closest(arr, target)
  return nil if arr.empty?
  arr.min_by  { |e| e <= target ? [0, target-e] : [1, e-target] }
end
Run Code Online (Sandbox Code Playgroud)
arr = [11, 7, 13, 8, 11, 14]
Run Code Online (Sandbox Code Playgroud)
closest(arr, 12)   #=> 11
closest(arr, 12.5) #=> 11
closest(arr, 11)   #=> 11
closest(arr, 4)    #=>  7
closest(arr, 7)    #=>  7
Run Code Online (Sandbox Code Playgroud)

请参阅Enumerable#min_by

请注意,数组是通过Array#<=>方法排序的(请参阅文档的第三段),因此,例如,

target = 12
arr.sort_by  { |e| e <= target ? [0, target-e] : [1, e-target] }
  #=> [11, 11, 8, 7, 13, 14]
Run Code Online (Sandbox Code Playgroud)

因为0 < 1,不大于 的四个元素target构成返回数组的前四个元素 ( [11, 11, 8, 7]),大于 的两个元素target构成最后两个元素 ( [13, 14]),这两个组均按到 的“距离”排序target。该方法不使用sort_by,但它以相同的方式对元素进行排序arr

[0, target-e]and[1, e-target]可以用[n1, target-e]and代替[n2, e-target],其中n1n2是 的任何对象n1 <=> n2 #=> -1。例如,n1 = -7n2 = 42n1 = 'a'n2 = 'b'


如果arr包含整数的字符串表示形式,如问题中所示,则可以首先将数组转换为整数数组,或者按如下方式更改方法。

def closest(arr, target)
  return nil if arr.empty?
  target_f = target.to_f
  arr.min_by do |e|
    e_f = e.to_f
    e_f <= target_f ? [0, target_f-e_f] : [1, e_f-target_f]
  end
end
Run Code Online (Sandbox Code Playgroud)