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

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

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

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

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

Примеры

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

Успех Instagram

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

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

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

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

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

Изначально мы имеет 15 шагов и при этом некоторые из-них могут содержать множество "вложенных". Благодаря Instagram сложность снизилась с O(15+x) до O(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, но об этом мало кто знает.

NPM и Rubygems vs apt-get

Если посмотреть на менеджеры пакетов в linux, например apt-get, то мы можем увидеть точно такую же закономерность: одномерные списки NPM и Rubygems в разы удобнее распределенных и разветвленных apt-get. Я искренне не понимаю, почему apt не содержит команды для просмотра установленных пакетов, а вместо этого я должен вызывать

dpkg --get-selections | grep -v deinstall

Это не просто не удобно, это отвратительно. Менеджер пакетов не делает то что должен делать из коробки, заставляя меня помнить дополнительную команду, причем достаточно длинную, чтобы несколько раз ошибиться при наборе и не поддающуюся автодополнению.

Да, я сейчас замахнулся на святая святых и кто-то уже собрался жечь меня на костре, но подождите, в MacOS есть директория /Applications и ~/Applications куда складываются программы вообще без использования каких-либо менеджеров – перетянул программу мышкой и работай. Установки требуют только программы, нуждающиеся в исключительных правах доступа или изменяющих что-то внутри системы. Я думаю мало кто поспорит, что MacOS сегодня одна из самых удобных ОС.

Почему веб-приложения будут доминировать

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

И т.д. и т.п.

Очень много сфер и компаний применяют этот принцип, перечислять можно очень долго DevOps, UX, PayPal, AppStore, iTunes, Github…

Мозг тоже делает это

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

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

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

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

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

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

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

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

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

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

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

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

Оценка эффективности

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

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

ссылка на оригинал статьи https://habrahabr.ru/post/283144/

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *