Методы Кристобаля Хунты: различия между версиями

м
Правки
м (Правки)
:''Исходный вариант статьи ([[w:Непейвода Николай Николаевич|Н. Н.  Непейвода]], «Методы Кристобаля Хунты») опубликован в [[Журнал «Потенциал»|журнале «Потенциал»]].''
 
{{Эпиграф|— Голубчики, — сказал Фёдор Симеонович озабоченно, разобравшись в почерках. — Это же проблема Бен Бецалеля. Калиостро же доказал, что она не имеет решения.
 
— Мы сами знаем, что она не имеет решения, — сказал Хунта, немедленно ощетиниваясь. — Мы хотим знать, как её решать.<br />
— Как-то странно ты рассуждаешь, Кристо... Как же искать решение, когда его нет? Бессмыслица какая-то...<br />
— Извини, Теодор, но это ты очень странно рассуждаешь. Бессмыслица — искать решение, если оно и так есть. Речь идёт о том, как поступать с задачей, которая решения не имеет. Это глубоко принципиальный вопрос...
|А. и Б. Стругацкие. «Понедельник начинается в субботу»}}
 
— Как-то странно ты рассуждаешь, Кристо… Как же искать решение, когда его нет? Бессмыслица какая-то…
==В чём прав и в чём неправ Кристобаль Хозевич?==
Конечно же, с позиций некоего высшего знания Кристобаль Хунта прав: зачем решать разрешимую задачу, если уже известно, что у неё есть решение? Но нельзя забывать, что он — бессмертный маг высшего класса, бывший Великий Инквизитор и прочее, и прочее. Он уже умеет щелкать элементарные задачи, и многие задачи, недоступные для Вас, являются элементарными для него. А вам нужно участвовать в олимпиадах и конкурсах, учиться, а затем работать на производстве, где подавляющее большинство задач именно разрешимые (в частности, на традиционных олимпиадах других и не дают). Так что знать способы решения таких задач и уметь реализовывать соответствующие алгоритмы — важнейшие знания и умения для тех, кто желает быть выше «чайника» в информатике.
 
— Извини, Теодор, но это ты очень странно рассуждаешь. Бессмыслица — искать решение, если оно и так есть. Речь идёт о том, как поступать с задачей, которая решения не имеет. Это глубоко принципиальный вопрос…
Профессионал порою сталкивается и с совершенно другой ситуацией. Ему ставят задачу, которая заведомо неразрешима. Но её надо решать (это лишь один из многих подобных случаев в реальной жизни: прочитайте книгу [1].
|[[w:Братья Стругацкие|Аркадий и Борис Стругацкие]]. [[w:Понедельник начинается в субботу|Понедельник начинается в субботу]]}}
 
== В чём прав и в чём неправ Кристобаль Хозевич? ==
Что же делать? Решать! Но решать, зная, а не забывая, что она неразрешима. Намеренное либо самоуверенное невежество в данном случае обязательно приведёт к краху. В своё время автор слышал доклад выдающегося математика (тогда российского) Б. А. Трахтенброта «Как решать неразрешимые задачи?» (1979 г., ныне грузинский, а тогда советский, город Телави). Когда через пару лет автору пришлось составлять обзор по семантике языков программирования, он натолкнулся на французскую статью под лихим названием «Indecidable? Sur pas!» («Неразрешимо? Наплевать!») и стало ясно, что такие вещи, как решение неразрешимого, формализация неформализуемого и прочее типичны для человеческой деятельности, как и замечали братья Стругацкие устами Кристобаля Хунты.
 
Конечно же, с позиций некоего высшего знания Кристобаль Хунта прав: зачем решать разрешимую задачу, если уже известно, что у неё есть решение? Но нельзя забывать, что он — бессмертный маг высшего класса, бывший Великий Инквизитор и прочее, и прочее. Он уже умеет щёлкать элементарные задачи, и многие задачи, недоступные для вас, являются элементарными для него. А вам нужно участвовать в олимпиадах и конкурсах, учиться, а затем работать на производстве, где подавляющее большинство задач именно разрешимые (в частности, на традиционных олимпиадах других и не дают). Так что знать способы решения таких задач и уметь реализовывать соответствующие алгоритмы — важнейшие знания и умения для тех, кто желает быть выше «чайника» в [[w:Информатика|информатике]].
==Первый способ: заранее сделать все правильно==
 
Допустим, что вам ставится задача построить программу, которая будет проверять, зациклятся ли программы, создаваемые при помощи новой системы программирования для каких-то частных приложений (например, нового языка скриптов для собачьих ошейников). Тогда нужно прежде всего понять, какого же сорта программы будут писать на данной системе, поскольку, как говорилось в прошлой статье, поставленная задача в общем случае неразрешима. Если программы не слишком сложные, достаточно всего-навсего выбросить из создаваемого мини-языка программирования циклы <code>while</code> и рекурсивные процедуры (которые, если явно не протестовать, системные программисты ради общности и крутизны обязательно вставят, поскольку «иначе система не будет универсальной»). Тогда всякая программа будет заканчивать работу (просто по построению не даём мы
Профессионал порою сталкивается и с совершенно другой ситуацией. Ему ставят задачу, которая заведомо неразрешима. Но её надо решать (это лишь один из многих подобных случаев в реальной жизни: прочитайте книгу <ref>Э. Йордан. Путь камикадзе. Как разработчику программного обеспечения выжить в безнадежном проекте. М.: ЛОРИ, 2003.</ref>.
 
Что же делать? Решать! Но решать, зная, а не забывая, что она неразрешима. Намеренное либо самоуверенное невежество в данном случае обязательно приведёт к краху. В своё время автор слышал доклад выдающегося математика [[w:Трахтенброт, Борис Авраамович|Бориса Авраамовича Трахтенброта]] «Как решать неразрешимые задачи?» (1979 год, город [[w:Телави|Телави]]). Когда через пару лет автору пришлось составлять обзор по семантике языков программирования, он натолкнулся на французскую статью под лихим названием «Indecidable? Sur pas!» («Неразрешимо? Наплевать!») и стало ясно, что такие вещи, как решение неразрешимого, формализация неформализуемого и прочее типичны для человеческой деятельности, как и замечали братья Стругацкие устами Кристобаля Хунты.
 
== Первый способ: заранее сделать всё правильно ==
 
Допустим, что вам ставится задача построить программу, которая будет проверять, зациклятся ли программы, создаваемые при помощи новой системы программирования для каких-то частных приложений (например, нового [[w:Скриптовый язык|языка скриптов]] для собачьих ошейников). Тогда нужно прежде всего понять, какого же сорта программы будут писать на данной системе, поскольку, как говорилось в прошлой статье, поставленная задача в общем случае неразрешима. Если программы не слишком сложные, достаточно всего-навсего выбросить из создаваемого мини-языка программирования циклы <code>while</code> и рекурсивные процедуры (которые, если явно не протестовать, системные программисты ради общности и крутизны обязательно вставят, поскольку «иначе система не будет универсальной»). Тогда всякая программа будет заканчивать работу (просто по построению не даём мы
ей возможности зациклиться!). Если системщики вопят, а начальство смотрит на них как на полубогов, то заставьте их хотя бы выдавать для программистов длинные предупреждения типа:
 
<code>Использование цикла while может привести программу к зацикливанию.
{{Рамка}}
Уверены ли вы, что нужно использовать цикл while?
<code>Использование цикла while может привести программу к зацикливанию.
(ответ No по умолчанию)</code>
Уверены ли вы, что нужно использовать цикл while?
(ответ No по умолчанию)</code>
{{Акмар}}
 
А после ответа <code>Yes</code> вдобавок ещё (в лучших традициях игр, из которых Вамвам хочется выйти побыстрее, чтобы шеф, либо мама, либо преподаватель не застукали, а вам задают кучу вопросов):
 
 
{{Рамка}}
<code>В большинстве случаев то, что может сделать цикл while, может cделать и цикл for. Не хотите ли Вы использовать цикл for? (ответ Yes по умолчанию)</code>
Не хотите ли вы использовать цикл for?
{{Акмар}}
(ответ Yes по умолчанию)</code>
 
И как финальный аккорд нечто типа:
 
<code>ОК. Ваша программа помечена как не сертифицируемая на корректность.
{{Рамка}}
<code>ОК. Ваша программа помечена как не сертифицируемая на корректность.<br> Желаем успехов.</code>
{{Акмар}}
 
Приведённое решение является частным случаем трёх общих принципов.
 
=== Принцип 1 ===
 
Если нельзя решить задачу в полном объёме, разберитесь, что же на самом деле нужно, и решите частную задачу.
разберитесь, что же на самом деле нужно, и решите частную задачу.
Если задачу можно решить в полном объёме, тем не менее подумайте, какого частного
случая на самом
деле достаточно, и тогда Ваше решение станет намного лучше.{{Ref|3}}
 
Если задачу можно решить в полном объёме, тем не менее подумайте, какого частного
случая на самом деле достаточно, и тогда ваше решение станет намного лучше.{{Ref|3}}
 
=== Принцип 2 ===
 
Если нечто нужно гарантировать для решения, лучший способ — обеспечить это с самого начала, ещё в ходе построения программы. Потом даже проверить будет трудно, а если ещё вдобавок выяснится, что требование нарушено, то переделывать будет ещё труднее, чем писать хорошо сразу.{{Ref|4}}
самого начала, ещё в ходе построения программы. Потом даже проверить будет трудно, а если ещё вдобавок выяснится, что требование нарушено, то переделывать будет ещё труднее, чем писать хорошо сразу.{{Ref|4}}
 
=== Принцип 3 ===
 
Если вам не удаётся заблокировать неразумное решение, при помощи технических и бюрократических частностей сделайте так, чтобы пользоваться им было как можно более противно.{{Ref|5}}
===Принцип 3===
 
Этот способ решения неразрешимых задач можно проиллюстрировать следующим рисунком.
Если вам не удаётся заблокировать неразумное решение,
при помощи технических и бюрократических частностей сделайте так, чтобы
пользоваться им было как можно более противно.{{Ref|5}}
 
Этот способ решения неразрешимых задач можно проиллюстрировать следующим рисунком.
 
То, что за забором, — наши культурные растения, а снаружи все считается сорняками и там мы просто не сажаем.
 
== Второй способ: ограничить задачу ==
Приведённый выше принцип 1 сразу же приводит к ещё одному методу решения, на самом деле неразрывно связанному с предыдущим, но не сводящемуся к нему. В частности, если те же программы для собачьих ошейников уже написаны, либо пишутся законченными хакерами, которым хоть кол на голове теши, но от крутизны они не откажутся, то остается посмотреть на данную конкретную совокупность программ и строить алгоритм, который будет работать именно на ней{{Ref|6}}. Главное, чтобы Ваш алгоритм успешно искал ошибки в конкретных программах, а то, что он в общем случае некорректен, стоит рассматривать как неизбежное зло.
 
Приведённый выше [[#Принцип 1| принцип 1]] сразу же приводит к ещё одному методу решения, на самом деле неразрывно связанному с предыдущим, но не сводящемуся к нему. В частности, если те же программы для собачьих ошейников уже написаны, либо пишутся законченными хакерами, которым хоть кол на голове теши, но от крутизны они не откажутся, то остаётся посмотреть на данную конкретную совокупность программ и строить алгоритм, который будет работать именно на ней{{Ref|6}}. Главное, чтобы ваш алгоритм успешно искал ошибки в конкретных программах, а то, что он в общем случае некорректен, стоит рассматривать как неизбежное зло.
Например, если вы замечаете, что программисты часто опрашивают один и тот же датчик, не проведя повторного измерения, то такую ошибку можно успешно вылавливать и сообщать о ней, скажем, так:
 
Например, если вы замечаете, что программисты часто опрашивают один и тот же датчик, не проведя повторного измерения, то такую ошибку можно успешно вылавливать и сообщать о ней, скажем, так:
{{Рамка}}
 
<code>«Давление повторно не измерялось перед очередной его проверкой в строке 1221.<br>Ваша программа зацикливается.»</code>
<code>Давление повторно не измерялось перед очередной его проверкой в строке 1221.
{{Акмар}}
Ваша программа зацикливается.</code>
 
Схематически можно изобразить такой способ работы на следующем рисунке. То, что за колючей проволокой, по определению считается плохим и отстреливается.
 
== Третий способ: работать неправильно ==
 
Поскольку неразрешимую задачу всё равно правильно не решишь, то нужно проанализировать, какие же ошибки вреднее, и, если уж делать, то безвредные. В предыдущих разделах предполагалось, что безвреднее не найти зацикливание, чем найти зацикливание в работающей программе. Но, порою, лучше выдать лишнее предупреждение, чем пропустить заведомо плохой алгоритм. Далее, порою можно допускать ошибки и в две стороны, если цена ошибки не очень высока и в большинстве случаев программа все равно работает правильно. Схематически можно изобразить такой способ работы на следующем рисунке. Здесь граница проведена попроще, но возле её могут быть ошибки в две стороны.
Поскольку неразрешимую задачу всё равно правильно не решишь, то нужно проанализировать, какие же ошибки вреднее, и, если уж делать, то безвредные. В предыдущих разделах предполагалось, что безвреднее не найти зацикливание, чем найти зацикливание в работающей программе. Но, порою, лучше выдать лишнее предупреждение, чем пропустить заведомо плохой алгоритм. Далее, порою можно допускать ошибки и в две стороны, если цена ошибки не очень высока и в большинстве случаев программа все равно работает правильно.
 
Схематически можно изобразить такой способ работы на следующем рисунке. Здесь граница проведена попроще, но возле её могут быть ошибки в две стороны.
 
Например, автор пользовался простым, но неправильным, алгоритмом быстрого нахождения возможного (формального либо фактического) зацикливания в программах студентов.
В подавляющем большинстве случаев это правило действительно указывало на плохо продуманные процедуры, которые, даже если и работали для совсем простых входных данных, разваливались на чуть более сложных. Но иногда такой приём оправдан (лично я советую вам таким приёмом не пользоваться; если хочется скрестить рекурсию и цикл, значит, почти наверняка вы плохо продумали структуры данных либо алгоритм).
 
== Четвёртый способ: работать наугад ==
 
Самые распространённые программы проверки программ пытаются прогнать программу на множестве подобранных тестах, которые должны вроде бы обеспечить прохождение всех возможных путей исполнения программы. Но возможных путей слишком много, поэтому тесты подбираются так, чтобы пройти разные пути, и чаще всего случайным образом.
Самые распространённые программы проверки программ пытаются прогнать программу на множестве подобранных тестах, которые должны вроде бы обеспечить прохождение всех возможных путей исполнения программы. Но возможных путей слишком много, поэтому тесты подбираются так, чтобы пройти разные пути, и чаще всего случайным образом.
 
Аналогично, один из способов ручного тестирования программы — генерация случайных данных и проверка того, как программа будет себя вести на них.
 
На самом деле эти приёмы имеют под собой теоретическую подоплёку, которую впервые заметил Б. А. Трахтенброт в уже упоминавшемся докладе. Часто неразрешимая задача превращается в достаточно простую, если мы разрешим ошибаться с малой вероятностью (в принципе даже со сколь угодно малой).
 
В качестве примера труднейшей (на практике) задачи, решение которой значительно облегчается переходом к случайному алгоритму, рассмотрим следующую, которую автор вместе с группой товарищей по общежитию исследовал ещё в студенческие годы.
 
'''Проблема'''.
 
В стратегических шахматах два шахматиста общаются через судью. Судья единственный, кто знает полное положение на доске. Каждый игрок знает лишь соотношение сил и положение своих фигур.
 
Судья сообщает игрокам минимально необходимую информацию о ходе. Он произносит примерно такую последовательность фраз в ответ на попытки одного из игроков сделать ход (черные это были или белые, догадайтесь сами):<br> — Ход невозможен.<br> — Ход невозможен.<br> — Ход невозможен.<br> — Ход сделан. Взята ладья на поле e1. Пешка превратилась в ферзя. Шах.
 
— Ход невозможен.
Первый возможный ход считается сделанным. Причина, по которой невозможен ход (открывается шах, перекрыта линия, король пошёл под удар и т. п.) не объясняется. Стратегические шахматы порождают ряд интереснейших задач, пару из которых (одна из них более простая, вторая — исключительно сложная) приведём сейчас:
 
— Ход невозможен.
''' Король-всезнайка против ферзя.'''
 
— Ход невозможен.
Король знает всё, а король и ферзь играют против него по правилам стратегических шахмат. Дать мат.
 
— Ход сделан. Взята ладья на поле e1. Пешка превратилась в ферзя. Шах.
''' Король-всезнайка против ладьи.'''
 
Первый возможный ход считается сделанным. Причина, по которой невозможен ход (открывается шах, перекрыта линия, король пошёл под удар и тому подобное) не объясняется. Стратегические шахматы порождают ряд интереснейших задач, пару из которых (одна из них более простая, вторая — исключительно сложная) приведём сейчас:
 
'''Король-всезнайка против ферзя'''
 
Король знает всё, а король и ферзь играют против него по правилам стратегических шахмат. Дать мат.
 
'''Король-всезнайка против ладьи'''
 
Король знает все, а король и ладья играют против него по правилам стратегических шахмат. Дать мат.
Во второй задаче достаточно просто дать алгоритм и составить программу, которая будет случайно матовать короля таким образом, что с вероятностью 1 в конце концов его заматует (но теоретически король, если он не только всезнайка, но ещё и прорицатель, знающий будущее, может бесконечно избегать мата). Есть и алгоритм, матующий с гарантией, но он намного сложнее и в разработке, и в реализации.
 
Желающие могут попробовать поломать голову над задачей и присылать решения автору через редакцию журнала.
 
Перечисленные методы лишь немногие из тех, которые мог бы предложить его преподобие магистр магии Кристобаль Хозевич Хунта, но автор, конечно же, не может равняться со столь почтенным персонажем, и умолкает.
 
----
И напоследок замечание о книге [2], из которой взят эпиграф. Перефразируя античную пословицу, тот, кто её не читал, верблюд. Вообще, фантастика времени расцвета содержит намного больше идей и в большинстве намного интереснее, чем нынешняя штампованная фэнтези (в этом с удивлением убеждались ученики автора, которым он настоятельно советовал почитать «старьё» типа Стругацких, Шекли, Азимова; ещё раз напоминаем, что классика не стареет, и в этом её отличие от поделок).
 
И напоследок замечание о книге <ref>Аркадий Стругацкий, Борис Стругацкий. Понедельник начинается в субботу. М.: Детгиз, 1964 (имеется ряд переизданий, начиная с 80-х годов).</ref>, из которой взят эпиграф. Перефразируя античную пословицу, тот, кто её не читал, верблюд. Вообще, фантастика времени расцвета содержит намного больше идей и в большинстве намного интереснее, чем нынешняя штампованная фэнтези (в этом с удивлением убеждались ученики автора, которым он настоятельно советовал почитать «старьё» типа Стругацких, Шекли, Азимова; ещё раз напоминаем, что классика не стареет, и в этом её отличие от поделок).
==Дальнейшее чтение==
 
# Э. Йордан. Путь камикадзе. Как разработчику программного обеспечения выжить в безнадежном проекте.ЛОРИ, М.: 2003
== Дальнейшее чтение ==
# Аркадий Стругацкий, Борис Стругацкий. Понедельник начинается в субботу. М.: Детгиз, 1964 (имеется ряд переизданий, начиная с 80-х гг.).
 
# С. Уэллин. Как не нужно программировать на С++. Питер, 2004
<references/>
 
С. Уэллин. Как не нужно программировать на С++. Питер, 2004.
 
== Примечания ==
 
{{Note|3}} Данный принцип ''нельзя применять на практике традиционных олимпиад по программированию''. Там задачу обязательно нужно решать в полном объёме, так, чтобы она работала при всех значениях допустимых параметров. Это лишь один из многих случаев, когда практика олимпиад приводит к развитию навыков, которые не просто бесполезны, а прямо вредны для последующей практической деятельности специалиста.
 
{{Note|4}} Это на самом деле общий принцип, применимый отнюдь не только в программировании. Так же стоит поступать и в жизни. Стоит помнить, что «умный человек — тот, кто с честью выходит из такой ситуации, в которую мудрый не попадает».
 
{{Note|5}} Последний принцип (с заменой «неразумное» на «ненужное мне, любимому») успешно используют бюрократы в борьбе против всего лучшего; иногда нужно перенимать их методы, поскольку лезть в лобовую атаку — почти всегда худшее решение.
 
{{Note|6}} Хоть они и крутые, но обычно достаточно мало изобретательные на самом деле, а главное, как было сказано в классическом русском водевиле, такие типы обычно ошибаются «Кажинный раз на ефтом самом месте».
==Примечания==
# {{Note|3}} — Данный принцип ''' нельзя применять на практике традиционных олимпиад по программированию'''. Там задачу обязательно нужно решать в полном объёме, так, чтобы она работала при всех значениях допустимых параметров. Это лишь один из многих случаев,когда практика олимпиад приводит к развитию навыков, которые не просто бесполезны, а прямо вредны для последующей практической деятельности специалиста.
# {{Note|4}} — Это на самом деле общий принцип, применимый отнюдь не только в программировании. Так же стоит поступать и в жизни. Стоит помнить, что «умный человек — тот, кто с честью выходит из такой ситуации, в которую мудрый не попадает».
# {{Note|5}} - Последний принцип (с заменой «неразумное» на «ненужное мне, любимому») успешно используют бюрократы в борьбе против всего лучшего; иногда нужно перенимать их методы, поскольку лезть в лобовую атаку — почти всегда худшее решение.
# {{Note|6}} - Хоть они и крутые, но обычно достаточно мало изобретательные на самом деле, а главное, как было сказано в классическом русском водевиле, такие типы обычно ошибаются «Кажинный раз на ефтом самом месте».
 
[[Категория:журнал «Потенциал»]][[Категория:информатика в журнале «Потенциал»]]