Учитывая массив a
и индекс массива i
, предположим, что idxa
- это массив индексов j
из a
, для которых a[j] > a[i]
.Если idxa
пусто, nil
должно быть возвращено;иначе проблема заключается в поиске индекса k
в idxa
, для которого (k-i).abs
является минимумом.Если idx
содержит более одного индекса k
, для которого (k-i).abs
является минимумом, любой из этих индексов может быть возвращен.
Выберите индексы и найдите ближайший
Вот простой, но не особенно эффективный способ решения проблемы.
def nearest_larger(arr, idx)
v = arr[idx]
a = arr.each_index.select { |i| arr[i] > v }
return nil if a.empty?
a.min_by { |i| (i-idx).abs }
end
test = [
[[2, 3, 4, 8], 2],
[[8, 5, 4, 3], 2],
[[8, 3, 4, 2], 2],
[[8, 5, 8, 2], 2]
]
test.each do |arr, idx|
i = nearest_larger(arr, idx)
puts "#{arr} idx = #{idx}: closest idx = #{i.nil? ? "nil" : i}"
end
[2, 3, 4, 8] idx = 2: closest idx = 3
[8, 5, 4, 3] idx = 2: closest idx = 1
[8, 3, 4, 2] idx = 2: closest idx = 0
[8, 5, 8, 2] idx = 2: closest idx = nil
Давайте рассмотрим шаги для этих значений arr
и idx
:
arr = [8, 5, 4, 3]
idx = 2
v = arr[idx]
#=> 4
e = arr.each_index
#=> #<Enumerator: [8, 5, 4, 3]:each_index>
Как вы видите метод Array # each_index , при вызове arr
возвращает перечислитель.Думайте об этом как о машине, которая генерирует ценности.Мы можем преобразовать перечислители в массивы, чтобы увидеть, какие значения они будут генерировать:
e.to_a
#=> [0, 1, 2, 3]
Продолжение,
a = e.select { |i| arr[i] > v }
#=> [0, 1]
e
передает элементы в Enumerable # select , которыйзатем по очереди переходит к блоку, присваивая переменной блока i
их значениям.Фактически, мы могли бы написать
ee = e.select
#=> #<Enumerator: #<Enumerator: [8, 5, 4, 3]:each_index>:select>
ee.to_a
#=> [0, 1, 2, 3]
a = ee.each { |i| arr[i] > v }
#=> [0, 1]
ee
, который можно рассматривать как составной перечислитель.Если этот последний бит сбивает с толку, забудьте об этом.
Продолжение,
return nil if a.empty?
a
не пусто, поэтому мы не возвращаемся.Последний шаг следующий:
a.min_by { |i| (i-idx).abs }
#=> 1
См. Enumerable # min_by .
Обратите внимание, что я мог бы заменить arr.each_index
на arr.size.times
(см. Целое число # раз ) или 0.up_to(arr.size-1)
(см. Целое число # до ).
Проверьте любую из сторон, затем двигайтесь на одну, пока не будет найдено большее значение
Мы можем повысить эффективность за счет повышенной сложности.На самом деле следующий код не очень похож на Ruby и выглядит довольно непривлекательно.
def nearest_larger(arr, idx)
v = arr[idx]
last = arr.size-1
iup, idn = idx + 1, idx - 1
while iup <= last || idn >= 0
if iup <= last
return iup if arr[iup] > v
iup += 1
end
if idn >= 0
return idn if arr[idn] > v
idn -= 1
end
end
nil
end
test.each do |arr, idx|
i = nearest_larger(arr, idx)
puts "#{arr} idx = #{idx}: closest idx = #{i.nil? ? "nil" : i}"
end
[2, 3, 4, 8] idx = 2: closest idx = 3
[8, 5, 4, 3] idx = 2: closest idx = 1
[8, 3, 4, 2] idx = 2: closest idx = 0
[8, 5, 8, 2] idx = 2: closest idx = nil