Учитывая массив предварительно отсортированных значений, как мы можем вернуть индекс для данного значения с помощью 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.