Алгоритмическая сложность или как сделать удобным все
Многие из нас сталкивались с оценкой сложности алгоритмов и хорошо представляют себе для чего она нужна. Но зачастую разработчики применяют О-нотацию исключительно к коду, забывая о том что алгоритмы существуют за его пределами. При этом, с ее помощью можно легко описать успех Инстаграма, предсказать, что веб-приложения займут доминирующую роль в ближайшем десятилетии и даже описать принцип работы мозга.
Недавно я столкнулся со статьей о пользе О-нотации в управлении проектом, которая и заставила меня написать данный текст. Я давно и успешно применяю оценку сложности в разработке и периодически сталкиваюсь с ограниченным пониманием ее разработчиками.
То о чем я говорю — это сама суть информационных технологий. Артемий Лебедев попробовал описать этот принцип, но получилось вот что.
Ключом к успеху является снижение сложности. Чем-то это напоминает смесь из принципов KISS и ТРИЗ.
Примеры
Для начала я приведу различные примеры применения принципа снижения алгоритмической сложности или же нарушения этих принципов. Возможно их будет больше чем вы ожидали. Вообще идеальным примером является конвейер Генри Форда, но давайте рассмотрим и другие:
Успех Instagram
Я давно занимаюсь фотографией и хорошо представляю себе процесс превращения снимка в продукт готовый к потреблению. Давайте перечислим шаги которые осуществляет пользователь для того чтобы поделиться красивой фотографией. Для этого он должен:
- Выгрузить снимок из фотоаппарата на компьютер:
- Физически подключить фотоаппарат к компьютеру/вставить карту памяти
- Открыть папку с фотографиями
- Создать папку назначения на компьютере
- Переместить файлы
- Выбрать подходящий снимок:
- Открыть программу-просмотрщик
- Отобрать лучшие снимки
- Выбрать один-два из понравившихся
- Конвертировать в формат подходящий для редактирования
- Обработать фотографию
- Запустить фоторедактор
- Перенести фотографию в фоторедактор
- Провести обработку (здесь количество вложенных шагов варьируется и может достигать нескольких десятков, если не сотен)
- Сохранить обработанный дубликат
- Поделиться результатом
- Выбрать подходящую площадку: flickr, 500 px, vk, fb, tumblr.
- Загрузить фотографию в альбом или коллекцию, поместить на стене/ленте (зависит от сервиса).
- Придумать название или описание, указать теги (опционально).
В описании данного алгоритма я опустил то, что человек при этом должен разобраться в отличии JPEG от RAW, принципе работы фоторедактора, установить несколько программ и разобраться в десятках нюансов работы каждой из них и при этом обладать художественным вкусом для получения приличного результата после обработки.
Теперь давайте взглянем на этот процесс с точки зрения пользователя Instagram:
- Запустить Instagram
- Сделать фотографию
- Наложить фильтр
- Придумать описание, добавить метки
- Опубликовать
Изначально мы имеет 15 шагов и при этом некоторые из-них могут содержать множество «вложенных». Благодаря Instagram сложность снизилась с 15+x действий до 5, а затраты времени снизились с 30–40 минут, до нескольких минут. Это колоссальный выигрыш.
Пример программы
Существует утилита NVM для разработчиков на Node.js. Она помогает устанавливать разные версии Node.js и переключаться между ними с помощью двух комманд install и use. Тот же самый процесс установки свежей версии Node.js на Ubuntu, требует как минимум трех шагов и двух принятий решений, а о переключении между версиями я и говорить не хочу.
При этом для работы NVM патчит файл ~/.profile
(чтобы загружаться каждый раз, как пользователь открывает консоль), для этого в нем содержится около тысячи строк достаточно сложного кода с множеством ветвлений для разных ОС. При этом в unix есть механизм стандартного расширения — это директория ~/.profile.d
, куда нужно складывать такие скрипты, тогда установка и удаление расширения занимает всего две строчки кода:
# Добавить
cp some-init-script.sh ~/.profile.d/
# Удалить
rm ~/.profile.d/some-init-script.sh
Конечно, не все системы включают поддержку ~/.profile.d
поэтому вы можете легко найти множество программ которые патчат ~/.profile
и каждый раз программисты тратят время на собственную реализацию такого патча для разных систем. Более того определить наличие патчей других разработчиков становится просто невозможно из-за обилия кастомных решений. Я называю это явление too custom, когда единое решение не может быть внедрено из-за чрезмерного количества ветвлений, которые нужно будет учесть.
Кстати, абсолютно такая же ситуация с автокомплитом. Многие программы не содержат скрипт автодополнения, потому что он требует прав супер-пользователя при установке, хотя иногда программа должна быть установлена от имени пользователя и тогда положить скрипт автодополнения можно в ~/.bash_completion.d
, но об этом мало кто знает.
Чтобы начать пользоваться веб-приложением, вам вообще не нужно ничего устанавливать. Время доставки стремится к нулю, как и количество принимаемых решений.
И т.д. и т.п.
Очень много сфер и компаний применяют этот принцип, перечислять можно очень долго DevOps, UX, PayPal, AppStore, iTunes, Github…
Мозг
Наш мозг тоже применяет снижение алгоритмической сложности. Наглядным примером является сленг и язык сам по себе. Вместо того, чтобы говорить «массивный газовый шар, излучающий свет и удерживаемый силами собственной гравитации и внутренним давлением, в недрах которого происходят (или происходили ранее) реакции термоядерного синтеза», мы просто говорим «звезда». Очевидно, что это не только экономит кучу времени, но и упрощает понимание. Возьмем простое предложение:
Солнце встает на Востоке
и развернем его
Массивный газовый шар, излучающий свет и удерживаемый силами собственной гравитации и внутренним давлением, в недрах которого происходят реакции термоядерного синтеза, являющийся центральным объектом Солнечной системы, появляется из-за линии горизонта со стороны света в направлении которой вращается планета Земля.
Более чем уверен, что вы читали второе предложение вскользь, вы ведь и так уже знаете о чем идет речь. Но что, если поменять примеры местами? Вам бы сначала пришлось разместить в памяти все эти понятия, разобраться в связях причастных оборотов и попробовать представить общий смысл прежде чем приступить к краткой версии.
Оценка сложности
Любой выбор — это увеличение сложности.
И так, как же оценить свой алгоритм. Необходимо составить подробный план действий пользователя от момента знакомства до начала использования программы или продукта, а так же проанализировать каждое действие. Для этого достаточно сделать следующее:
- Необходимо расписать каждый шаг даже с виду незначительный.
- Учесть компетенцию пользователя и внести получение этой компетенции в сложность алгоритма. Можно разделить пользователей по категориям.
- Каждый момент принятия решений пользователем должен быть проанализирован. Любой выбор усложняет алгоритм.
- Оцените ветвление алгоритма (все конструкции «если-то» создают новую ветку), если какие-то ветви являются наиболее часто-используемыми примените к ним механизм упрощения: придумайте термин, команду, сделайте отдельную кнопку или панель быстрого доступа/настроек/редактирования/добавления/удаления.
Не лишним будет учесть предыдущий опыт пользователя, так как переучиваться труднее, чем освоить новое. И теперь вы понимаете почему: сначала нужно ослабить старые связи в мозгу, а затем создать и укрепить новые, а это два действия, вместо одного.
После того как составлен план, нужно классифицировать действия и выбрать подходящий способ упрощения.
К сожалению в данный момент у меня нет списка универсальных способов снижения сложности, этот вопрос требует отдельного изучения, а применение каждого способа требует понимания специфики продукта. Хорошим примером является геймификация, которую можно определить как процесс упрощения освоения алгоритмов с более высокой сложностью за счет поэтапного увеличения сложности. Так же в своей работе я часто применяю изоляцию, сложные участки кода я выношу в отдельный проект, чтобы отсечь все лишнее и сконцентрироваться на главном, когда результат достигнут и все сложные связи распутаны или устранены, я переношу код.
Для того чтобы оценить результат нужно подсчитать затрачиваемые пользователем ресурсы: время, деньги и энергию. Учитывать желательно все: количество движений мыши, время ожидания, поиск нужной кнопки и т.п. Если в результате изменения алгоритма вы сэкономили какой-то ресурс или значительно улучшили полученный результат, значит вы на правильном пути. Если вы не можете определить ресурс, который пользователь получает в результате, значит, скорее всего, вы не понимаете что делаете на самом деле.
Надеюсь эта статья поможет вам в дальнейшем при разработке ваших продуктов. Рассчитываю на дальнейшее продолжение дискуссии для развития этой темы.