Интересная статья 2020 года: "What makes a rule complex?". Что делает правило (алгоритм) сложным для выполнения человеческим мозгом?1
Мы часто оцениваем стоимость выполнения алгоритма: на какой-то абстрактной машине, или на реальном железе (конечном автомате, машине Тьюринга, Intel Core i7…). А что насчет человеческого мозга? Если смотреть на мозг как вычислительную машину, и скармливать ему разные "программы" – описания алгоритмов на человеческом языке – то насколько "дорого" для мозга будет выполнить действия соответственно описанию? Как цена выполнения алгоритма мозгом будет зависеть от устройства алгоритма?
Это важно для многих задач: огромное количество вещей на планете все еще делается живыми людьми, а не роботами, и эти люди часто должны действовать по некоторым правилам, процедурам – т.е. алгоритмам. Если знать, насколько сложно для человеческого мозга реализовывать разные алгоритмы, то можно подбирать такие протоколы, которые людям будет выполнять проще.
Лично мне этот вопрос интересен в контексте разработки: хотя программисты и пишут алгоритмы для компьютера, но нам постоянно приходится выполнять их своими мозгами. И когда я пишу код, и когда читаю – я практически постоянно выполняю этот код в голове.
Похожая картина и с документацией: заметная часть документации состоит из описания процедур. Операционные инструкции, описание взаимодействия частей приложения, описание API – везде есть какие-то процедуры, алгоритмы.
И одни правила-процедуры-алгоритмы мозг легко понимает и выполняет, а другие – заметно скрипит, пытаясь выполнить, и часто ошибается. Интересно, чем "простые" процедуры отличаются от "трудных" – особенно интересно потому, что одну и ту же процедуру часто достаточно чуть иначе сформулировать, и она станет восприниматься легче или тяжелее.
(Практически идея mechanicalneurological sympathy: один и тот же алгоритм может выполняться быстрее или медленнее в зависимости от того, насколько хорошо конкретная реализация ложится на детали конкретного исполняющего устройства)
Количество состояний – доминирующий фактор
Автор исследования давал испытуемым словесное описание алгоритма, и просил назначить цену (в долларах), за которую они готовы выполнить этот алгоритм на случайно сгенерированных входных данных2. БОльшая часть алгоритмов – оформлены в виде конечных автоматов, хотя была пара и более сложных примеров3.
Основные результаты такие:
Количество внутренних состояний (конечного автомата) – дает наибольший вклад в "стоимость" алгоритма для мозга. Каждое дополнительное состояние делает алгоритм примерно на 20-30% дороже для исполнения человеком: простейший алгоритм с 4 состояниями примерно вдвое дороже, чем алгоритм с 1 состоянием. Количество состояний больше всех остальных факторов влияет на время исполнения алгоритма, и на вероятность ошибок при исполнении4.
Сильно удешевляет алгоритм для человека наличие абсорбирующего состояния. Абсорбирующее состояние примерно эквивалентно удалению одного состояния (-20..30% стоимости): алгоритм с 4 внутренними состояниями, одно из которых абсорбирующее, примерно равен по стоимости исполнения алгоритму с 3 внутренними состояниями.
Количество переходов – последний по значимости фактор, сильно уступающий числу состояний. Добавление в алгоритм дополнительного перехода примерно вдвое дешевле (10-15%), чем добавление дополнительного состояния. До кучи: количество переходов мало влияет на время исполнения алгоритма, и на вероятность ошибок в нем.
Результат про абсорбирующие состояния у меня ассоциируется с early return pattern: если рассматривать блоки внутри метода как "состояния", то return делает такой блок "абсорбирующим состоянием" – из него нет переходов в другие блоки внутри метода. И если абсорбирующие состояния удешевляют алгоритм для человека – то и понятно, почему код с early return считается более читабельным.
Помимо этих основных результатов есть еще несколько вторичных
Мозг сам не оптимизирует
По-видимому, интуитивно мозг не делает упрощение (редукцию) алгоритма, даже если оно возможно: автор предлагал испытуемым "упрощаемые" конечные автоматы, но испытуемые не оценивали их сколько-либо дешевле, чем неупрощаемые.
Уточню, что испытуемых не просили упростить алгоритм – испытуемым давали алгоритм, и просили его выполнить. Алгоритмы в рамках исследования не очень сложные, и будь явно поставлена задача "найдите возможность упрощения" – уверен, многие ее увидели бы.
Но вопрос был в другом: ощущает ли человек интуитивно, что некоторые алгоритмы можно выполнить более дешевым способом? По-видимому – нет, интуитивно мозг не догадывается даже до самых простых оптимизаций, вроде "склеить три идентичных состояния с идентичными переходами подряд в одно состояние".
Интересно, как это может проявляться: например, я пишу в инструкции длинную процедуру с повторяющимися действиями – в надежде, что читатели смогут сами заметить общий паттерн, и сократить лишние части. Может быть и смогут, но – судя по результатам исследования – особо надеяться на это не стоит. Скорее всего читатели будут выполнять действия так, как я их опишу – как бы неэффективно это ни было. Вероятно, будет эффективнее написать несколько вариантов инструкции – полную, и сокращенную – и в предисловии объяснить, когда какой пользоваться.
Привыкание удешевляет алгоритм, но эффект хрупок
Очевидный вопрос: уменьшается ли субъективная цена исполнения алгоритма, если человек с этим алгоритмом хорошо знаком – уже много раз его выполнял? И да, такой эффект есть: алгоритмы "дешевеют" по мере привыкания к ним испытуемых. Эффект очень большой: знакомый алгоритм дешевеет для человека почти в полтора раза – эквивалентно сокращению его на 2 состояния.
Конечно, и без исследований понятно, что знакомые правила выполнять легче. Но что интересно – что этот эффект оказывается очень хрупок. Оказывается, достаточно чуть-чуть изменить описание алгоритма, без изменения сути – буквально поменять буквы, которыми обозначаются состояния – и вместо 40-50% снижения стоимости получается всего лишь 20%.
Я часто встречаюсь со сложно организованным кодом, который автор не считает сложным, потому что код ему хорошо знаком (нередко автор – это я). Результат исследования говорит, что такое знакомство действительно сильно упрощает восприятие сложного алгоритма – но полагаться на это не стоит, эффект легко может исчезнуть. Коллега внесет в ваш алгоритм мелкие, несущественные правки, и ранее знакомый алгоритм перестанет ощущаться знакомым, бонус к пониманию исчезнет – а алгоритм останется, и с ним придется разбираться.
(Скорее всего, даже не обязательно, чтобы код кто-то трогал: в статье нет ничего про забывание, но логично предположить, что "знакомство" с алгоритмом и само выветривается из головы со временем)
Мозг умеет не только конечные автоматы
Интересный вопрос: можно ли упростить алгоритм для человека, если представить его не как конечный автомат (состояния+переходы), а как что-то более мощное, но и более компактное?
Например, возьмем алгоритм "если 3 последних символа во входном потоке были А, то вывести А, иначе Б". У этого алгоритма всего 2 состояния, но еще нужна ячейка памяти для хранения числа встреченных А – т.е. это уже не конечный автомат, это автомат с памятью (pushback automata). Можно переформулировать этот алгоритм как конечный автомат, но тогда у него будет 4 состояния. Что для человека будет дешевле: реализовать алгоритм, описанный как конечный автомат с 4 состояниями – или реализовать его же в формулировке автомата с памятью? Или стоимость будет одинаковая?
Эксперименты показали, что испытуемые явно предпочитают более компактную но и более сложную формулировку автомата с памятью – стоимость реализации такого алгоритма лишь чуть-чуть выше стоимости обычного конечного автомата с 2 состояниями. И так же время исполнения и число ошибок сильно меньше для более компактной версии, чем для более простой но развесистой. То есть добавить ячейку с числом встреченных А мозгу стоит очень дешево.
Снова уточню: вопрос не в том, способен ли мозг вообще выполнять алгоритмы, более сложные, чем конечные автоматы – конечно способен, мозг почти наверняка Тьюринг-полный вычислитель. Вопрос в другом: выполняет ли мозг алгоритм для более мощного вычислителя эффективно? И ответ – да, как минимум автоматы с памятью мозг способен исполнять почти так же эффективно, как и просто конечные автоматы. Поэтому если алгоритм можно сформулировать с меньшим числом состояний и переходов, но нужна рабочая память – такой алгоритм, скорее всего, будет для человека проще, чем алгоритм с бОльшим числом состояний и переходов, но без памяти.
Получается, что слишком большое стремление к упрощению процедур может на самом деле иметь обратный эффект.
Например, я сталкивался с инструкциями, которые автор явно стремился написать так подробно, чтобы даже идиот понял. Эффект часто обратный: описание в пару страниц длиной воспринимается тяжело уже за счет одного размера – даже если каждый конкретный шаг тривиален. Та же инструкция, переформулированная "для нормального человека", сокращается в 3-4 раза, и гораздо легче понимается, запоминается, и выполняется.
Этот результат еще хорошо вяжется с предыдущим результатом о том, что мозг сам по себе, интуитивно – не выполняет упрощение алгоритмов. То есть если я пишу очень подробную инструкцию "для идиотов" в надежде на то, что не-идиоты сами сообразят, как ее упростить – то это вряд ли. Весьма вероятно, что даже умные люди далеко не сразу сообразят, что инструкция допускает упрощение, и долгое время будут работать с ней так, как она написана, и воспринимать ее сильно более сложной, чем она по-сути есть.
Примечания
Oprea, R. "What Makes a Rule Complex?” American Economic Review, 2020, 110:12, 3913-3951 (pdf). В статье много всего интересного, но – 40 страниц это 40 страниц.↩︎
Это очень грубое описание: на самом деле автор давал испытуемым сначала познакомиться с алгоритмами, выполняя разные алгоритмы за фиксированную цену, а потом предлагал пару алгоритмов разной сложности, и просил назначить дополнительную цену, за которую испытуемый готов исполнять более сложный из двух. И использовал идею аукциона Викри чтобы вытащить подлинную субъективную "цену". В общем, детальное описание экспериментальной процедуры и анализа результатов занимает страниц 20, и там немало тонкостей.↩︎
Использованные в статье "алгоритмы" могут выглядеть примитивно для программистов, но испытуемые не были программистами – задача была оценить "обычный" человеческий мозг, а не человеческий мозг, который приспособился к решению таких задач за 10-15 лет ежедневной практики.↩︎
В IT мы чаще используем время исполнения (число инструкций) как метрику сложности алгоритма – но автор берет за главную метрику субъективную цену, а время исполнения (и число ошибок) приводит как второстепенные метрики. Если искать IT-аналогию, то аналогом "цены" будет потраченная на выполнение алгоритма энергия – предполагая, что человеческий мозг рационально оценивает затраты.
Цена выбрана главной метрикой сложности алгоритма потому, что она показывает насколько сильно человек не захочет выполнять этот алгоритм. Кроме того время исполнения – более зашумленная метрика, ведь сложно проконтролировать, что испытуемые во время работы ни на что постороннее не отвлекались.↩︎
Интересная статья, спасибо.
ОтветитьУдалитьЕще одно подтверждение того что ООП является ненужным усложнением, со всеми его состояниями, классами и т.д, в отличие от ФП