Может кто-нибудь объяснить, почему это не работает?иногда я получаю ноль - PullRequest
0 голосов
/ 21 марта 2019

Итак, я прохожу эти проблемы с практикой и, возможно, есть лучший способ сделать это, но это то, что я получил. Я сильно смущен, почему иногда это работает ...

def nearest_larger(arr, idx)
bigs = []

arr.each do |num|
    if num > arr[idx]
      bigs << num
    end
end

sorted = bigs.sort
    if sorted.length > 1
      sorted = sorted[0]
    end

return arr.index(sorted)
end

puts nearest_larger([2, 3, 4, 8], 2) #DOES NOT WORK
puts nearest_larger([2, 3, 4, 8], 2) #DOES WORK
puts nearest_larger([8, 8, 4, 2], 2) #ALSO WORKS... why?!!!

Ответы [ 2 ]

1 голос
/ 21 марта 2019

Учитывая массив 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
1 голос
/ 21 марта 2019

Мне не совсем ясно, что именно должна делать эта функция, поскольку я не знаю, какие упражнения вы выполняете, но самая очевидная проблема, которую я вижу, это то, что вы иногда проверяете массив для другого массива,и иногда проверяя это для фактического элемента.В частности, подозрительным блоком является if sorted.length > 1; sorted = sorted[0]; end.

. В вашем первом вызове, который не работает, есть только один номер, который больше индекса 2, который в этом случае равен 8. sorted.case будет иметь длину только 1 - поэтому он не меняет его с массива на первый элемент из-за вашего оператора if.

В любом случае, второй регистр также не должен работать.

Слегка измененная рабочая версия вашего кода будет выглядеть следующим образом.

def nearest_larger(arr, idx)
  larger_numbers = []

  arr.each do |num|
    if num > arr[idx]
      larger_numbers << num
    end
  end

  smallest_larger_number = larger_numbers.sort()[0]

  return arr.index(smallest_larger_number)
end

Меньшая версия будет выглядеть так:

def nearest_larger(arr, idx)
  arr.index(arr.select {|num| num > arr[idx]}.sort()[0])
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...