Пару лет назад я написал статью Размышления о структурном программировании, в которой пытался разобраться с заблуждением, будто Эдсгер Дейкстра доказал, что любой алгоритм можно построить всего из трех конструкций (следования, ветвления, цикла). Тогда как на самом деле:
Эту теорему доказали (с условиями и ограничениями) итальянские ученые Бём и Якопини, а Дейкстра лишь ссылался на неё.
Трех указанных базовых алгоритмических конструкций недостаточно для современного программирования (например, требуется обработка исключений).
Знаменитая критика оператора
gotoот Дейкстры (а также оператора присвоения, о чем всегда забывают рассказать) была не строгим запретом, а всего лишь рекомендацией по улучшению стиля кода, чтобы программы было легче анализировать.
А вот теперь настало время написать про некоторые проблемы машины Тьюринга - фундаментальной основы всех информационных технологий.
Зачем?
Поводом для текущей статьи стал мой диалог с одним пользователем на Reddit при обсуждении статьи Гарантии языка программирования как основа безопасной разработки программного обеспечения (там я тоже получаю обратную связь от читателей, как на Хабре, только от англоязычной аудитории).
Мне пытались доказать свою позицию, аргументируя её теоретическими выкладками на основе рассуждений о гипотетической машине Тьюринга, не понимая при этом некоторых её особенностей и ограничений. В результате мне стало очевидно, что, чтобы двигаться дальше, нужно обязательно прояснить и этот момент.
Что не так с машиной Тьюринга?
Машина Тьюринга - это фундамент информатики и теории вычислений, чисто абстрактный исполнитель (математическая модель вычислений), способный имитировать всех исполнителей (с помощью задания правил перехода), каким-либо образом реализующих процесс пошагового вычисления.
Каково бы ни было разумное понимание алгоритма, любой алгоритм, соответствующий такому пониманию, может быть реализован на машине Тьюринга.
Однако, как и любой математической абстракции, ей присущи некоторые ограничения и допущения, например наличие бесконечной ленты для хранения данных и игнорирование вопросов учета используемых ресурсов (объема памяти и времени выполнения программы), тогда как в реальном мире любой носитель информации конечен, да и время выполнения алгоритма тоже имеет значение, в связи с чем машина Тьюринга определяет теоретическую, а не практическую вычислимость.
Из-за этого машина Тьюринга (а следовательно, и любой компьютер, который мы можем себе представить) не позволяет сделать некоторые вещи:
Решить невычислимые задачи - те, для которых в принципе не существует алгоритма. Неважно, сколько времени или памяти мы дадим машине, она никогда не сможет гарантированно дать правильный ответ для всех возможных входных данных.
Невозможно создать программу, которая проанализирует любую другую программу и её входные данные и скажет, завершится ли эта программа когда-нибудь (остановится) или будет работать вечно (зациклится). Кстати, скорее всего, это и подтверждают слова Э. Дейкстры: «Тестирование выявляет только наличие, но никак не отсутствие ошибок».
Решить NP-трудные задачи за разумное время.
Адекватно моделировать определённые процессы. Машина Тьюринга - это всегда обработка входных данных с последующей остановкой и выдачей результата. Она не предназначена для моделирования постоянно работающих систем, которые взаимодействуют с внешним миром и используют прерывания.
Мысленный эксперимент "Китайская комната", показывает, что машина Тьюринга не позволяет "создать понимание" или "сознание" в человеческом смысле, она лишь симулирует его внешние проявления.
Конечно, все это не умаляет значимости машины Тьюринга, и её простота - это её сила, которая позволяет строго доказывать фундаментальные теоремы о возможностях и границах вычислений.
Если вам интересна эта тема, рекомендую почитать Surprisingly Turing-Complete · Gwern.net или её перевод Неожиданная полнота по Тьюрингу повсюду - Хабр.
Причём тут машина Тьюринга?
В контексте предыдущей статьи о Гарантиях языка программирования как основы безопасной разработки программного обеспечения, свойства Тьюринг-полных языков программирования позволяют сделать следующие выводы:
Любой язык программирования, обеспечивающий гарантии безопасной разработки обязан ограничивать возможности реализации некоторых алгоритмов (как минимум таких, которые содержат ошибки), а это означает, что любой безопасный язык программирования не может быть Тьюринг-полным по определению (раз на нем нельзя написать программу с ошибкой).
Интересен и обратный вывод: полнота языка программирования по Тьюрингу является достаточным основанием, чтобы такой язык программирования нельзя было отнести к безопасным :-)
Sirion
Есть ощущение, что вы путаете тёплое с мягким. Тьюринг-полнота - это про вычисления, а безопасность - это, как правило, про изменение состояния.
rsashka Автор
Про вычисления речь не идет вообще, так как машина Тьюринга, это еще и возможность записи этой самой программы (на бесконечной ленте), тогда как гарантия безопасной разработки на уровне синтаксиса языка программирования, это невозможность записать не безопасный код.