{"id":14293,"url":"\/distributions\/14293\/click?bit=1&hash=05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","hash":"05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","title":"\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0435 \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0432 \u043d\u0438 \u043a\u043e\u043f\u0435\u0439\u043a\u0438","buttonText":"","imageUuid":""}

Компьютерные алгоритмы – Binary Search и Big O нотация

Как ускорить поиск с 301 067 операций до 18.

В жизни каждого разработчика наступает момент, когда ему приходится знакомиться с алгоритмами. У кого-то, кто учится на Computer Science, он наступает раньше, у кого-то, кто учится сам — позже.

По поводу алгоритмов часто возникают холивары. Даже не так — они всегда возникают, когда о них заходит речь. В одном лагере те, кто считает, что это чисто академическая дисциплина, которая не имеет ничего общего с реальной жизнью.

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

На мой взгляд, каждая сторона права по своему. Front-end разработчикам алгоритмы действительно бывают не нужны, если работа не связана с бизнес-логикой. Собирать фронты на Vue/React/Angular, делать их адаптивными, красивыми и анимированными - тут подобные знания не пригодятся. А всю тяжелую работу делает бекенд.

Однако, когда встает необходимость писать какую-то бизнес-логику, которая использует нетривиальную последовательность действий, например, для формирования того же списка, (ну, не смогла команда бека впихнуть в спринт сортировку для адресной книги, сделайте на клиенте, вы-чо-не-программисты-чтоле) алгоритмы могут быть очень даже кстати.

Давайте для примере разберем один базовый алгоритмов, который известен как Binary Search.

Есть массив сортированных чисел.

Я загадываю число, которое есть в массиве. А вам надо его отгадать.

Отгадать надо с наименьшего количества попыток. После каждой попытки я говорю — была ли попытка «слишком мало» или «слишком много». Как «тепло»/«холодно».

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

Например, я загадываю 416.

Если поставить внутри цикла принт, то можно посчитать, сколько будет операций поиска. Я загадал предпоследнее, поэтому их будет количество чисел минус один.

Есть другой способ — Binary Search.

Работает он так — искать начинаем с середины списка.

Представим, что список у нас от 0 до 1000, и загадано число 600.

Число на позиции 500 - слишком много, или слишком мало ?

— Слишком мало

Ага, таким образом все, что меньше, нас не интересует. Ищем в оставшейся половине.

— А на позиции 750 ?

— Слишком много.

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

Таким образом не надо просеивать последовательно весь список.

  • Объявляем минимальную границу поиска - первый индекс в списке.
  • Объявляем максимальную границу поиска - последний индекс в списке.
  • Идем циклом, пока нижняя граница не станет равной максимальной или не будет найден нужен индекс.Если ничего не нашли - возвращаем пустоту.
  • В цикле начинаем с того, что ищем индекс середины между текущей минимальной и максимальной границами. Найдя его, достаем из массива по этому индексу число - кандидат.
  • Если кандидат равен загаданному числу - все супер, мы нашли.
  • Если кандидат больше загаданного числа, то меняем верхнюю границу поисков – уменьшаем текущую середину на 1.
  • Если кандидат меньше загаданного числа, то меняем верхнюю границу поисков – увеличиваем текущую середину на 1.

Если после строки let candidate = numbers[middle] добавить принт, то можно проследить, как работает алгоритм.

Minimum = 0 Maximum = 15 Middle = 7 Candidate = 19 Target = 416 Minimum = 8 Maximum = 15 Middle = 11 Candidate = 110 Target = 416 Minimum = 12 Maximum = 15 Middle = 13 Candidate = 400 Target = 416 Minimum = 14 Maximum = 15 Middle = 14 Candidate = 416 Target = 416

Поиск нашел нужное число за 4 попытки.

Представим, что мы ищем индекс записи в списке адресов, в котором 400 000 записей.

Сколько операций поиска надо сделать, когда загадано число 301 067, используя простой поиск ?

Правильно, 301 067.

А используя бинарный поиск - 18.

При использовании программы, которая будет работать с первым алгоритмом на данных выше, можно будет сходить выгулять собаку, пока программа будет делать свою магию.

Big O

Существует так называемая Big O нотация. Она описывает, насколько алгоритм быстр в использовании. Если у нас есть список длинной n, то обычный поиск будет искать в нем индекс со скоростью O(n) — чем больше элементов в списке, чем больше величина n. А бинарный – O(log n). Берется всегда самый худший кейс, на нашем примере – число загаданное в самом конце.

