Учитывая массив предварительно отсортированных значений, как мы можем вернуть индекс для данного значения с помощью Ruby? В реальной жизни в большинстве случаев вы бы использовали преимущества встроенного в Ruby итеративного поиска #index, но сегодня давайте воспользуемся подходом «сделай сам» с алгоритмом бинарного поиска. Мы будем использовать следующий массив случайных чисел в порядке возрастания:

arr = [1, 5, 7, 30, 51, 55, 67, 88, 89, 100, 102]

Мы будем использовать подход «разделяй и властвуй» к проблеме. Первый шаг — установить переменные для пола и потолка массива. Затем мы запускаем цикл while, в котором мы делаем обоснованные предположения с некоторой условной логикой, чтобы отсеять поле подозреваемых. Нашим первым предположением будет расположение индекса в середине нашего массива.

def binary_search(arr, value)
  floor = 0
  ceiling = arr.length - 1
  counter = 0
  
  while floor <= ceiling
    guess = (floor + ceiling)/ 2
  
    # if guess is correct, then great, but if not, then do 
    # stuff with info learned to make the next try more likely to 
    # succeed!
  
  end
end

Хорошо, теперь об этой логике. Мы знаем, что наше первое предположение будет либо равно, либо меньше, либо больше индекса искомого значения, и с помощью оператора if мы можем использовать эту информацию для быстрого отсеивания больших блоков индексов для каждого последующего предположения. . Если наше предположение слишком низкое, все значения индекса, равные или меньшие, чем предположение, также будут слишком низкими, поэтому мы можем безопасно сбросить нашу переменную пола на guess + 1 для следующего хода. Точно так же, если наше предположение слишком велико, все значения индекса, равные или превышающие предположение, также будут слишком высокими, поэтому мы сбрасываем переменную потолка на guess — 1.

def binary_search(arr, value)
  floor = 0
  ceiling = arr.length - 1
  while floor <= ceiling
    guess = (floor + ceiling)/ 2
    if arr[guess] == value
      puts "#{guess} is right!"
      break
    elsif arr[guess] < value
      puts "Too low. Guess higher."
      floor = guess + 1
    else
      puts "Too high. Guess lower."
      ceiling = guess - 1
    end 
  end
end

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

def binary_search(arr, value)
  floor = 0
  ceiling = arr.length - 1
  counter = 0
  winner = nil
  while floor <= ceiling
    counter +=1
    guess = (floor + ceiling)/ 2
    print "Attempt #{counter}: "
    if arr[guess] == value
      winner = guess
      puts "Index ##{guess} is right!"
      break
    elsif arr[guess] < value
      puts "Too low. Guess higher."
      floor = guess + 1
    else
      puts "Too high. Guess lower."
      ceiling = guess - 1
    end
  end
  
  puts "Your number is not in array" unless winner
end

Здесь мы добавляем переменные для счетчика и победителя. Первый увеличивается в нашем цикле while, чтобы показать, сколько ходов он прошел, чтобы получить ответ. Переменная победителя служит для запуска нашего сообщения об ошибке. Таким образом, при использовании с нашим исходным массивом и значением 88 мы получаем следующий вывод:

arr = [1, 5, 7, 30, 51, 55, 67, 88, 89, 100, 102]
binary_search(arr, 88)
# returns:
Attempt 1: Too low. Guess higher.
Attempt 2: Too high. Guess lower.
Attempt 3: Too low. Guess higher.
Attempt 4: Index #7 is right!

И если мы попробуем метод со значением, которого нет в нашем массиве:

arr = [1, 5, 7, 30, 51, 55, 67, 88, 89, 100, 102]
binary_search(arr, 74)
# returns
Attempt 1: Too low. Guess higher.
Attempt 2: Too high. Guess lower.
Attempt 3: Too low. Guess higher.
Attempt 4: Too high. Guess lower.
Your number is not in array

Метод Ruby #index более универсален (во-первых, он может работать с несортированными массивами), но полезно знать, что скрывается под капотом метода binary_sort.