Что такое алгоритмы и какими они бывают
Ты можешь разрабатывать микросервисы и знать все уровни модели OSI, но какой ты программист, если не можешь объяснить ребёнку, что такое алгоритм?
Иллюстрация: Катя Павловская для Skillbox Media
Август Вилакия
Ведущий бэкенд-разработчик мобильного приложения «Альфа-Банка».
Иногда совсем простые вопросы о профессии вводят в ступор даже опытных специалистов. Примерно так происходит, когда у разработчика с 5–10-летним стажем спрашивают: «Что такое алгоритм?»
Но для того мы здесь и собрались, чтобы дать понятные ответы на «глупые» вопросы. В этой статье расскажем, что такое алгоритмы, для чего они нужны и какими бывают.
Вы узнаете:
- Что такое алгоритмы
- Для чего их используют
- Какие у них есть свойства
- Что такое псевдокод
- Что такое блок-схемы и как их рисовать
- Примеры линейных, ветвящихся, циклических и рекурсивных алгоритмов и блок-схем
Что такое алгоритм
В широком смысле алгоритм — это последовательность действий, которые нужно выполнить, чтобы получить определённый результат.
Слово «алгоритм» произошло от имени персидского математика Абу Абдуллаха аль-Хорезми. В своём труде «Китаб аль-джебр валь-мукабала» учёный впервые дал описание десятичной системы счисления. А наука алгебра получила своё название в честь его книги.
Мы часто пользуемся алгоритмами в повседневной жизни. Например, когда хотим приготовить кофе в капсульной кофемашине, руководствуемся примерно таким алгоритмом:
1. Устанавливаем капсулу.
2. Проверяем уровень воды в специальном отсеке.
3. Если воды недостаточно — доливаем.
4. Ставим чашку под кран кофемашины.
5. Запускаем кофемашину.
6. Выключаем кофемашину, когда чашка наполнилась.
7. Достаём кружку.
Если не перепутать порядок шагов, то с помощью такой инструкции любой сможет порадовать себя чашкой горячего кофе. Достаточно лишь знать, как установить капсулу и включить/выключить кофемашину.
С компьютерами намного сложнее. Им неизвестно, что значит «установить капсулу», «долить воду», «запустить кофемашину» и так далее. Чтобы запрограммировать робота-баристу под определённую модель бытовой техники, алгоритм придётся расписать более детально:
1. Возьми штепсельную вилку шнура питания кофемашины.
2. Вставь штепсельную вилку в розетку.
3. Проверь, есть ли вода в отсеке для воды.
4. Если воды недостаточно:
4.1. Подними крышку отсека.
4.2. Возьми кувшин с водой.
4.3. Лей воду из кувшина в отсек, пока он не заполнится.
4.4. Закрой крышку отсека.
4.5. Поставь кувшин с водой на стол.
5. Открой крышку кофемашины.
6. Возьми из коробки капсулу с кофе.
7. Вставь капсулу в отсек для капсулы.
8. Закрой крышку кофемашины.
9. Поверни рычаг кофемашины вправо.
10. Когда чашка наполнится, поверни рычаг кофемашины влево.
11. Возьми кружку.
12. Принеси кружку хозяину.
Конечно, если мы собираем робота с нуля, то даже такой детализации будет недостаточно. Каждую процедуру ещё нужно будет реализовать на языке программирования (например, на C++ или Python), что само по себе — нетривиальная задача. Тем не менее описание стало более точным и формальным.
C научной точки зрения определение алгоритма, которое мы дали выше, не совсем точное. Ведь не всякую последовательность действий, приводящую к результату, можно назвать алгоритмом.
Алгоритм в информатике — это понятный исполнителю набор правил для решения конкретного множества задач, который получает входные данные и возвращает результат за конечное время.
Для чего нужны алгоритмы
У алгоритмов есть два замечательных качества: они позволяют эффективно решать задачи и не изобретать решения, которые кто-то уже придумал до нас. Это справедливо как для повседневной жизни, так и для IT.
Не нужно изобретать велосипеды
Представьте, что оформляете загранпаспорт. Если будете всё делать сами и без инструкции, около 40 минут потратите только на выяснение необходимых справок и порядка оформления. Куда проще воспользоваться «Госуслугами», потому что алгоритм там уже составлен — делаете, что вам говорят, и ждёте результат. А ещё проще — обратиться к посреднику, который подготовит все справки и оформит паспорт за неделю.
Это очень бытовой пример, но программирование примерно так и работает. Разработчики изучают алгоритмы, чтобы писать быстрый и эффективный код, — распознают типовую задачу и подбирают для неё оптимальный алгоритм.
Допустим, нужно отсортировать в порядке возрастания числа в списке из 1000 элементов. Можно пройтись по списку 1000 раз: на каждой итерации находить наименьшее число и переставлять его в начало списка. В этом случае общее количество шагов будет равно 1 000 000 — современный компьютер справится с этим за секунду.
А если нужно упорядочить массив из 10 000 000 элементов? Тогда компьютеру придётся выполнить 1014 шагов, что потребует гораздо больше времени. Надо оптимизировать!
Разработчик, не сведущий в computer science, начнёт ломать голову над более эффективным решением. А опытный специалист применит алгоритм быстрой сортировки, который в среднем случае даст «время» 16 × 107 шагов.
Знатоки скажут, что ещё проще было бы воспользоваться библиотечной функцией сортировки (например, sorted() в Python). Тем не менее даже встроенные алгоритмы бывают недостаточно эффективными и разработчикам приходится писать собственные функции для сортировки. Но это уже совсем другая история 🙂
Алгоритмы позволяют решать «нерешаемые» задачи
Теперь представьте: вы живёте в XX веке где-нибудь в США и зарабатываете тем, что ездите по городам и продаёте мультимиксеры. Чтобы сэкономить время и деньги, вам нужно придумать кратчайший маршрут, который позволит заехать в каждый город хотя бы один раз и вернуться обратно.
Это знаменитая задача коммивояжёра, для которой практически невозможно подобрать лучшее решение. Простой перебор здесь не поможет. Уже при 10 городах количество возможных маршрутов будет равно 3,6 млн, а при 26 — даже самым мощным компьютерам понадобится несколько миллиардов лет, чтобы перебрать все варианты.
Тем не менее каждый день миллионы устройств решают эту задачу: смартфоны строят маршруты между городами, а маршрутизаторы рассчитывают оптимальный путь для пакетов в сети. Дело в том, что существуют специальные алгоритмы, которые дают неидеальный, но достаточно эффективный результат. И их нужно знать, если вы хотите работать в компаниях, которые создают сложные, интересные проекты.
Свойства алгоритмов
Информатик и автор классических учебников по программированию Дональд Кнут выделял следующие свойства алгоритмов:
- конечность,
- определённость,
- наличие ввода,
- наличие вывода, или результативность,
- универсальность,
- эффективность.
Рассмотрим каждое подробно.
Конечность. Алгоритм должен решать задачу за конечное число шагов. Необходимость этого критерия очевидна: программа, которая решает задачу бесконечно долго, никогда не приведёт к результату.
Определённость. Исполнитель (компьютер, операционная система) должен однозначно и верно интерпретировать каждый шаг алгоритма.
Наличие ввода. Как и у математической функции, результат работы алгоритма зависит от входных данных. Например, на вход алгоритма сортировки подаётся массив чисел. А функция, рассчитывающая факториал, принимает натуральное число.
Наличие вывода, или результативность. Алгоритм должен выдавать конкретный результат. Например, если мы ищем подстроку в строке и такая подстрока в ней присутствует, то на выходе мы должны получить позицию этой строки. Если такой подстроки нет — алгоритм должен вернуть соответствующее значение, например -1.
Универсальность. Алгоритм должен решать задачи с разными входными данными. Например, хорошая функция для сортировки массивов должна одинаково хорошо справляться с массивами из 10, 100 и 1 000 000 элементов.
Эффективность. Это требование продиктовано ограниченными ресурсами компьютеров. На заре развития вычислительной техники каждая секунда работы процессора, каждый байт памяти были на счету. И хотя современные компьютеры гораздо мощнее своих предшественников, они тоже могут «тормозить» из-за неэффективных алгоритмов.
Псевдокод: как написать алгоритм без языка программирования
Представьте, что вы изучили какой-нибудь язык программирования, например Go, и устроились бэкенд-разработчиком в IT-компанию. В вашей команде, помимо бэкендеров, есть фронтенд-разработчики, которые пишут код на JavaScript.
Вы придумали крутой алгоритм, который ускорит работу приложения, и хотите рассказать о нём коллегам. Но как это сделать, если они программируют на другом языке?
Для таких ситуаций есть псевдокод. Он позволяет изложить логику программы с помощью понятных для всех команд, не углубляясь в детали реализации конкретного языка. В учебной литературе алгоритмы описывают в основном с помощью псевдокода.
У псевдокода нет общепринятых стандартов, и авторы используют собственные оригинальные нотации. Хотя часто они заимствуют названия операций из Python, Pascal и Java. Например, код ниже напоминает программу на Python:
Также псевдокод можно писать на русском языке, как в школьных учебниках по информатике:
ФУНКЦИЯ линейный_поиск(целое[] массив, целое x):
ЕСЛИ массив ПУСТОЙ:
ВЕРНУТЬ -1
ДЛЯ i В ДИАПАЗОНЕ ОТ 0 ДО ДЛИНА(массив):
ЕСЛИ массив[x] РАВНО x:
ВЕРНУТЬ i
ВЕРНУТЬ -1
Главное — чтобы тот, кто читает ваш алгоритм, понял его и воспроизвёл на своём языке программирования.
Что такое блок-схемы, или Как нарисовать алгоритм
Если у вас в школе были уроки по информатике, то вы наверняка рисовали и читали блок-схемы. Если нет, то знайте: алгоритмы можно описывать не только словесно, но и графически.
Блок-схемы — это геометрические фигуры, соединённые между собой стрелками. Овалы, прямоугольники, ромбы и другие фигуры обозначают отдельные шаги алгоритма, а стрелки указывают направление потока данных. При этом в каждый блок записывается команда в виде логического или математического выражения.
В таблице ниже представлены основные элементы блок-схем:
Графическое изображение
Значение
Элемент кода в Python
Начало/конец программы
Никак не обозначается
или обозначается как начало функции:
Конец функции обозначается словом return
Ввод/вывод данных
Операторы ввода и вывода:
Арифметические операции
Арифметические операторы:
Условие
Условный оператор:
Цикл со счётчиком
Цикл for:
Ввод/вывод в файл
Функции для работы с файлами:
С помощью этого нехитрого набора фигур можно нарисовать схему практически любого алгоритма. Другие фигуры блок-схем вы найдёте в документации к ГОСТ 19.701-90.
Блок-схемы можно рисовать в Microsoft Visio и в Google Docs (Вставка → Рисунок → Новый +). Также есть специальные сервисы: например, облачный Draw.io и десктопные Dia и yEd.
А теперь разберёмся, какими бывают алгоритмы, напишем примеры на Python и нарисуем для них блок-схемы.
Виды алгоритмов и примеры
По конструкции алгоритмы можно разделить на несколько групп.
Линейные алгоритмы
В линейных алгоритмах действия идут последовательно, одно за другим. Такие программы — самые простые, но на практике они встречаются редко.
Пример. Напишите программу, которая умножает число, введённое пользователем, на 100 и выводит результат на экран.
Последовательность действий уже изложена в задании: ввести число → умножить на 100 → вывести результат. Переведём это на язык блок-схем:
Ниже приведена реализация алгоритма на языке Python:
Ветвящиеся алгоритмы
В ветвящихся алгоритмах ход программы зависит от значения логического выражения в блоке «Условие». По большому счёту, любое логическое выражение сводится к выбору между истиной (True, «1») или ложью (False, «0»).
Пример. Напишите программу, которая запрашивает у пользователя возраст. Если он равен или больше 18, программа выводит приветствие, увеличивает значения счётчика посетителей на 1 и прощается, а если меньше — сразу прощается и завершает работу.
Чтобы изобразить ход решения, воспользуемся условным блоком. Во всех схемах его обозначают ромбом с вписанным условием:
То же самое на Python:
Когда пользователь вводит 18 или больше, программа выполняет часть кода, которая записана под оператором if. Если же возраст меньше 18, то на экран выводится сообщение «Доступ запрещён» и программа завершает работу.
Циклические алгоритмы
Такие алгоритмы содержат циклы — наборы действий, которые выполняются несколько раз. Количество повторений может задаваться целым числом или условием. В некоторых случаях, например, в операционных системах и прошивках микроконтроллеров, используются бесконечные циклы.
Пример. Напишите программу, которая циклично увеличивает значения счётчика на 1 и на каждом шаге выводит его значение. Когда значение счётчика достигнет 10, программа должна завершиться.
В основе нашего решения будет лежать следующее условие: если значение счётчика меньше 10 — прибавить 1, иначе — завершить работу. Вот как это выглядит в виде блок-схемы:
Переведём это в код на Python. Обратите внимание, что мы не прописываем отдельную ветвь для случая «Нет»:
Результат работы программы:
Рекурсивные алгоритмы
Рекурсия — это явление, при котором система вызывает саму себя, но с другими входными данными. Такие алгоритмы используют для обхода словарей в глубину, вычисления факториала, расчёта степеней и других практических задач. В целом всё это можно сделать с помощью циклов, но код рекурсивных функций более лаконичен и удобочитаем.
Пример. Пользователь вводит число n. Посчитайте его факториал и выведите результат на экран.
Вот как выглядит блок-схема рекурсивного алгоритма:
На практике чисто последовательные, условные или циклические алгоритмы встречаются редко, но вместе они позволяют создать решение любой сложности.
Есть и другие классификации алгоритмов. Например, по множеству решаемых задач их можно разделить на численные, поисковые, сортировочные, строковые, сетевые и криптографические. А по точности получаемых результатов — на нормальные и стохастические (вероятностные).
Что дальше
Если хотите изучить алгоритмы более подробно, начните с простых и увлекательных книг по computer science:
- «Грокаем алгоритмы», Адитья Бхаргава;
- «Теоретический минимум по Computer Science», Владстон Фило;
- «Гид по Computer Science», Вильям Спрингер.
Когда познакомитесь с основными алгоритмами и научитесь решать с их помощью стандартные задачи, переходите к более серьёзной литературе. Например, прочитайте Computer Science Роберта Седжвика и «Алгоритмы» Рода Стивенса.
У «Яндекса» есть бесплатные тренировки с разбором алгоритмических задач и распространённых ошибок. А попрактиковаться, закрепить теорию и подготовиться к техническому интервью можно на LeetCode — там есть сотни задач разной сложности и для разных языков программирования.