Алгоритмическая сложность или как сделать удобным все

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

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

То о чем я говорю — это сама суть информационных технологий. Артемий Лебедев попробовал описать этот принцип, но получилось вот что.

Ключом к успеху является снижение сложности. Чем-то это напоминает смесь из принципов KISS и ТРИЗ.


Примеры

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


Успех Instagram

Я давно занимаюсь фотографией и хорошо представляю себе процесс превращения снимка в продукт готовый к потреблению. Давайте перечислим шаги которые осуществляет пользователь для того чтобы поделиться красивой фотографией. Для этого он должен:


  1. Выгрузить снимок из фотоаппарата на компьютер:
    1. Физически подключить фотоаппарат к компьютеру/вставить карту памяти
    2. Открыть папку с фотографиями
    3. Создать папку назначения на компьютере
    4. Переместить файлы
  2. Выбрать подходящий снимок:
    1. Открыть программу-просмотрщик
    2. Отобрать лучшие снимки
    3. Выбрать один-два из понравившихся
    4. Конвертировать в формат подходящий для редактирования
  3. Обработать фотографию
    1. Запустить фоторедактор
    2. Перенести фотографию в фоторедактор
    3. Провести обработку (здесь количество вложенных шагов варьируется и может достигать нескольких десятков, если не сотен)
    4. Сохранить обработанный дубликат
  4. Поделиться результатом
    1. Выбрать подходящую площадку: flickr, 500 px, vk, fb, tumblr.
    2. Загрузить фотографию в альбом или коллекцию, поместить на стене/ленте (зависит от сервиса).
    3. Придумать название или описание, указать теги (опционально).

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

Теперь давайте взглянем на этот процесс с точки зрения пользователя Instagram:


  1. Запустить Instagram
  2. Сделать фотографию
  3. Наложить фильтр
  4. Придумать описание, добавить метки
  5. Опубликовать

Изначально мы имеет 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…


Мозг

Наш мозг тоже применяет снижение алгоритмической сложности. Наглядным примером является сленг и язык сам по себе. Вместо того, чтобы говорить «массивный газовый шар, излучающий свет и удерживаемый силами собственной гравитации и внутренним давлением, в недрах которого происходят (или происходили ранее) реакции термоядерного синтеза», мы просто говорим «звезда». Очевидно, что это не только экономит кучу времени, но и упрощает понимание. Возьмем простое предложение:


Солнце встает на Востоке

и развернем его


Массивный газовый шар, излучающий свет и удерживаемый силами собственной гравитации и внутренним давлением, в недрах которого происходят реакции термоядерного синтеза, являющийся центральным объектом Солнечной системы, появляется из-за линии горизонта со стороны света в направлении которой вращается планета Земля.

Более чем уверен, что вы читали второе предложение вскользь, вы ведь и так уже знаете о чем идет речь. Но что, если поменять примеры местами? Вам бы сначала пришлось разместить в памяти все эти понятия, разобраться в связях причастных оборотов и попробовать представить общий смысл прежде чем приступить к краткой версии.


Оценка сложности


Любой выбор — это увеличение сложности.

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


  • Необходимо расписать каждый шаг даже с виду незначительный.
  • Учесть компетенцию пользователя и внести получение этой компетенции в сложность алгоритма. Можно разделить пользователей по категориям.
  • Каждый момент принятия решений пользователем должен быть проанализирован. Любой выбор усложняет алгоритм.
  • Оцените ветвление алгоритма (все конструкции «если-то» создают новую ветку), если какие-то ветви являются наиболее часто-используемыми примените к ним механизм упрощения: придумайте термин, команду, сделайте отдельную кнопку или панель быстрого доступа/настроек/редактирования/добавления/удаления.

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

После того как составлен план, нужно классифицировать действия и выбрать подходящий способ упрощения.

К сожалению в данный момент у меня нет списка универсальных способов снижения сложности, этот вопрос требует отдельного изучения, а применение каждого способа требует понимания специфики продукта. Хорошим примером является геймификация, которую можно определить как процесс упрощения освоения алгоритмов с более высокой сложностью за счет поэтапного увеличения сложности. Так же в своей работе я часто применяю изоляцию, сложные участки кода я выношу в отдельный проект, чтобы отсечь все лишнее и сконцентрироваться на главном, когда результат достигнут и все сложные связи распутаны или устранены, я переношу код.

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

Надеюсь эта статья поможет вам в дальнейшем при разработке ваших продуктов. Рассчитываю на дальнейшее продолжение дискуссии для развития этой темы.

© Habrahabr.ru