Поиск и смещение
В этом разделе мы поговорим подробнее про пару функций MATCH и OFFSET
(ПОИСКПОЗ и СМЕЩ в русском варианте).
Введение
Функция MATCH ищет указанное значение в указанном одномерном диапазоне.
У неё три входа: искомое значение, диапазон, вид поиска. На выходе —
порядковый номер найденного значения (нумерация начинается с единицы).
Поговорим подробнее про вид поиска: он бывает бинарным (значения входа -1 и 1) и линейным (значение входа 0). Бинарный поиск быстрее, но предполагает упорядоченность массива данных (для 1 — в порядке возрастания, для -1 — в порядке убывания).
Основной подвох: по умолчанию вид поиска равен 1 (бинарный по возрастающему массиву данных), а в реальности чаще требуется поиск по неупорядоченным данным. Поэтому важно следить за тем, чтобы третий вход был равен 0, если только данные не упорядочены.
Функция OFFSET позволяет по ссылке и вектору смещения получить ссылку,
смещённую относительно входной на указанный вектор. Её входы: смещаемая ссылка,
вертикальное смещение (сверху вниз), горизонтальное смещение
(слева направо).
С этой функцией связано два подвоха:
- Нетрадиционный порядок координат вектора смещения. Более того, он даже не
соответствует порядку координат в стандартном синтаксисе для ссылок. То есть
на первый взгляд кажется, что
OFFSET(A1,2,4)— этоC5, но на самом делеOFFSET(A1,2,4)— этоE3. - Нумерация «с единицы» у функции
MATCH, которая очень часто используется в паре сOFFSET. В итоге, чтобы получить найденную ячейку, приходится дописывать минус единицу:OFFSET($A$1; MATCH($G$42;$A$1:$A$1000;0)-1; 0)
Стандартные ситуации применения
Наиболее стандартная (и почти единственная) ситуация применения
пары MATCH и OFFSET — ассоциативный массив. С точки зрения
электронных таблиц ассоциативный массив — это два столбца
(или две строчки). В одной — ключи, в другой — значения.
Запрос к такому массиву — это ответ на вопрос «какое значение находится напротив указанного ключа?»
В предположении, что ключи находятся в столбце A, значения —
в столбце B, а запрашиваемый ключ — в ячейке C1, сам запрос
полностью выглядит как
=OFFSET($B$1; MATCH($C$1;$A$1:$A$1000;0)-1; 0)
Зачастую в реальных таблицах однородные данные оказываются разнесены сразу на несколько ассоциативных массивов. В таком случае рекомендуется следующая схема организации вычислений:
- Каждый из массивов обработать по отдельности.
- Результаты обработки выстроить друг за другом.
- Предположим, для определённости, что было 10 массивов и результаты
запросов к ним оказались в диапазоне
P1:P10. - В ячейку
Q1пишем формулу=P1. - В ячейку
Q2пишем формулу=IFERROR(Q1;P2). - Копируем ячейку
Q2во все остальные ячейки диапазонаQ2:Q10.
В итоге в ячейке Q10 окажется ответ на запрос ко всему множеству данных.
Примитивная реализация
Наконец, покажем, как можно реализовать MATCH и OFFSET (по модулю
обработки ошибок) в терминах примитивного IF.
Сначала MATCH. Допустим, что нам нужно найти значение ячейки H1 в
диапазоне A1:A1000. Для этого можно выполнить следующие действия:
- Поместить в ячейку
B1число 1 (порядковый номер первого значения). - Поместить в ячейку
B2формулу=B1+1и скопировать её на весь диапазонB2:B1000. Теперь в этом диапазоне порядковые номера соответствующих значений. - Поместить в ячейку
C1формулу=IF(A1=$H$1; B1; NA()). - Поместить в ячейку
C2формулу=IF(A2=$H$1; B2; C1). Скопировать эту формулу на весь диапазонC2:C1000. - В клетке
C1000находится ответ.
Это — аналог запроса =MATCH($H$1; $A$1:$A$1000; 0). Бинарный
поиск оставляем в качестве (необязательного и весьма трудного) упражнения.
Теперь реализуем аналог запроса =OFFSET($A$1;$H$1;0) (вариант с двумя
ненулевыми координатами можно реализовать как два последовательных сдвига,
в каждом из которых одна из координат нулевая). Достаточно выполнить
следующее:
- Поместить в ячейку
B1число 0 (смещение первого значения). - Поместить в ячейку
B2формулу=B1+1и скопировать её на весь диапазонB2:B1000. Теперь в этом диапазоне смещения соответствующих значений. - Поместить в ячейку
C1формулу=IF(B1=$H$1; A1; NA()). - Поместить в ячейку
C2формулу=IF(B2=$H$1; A2; C1). Скопировать эту формулу на весь диапазонC2:C1000. - В клетке
C1000находится ответ.
Как можно заметить, примитивные реализации MATCH и OFFSET очень
похожи друг на друга. Это и не удивительно: первая восстанавливает порядковый
номер по значению, а вторая — значение по порядковому номеру. Другое
дело, что реальная функция OFFSET работает гораздо эффективнее примитивной
реализации (а вот MATCH имеет ровно ту же асимптотическую вычислительную
сложность, что и примитивная реализация).