Вычислительное мышление. Метод решения сложных задач - Питер Макоуэн
Шрифт:
Интервал:
Закладка:
Иногда действительно невозможно создать алгоритм, который обеспечит наилучшее решение для задачи — или вообще, или в доступное время (и это правда невозможно, а не просто трудно). В таких ситуациях используется эвристический алгоритм. Он не гарантирует оптимального решения, но обеспечивает разумный вариант в разумный срок. Для создания такого алгоритма необходимо эвристическое решение задачи. И пусть ответ не всегда будет лучшим — в большинстве случаев он окажется хорошим. По этому принципу действует ваш навигатор, когда прокладывает для вас путь.
Чтобы мыслить алгоритмически, необходимо логическое мышление, крайне тщательный и точный подход к деталям. Например, инструкции в алгоритме должны охватить все возможные варианты развития событий. Если в вашем алгоритме используется сложение, указали ли вы, что делать с отрицательными числами? Столкнувшись с ними, компьютер либо даст неправильный ответ, либо вообще зависнет. Разрабатывая алгоритм, нужно предельно логично оценить то, как он работает. Если не на бумаге, то уж точно в голове вам нужно иметь логические аргументы, подтверждающие его бесперебойную работу. Нельзя допустить, чтобы ваш спускаемый аппарат потерпел крушение, когда он наконец-то опустится на поверхность Марса спустя долгие месяцы, только потому, что вы забыли какую-то деталь. Как мы увидим далее, логическое мышление — это часть оценки.
Умение видеть две одинаковые (или очень похожие) задачи — важный элемент вычислительного мышления, сопоставление с образцом. Мы все время занимаемся этим, даже не задумываясь, и специалисты постоянно используют в работе сопоставление с образцом — они узнают ситуацию и без колебаний делают то, что нужно. Этот же принцип лежит в основе многих программ: они сопоставляют правила с ситуацией и определяют, каким именно указаниям надо следовать. Программисты, создающие подобные программы, должны разработать образцы, на которые будет ориентироваться программа. Машинное самообучение тоже подразумевает сопоставление с образцом, но в этом случаи образцы подбирают сами программы.
При решении задач сопоставление с образцом помогает уменьшить объем работы, потому что при получении новой задачи позволяет не повторять одни и те же сложные операции каждый раз. Нужно найти аналогичную задачу из числа уже решенных и взять старое решение. Например, когда вы видите задачу или головоломку, связанную с составлением маршрута, нужно вспомнить о графах. Эту же мысль можно сформулировать по-другому: если вы видите, что задача соответствует модели передвижения из одной точки в другую, то для ее представления стоит использовать граф. Точки необязательно должны быть физически существующими местами. Например, это могут быть веб-страницы (с гиперссылками между ними), режимы будильника (с кнопочным переходом между ними), города (с перелетами из одного в другой) и так далее.
Решение проблемы можно облегчить, выбрав для нее хорошее представление, котороеявляется способом организации информации. Есть на удивление много возможностей представить одну и ту же информацию, и, как только вы это осознаете и с самого начала сосредоточитесь на выборе хорошего представления, задачи станут проще. Как мы видели на примере задач с составлением графа и игры «Бусы, хлеб, баня», хорошее представление значительно облегчает весь процесс как для людей, так и для компьютеров. Однако оно может полностью поменять используемый алгоритм. Порой очевидно, что благодаря верному представлению открываются возможности использовать простой и быстрый алгоритм вместо медленного и сложного. В этой книге мы ознакомились с разными представлениями — например, растровые изображения, состоящие из большого количества пикселов, формирующих решетку, и векторные изображения в виде линий и фигур. Мы узнали, что представление в виде сетки открывает самые разные возможности. Также мы увидели, что если числа хранятся в двоичном представлении на перфокартах, то возможен быстрый поиск и применение алгоритма упорядочивания. Представление образца в виде цифрового фильтра позволяет создать алгоритмы, помогающие компьютерам видеть. То есть выбор хорошего представления является важной частью абстрагирования и генерализации.
Абстрагирование — это сокрытие деталей разными способами с целью облегчить решение задачи. Есть масса разных способов, которые позволяют спрятать подробности. Этому способствует создание алгоритмов и их оценка.
Один из важных способов использования абстрагирования, который называется абстракция управления, мы рассмотрели на примере фокусов. Он состоит в объединении инструкций, что позволяет получить инструкции для достаточно больших этапов процесса. Идея здесь состоит в том, чтобы спрятать подробности отдельных маленьких шагов. В кулинарных книгах это встречается на каждом шагу. Там даются инструкции вроде «сварите картошку». Сюда входит много этапов: наполнить кастрюлю водой, включить конфорку, довести воду до кипения, положить картошку и так далее. Все эти шаги сведены вместе в одну простую команду «сварите картошку». Чтобы выполнить инструкцию, нужны все детали, но абстрагирование помогает записать инструкции и воспринимать алгоритм (или рецепт) в целом, чтобы работать с большими шагами. Изобилие подробностей мешает думать, пока вы не дойдете до них и не начнете с ними работать. Эта форма абстрагирования очень близка к декомпозиции, которую мы опишем ниже.
Есть еще один тип абстрагирования — абстрагирование данных. В этом случае прячут подробности хранения данных, то есть как они представлены на самом деле. Например, числа в реальности хранятся на компьютере в бинарном коде — как последовательность нолей и единиц. То есть число 16 выглядит как 00010000. Мы игнорируем этот факт, когда думаем о числах. У нас в голове именно 16 — десятичное число, которое мы привыкли использовать. Когда мы пишем программы, то используем в инструкциях десятичные числа, а не двоичные. Но наши программы просят пользователей вводить десятичные числа. При этом компьютер с ними не работает. Пользователям вообще не надо знать, что числа хранятся именно в таком виде, — эта подробность спрятана.
Мы используем абстрагирование не только при создании программ. Оно подходит и для их оценки. Например, как мы видели ранее, оно кстати при выборе алгоритма для помощи пациенту с синдромом «запертого человека». Если нужно решить, какой из двух алгоритмов с одинаковой задачей работает быстрее, не надо думать о времени как таковом. Отвлечемся от этих подробностей и будем думать только о выполненной работе — сколько операций нужно провести, чтобы довести до конца каждый алгоритм и справиться с задачей. Если для одного алгоритма нужно выполнить 100 инструкций, а для другого — только 10, то второй будет быстрее. Проверяем это, просто пересчитывая операции, не засекая время. Мы прячем подробности о временны`х затратах на операции, чтобы облегчить себе задачу.
Идея взять решенную проблему и приспособить ее решение (алгоритм) к другим подобным задачам называется обобщением. Предположим, что нужно найти свое имя в списке рассадки гостей. Этот список — представление имен, из которого мы узнаем, где сидим. Вместо того чтобы искать в случайном порядке, мы начинаем сверху и проверяем каждое имя по очереди, пока мы не дойдем до своего. Другой пример — надо найти на полке CD, и мы понимаем, что это похожая задача. Таким образом мы осуществляем сопоставление с образцом — сравниваем одну задачу с другой. Как только мы осознаем, что две задачи сводятся к одной, то используем для обеих одно и то же решение, и необходимость поиска нового алгоритм отпадает. Мы начинаем с одного конца и ведем пальцем по полке, проверяя каждый диск по очереди, пока не найдем нужный (или не доберемся до конца, что будет означать отсутствие диска). Мы обобщили и преобразили алгоритм (решение для первой задачи) и используем его для решения новой задачи.