На всякий случай напомню, что такое логарифм

Логарифм — это степень, в которую надо возвести число после log, чтобы получить число перед знаком равно.

log(2)8 = 3

log(2)4 = 2

log(2)16 = 4

Кроме O(n) и O(log n) чаще всего встречаются следующие сложности

  • O(1) - константа. Сложность всегда одинаковая, для любого объема данных. Например, сравнение двух чисел.
  • O(n!) - факториал. Пример, Задача коммивояжёра.
  • O(n в квадрате) - квадратичная сложность. Пример - сортировка пузырьком.

По сути нотация Big O - это бенчмарк, который позволяет понять, на сколько алгоритм производителен. С ним можно прикинуть, какое время займет обработка тех или иных данных, в зависимости от их объема.

На этом у меня сегодня все.

Пока!

PS: Не обижайте алгоритмы, иногда они бывают полезны.

0
15 комментариев
Написать комментарий...
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
Василий Петров

Ещё хуже, когда balance это тип uint, и если из нулевого баланса вычесть единицу, получается максимальное число. Это очень весело, согласитесь?

Ответить
Развернуть ветку
V K

Можно ещё статью про умножение столбиком.

Ответить
Развернуть ветку
Юрий Б.

Грокнули алгоритмы не вставая с дивана вообще!

Ответить
Развернуть ветку
Василий Петров

Спасибо автору за эту статью, я порадовался

Ответить
Развернуть ветку
Саша G

И на кой это в "карьера"?

Ответить
Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
Time Coder

Берётся худший случай - это не так. Берётся средний. Более того, автор не передал главной идеи: big O показывает природу роста вычислительной сложности, а не ее абсолютное значение. Есть массив размером N, есть алгоритм, пусть, того же поиска перебором. O(N) означает не «алгоритму надо сделать N действий», а «если элементов станет вдвое больше, алгоритм будет работать вдвое дольше». Отсюда ещё одно важнейшее понимание: все константы опускаются. Например, вам надо найти в массиве первую попавшуюся пару чисел, в сумме равную некому числу. Делаем цикл в цикле, и перебираем все сочетания. Потом понимаем - если мы проверили второе и десятое, нет смысла поверять десятое и второе (от перемены мест слагаемых сумма не меняется). Значит первый цикл проходит все элементы от первого до предпоследнего, а вложенный - каждый раз стартует со следующего элемента. Какова сложность этого алгоритма? Если нарисовать в виде квадратной таблицы все сочетания, мы теперь перебираем ровно половину, на первый взгляд ответ O(N/2). Но нас интересует природа роста. В 10 раз больше элементов - в 10 (а не в 5) раз больше действий, поэтому ответ O(N). Изучайте алгоритмы, это ключ от многих дверей.

Ответить
Развернуть ветку
Кемаль

Те кто говорят что на фронте не нужны алгоритмы, явно работают или работали над лёгким продуктом. Я раньше тоже так думал, но в конце прошлого года и начале 2022, мне за месяц залетело 4 задачи с древовидной структурой, где нужно было использовать рекурсию и так же добавлять дополнительные отсортированные структуры в дерево, тут то я и "приехал", прожигая стул полтора дня, пошел за помощью к СТО, он помог решить, после этого всерьез задумался что алгоритмы нужны, хотя бы те что связаны с обходом деревьев, поиском и т.д. а уж тем более уметь решать сложные задачи рекурсией.

Ответить
Развернуть ветку
bjl

Почему оказывается, что это объясняют на информатике в школе в первый год? Разве нет?

Ответить
Развернуть ветку
Gre Li

Хорошо у вас информатику преподают, если объясняют это, а не как сделать таблицу в Ворде.

Ответить
Развернуть ветку
Dmitry Kuteiko

Многие прогуливают, остальные пропускают мимо ушей.

Ответить
Развернуть ветку
Александр

Тут про ошибку в вашем алгоритме https://habr.com/ru/post/203398/?

Ответить
Развернуть ветку
Aidar S

Топ, благодарю за статью. С таким прорывом в обучении тайнам программирования мы точно скоро импортозаместим всю продукцию.

Ответить
Развернуть ветку
12 комментариев
Раскрывать всегда