ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.10.2023
Просмотров: 406
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
произошел от строго процедурного языка С, код на C++ может быть написан с использованием как процедурной, так и объектно ориен- тированной парадигмы. Объектно ориентированное программиро- вание применяется сегодня настолько повсеместно, что не может быть исключено из обсуждения решения задач. Однако многие фун- даментальные подходы к решению задач могут быть реализованы в строго процедурной парадигме, что упрощает как написание, так и изменение кода. В-третьих, будучи низкоуровневым языком с высо- коуровневыми библиотеками, С++ позволяет мне обсудить оба уров- ня программирования. При необходимости лучшие программисты могут объединять низкоуровневые части с высокоуровневыми биб- лиотеками и компонентами для сокращения времени разработки.
Наконец, С++ — это отличный выбор, так как, научившись решать за- дачи на этом языке, вы сможете в принципе решать задачи на любом другом языке программирования. Изучение одного языка програм- мирования облегчает последующее изучение других. Это особенно характерно для языка C++, так как он довольно сложен и основан на двух парадигмах одновременно. Возможно, это может вас испугать.
Но преуспев в C++, в сможете не просто писать программы — вы по- настоящему станете программистом.
Наконец, С++ — это отличный выбор, так как, научившись решать за- дачи на этом языке, вы сможете в принципе решать задачи на любом другом языке программирования. Изучение одного языка програм- мирования облегчает последующее изучение других. Это особенно характерно для языка C++, так как он довольно сложен и основан на двух парадигмах одновременно. Возможно, это может вас испугать.
Но преуспев в C++, в сможете не просто писать программы — вы по- настоящему станете программистом.
16 Глава 1
16
Эта книга посвящена решению задач, но что это значит на самом деле? Когда люди используют этот термин в разговоре, они зачастую име- ют в виду нечто очень отличное от того, о чем я говорю в этой книге.
Если из выхлопной трубы вашего старого автомобиля идет сизый дым, двигатель то и дело глохнет, а расход топлива вырос, то эта проблема, как задача, может быть ре- шена с применением знаний автомобиля, диагностикой и заменой деталей при помощи инструментов автомеханика. Впрочем, если вы расскажете о своей проблеме друзьям, то один из них может вам ответить: «Эй, ты должен заменить свою старую машину на что-то поновее, и проблема будет решена». Однако предложение вашего друга на самом деле не будет решением задачи: это будет лишь спосо- бом ее избежать.
1
Стратегии решения задач 17
Задачи подразумевают ограничения, незыблемые правила, ка- сающиеся задачи или способа ее решения. В случае со сломанной машиной одно из ограничений заключается в том, что вам нужно отремонтировать имеющийся автомобиль, а не купить новый. Огра- ничения также должны подразумевать общую стоимость ремонта, время на ремонт и невозможность приобретения инструмента для ремонта только этой поломки.
При решении проблемы в программе у вас также есть опреде- ленные ограничения. Среди общих ограничений язык программи- рования, платформа (будет ли программа работать на ПК, iPhone или ином устройстве?), производительность (для игровой програм- мы может требоваться обновление графики до 30 кадров в секунду, бизнес-приложение должно иметь ограничение по максимальному времени отклика на ввод пользователя) или объем требуемой памя- ти. Иногда ограничения также подразумевают код, на который вы можете ссылаться: возможно, программа не может включать опреде- ленный открытый код или, наоборот, должна использовать только открытый код.
Таким образом, термин решение задач для программистов я могу определить как написание оригинальной программы, выполняю- щей определенный набор заданий и соответствующей всем установ- ленным ограничениям.
Начинающие программисты зачастую так хотят выполнить пер- вую часть определения — написать программу, выполняющую опре- деленное задание — что забывают про выполнение второй части определения — соответствовать установленным ограничениям. Я на- зываю такую программу, выдающую правильный результат, но нару- шающую одно или несколько заявленных правил, «Кобаяси Мару ».
Если это название вам незнакомо, значит вы не смотрели культовый в узких кругах кинофильм «Звездный Путь 2: Ярость Хана». В этом фильме есть сюжетная линия про тест для начинающих офицеров
Академии Звездного флота. Кадетов помещают на мостик симулято- ра звездного корабля или поручают роль капитана, выполняющего миссию, в ходе которой они встречаются с невозможным выбором.
Гибель грозит людям на подбитом корабле «Кобаяси Мару», однако, чтобы добраться до них, вам нужно пойти на самоубийственное сра- жение с врагами. Цель этого упражнения — проверить смелость и отвагу кадета, когда он находится под огнем. В данном сражении по- бедить невозможно, и любой выбор ведет к провалу. Ближе к концу фильма мы обнаруживаем, что капитан Кирк модифицировал симу- лятор, сделав победу возможной. Кирк был хитер, но он не решил дилемму «Кобаяси Мару», а лишь избежал ее.
К счастью, задачи, с которыми вы столкнетесь как программист, разрешимы, но многие программисты все еще прибегают к подхо- ду Кирка. В некоторых случаях они делают это случайно. («О, черт!
Мое решение работает только в том случае, если элементов данных
1 2 3 4 5 6 7 8 9 ... 34
18 Глава 1
сто или меньше. Оно должен работать для неограниченного набора данных. Мне придется переосмыслить это решение».) В других слу- чаях ограничения изменяют преднамеренно, прибегая к этому как к уловке, чтобы успеть к сроку, установленному начальником или пре- подавателем. В других случаях программист просто не знает, как со- ответствовать всем ограничениям. В худших случаях, которые я ви- дел, студенты-программисты платили третьим лицам за написание программы. Независимо от мотивов, мы всегда должны проявлять максимум усердия во избежание «Кобаяси Мару».
Классические головоломки
По мере чтения книги вы заметите, что, несмотря на изменение частностей исходного кода от одной проблемной области к другой, некоторые схемы в моих подходах останутся неизменными. И это хорошо, потому что в конце концов позволит нам уверенно подхо- дить к решению любой задачи, вне зависимости от того, есть ли у вас большой опыт в той или иной проблемной области. Эксперты в решении задач могут быстро распознать аналогию, т. е. пригодное для использования сходство между решенной и нерешенной зада- чей. Если мы распознаем, что некое свойство задачи А аналогично некоему свойству задачи Б, при условии, что мы уже решили задачу
Б, у нас будет очень ценная наработка для решения задачи А.
В этом разделе мы обсудим классические задачи не из мира про- граммирования, из которых мы можем вынести уроки, применимые при решении задач по программированию.
Лисица, гусь и кукуруза
Первая классическая задача, которую мы обсудим, — это загадка про крестьянина, которому нужно пересечь реку. Возможно, вы уже встречались с этой задачей в той или иной форме.
: ?
Крестьянин с лисицей, гусем и мешком кукурузы должен пересечь реку. У крестьянина есть лодка, однако в ней есть место только для самого крестьянина и одного из пере- численных объектов. К сожалению, и лисица, и гусь голодны. Лисица не может быть оставлена наедине с гусем, так как она съест гуся. Точно так же гусь не может остаться наедине с мешком кукурузы, иначе он склюет кукурузу. Как крестьянину перевезти животных и кукурузу через реку?
Условие задачи проиллюстрировано на рис. 1.1. Если вы никогда ранее не встречались с этой задачей, сделайте небольшую паузу и по- тратьте несколько минут на попытки решить ее. Если же вы слыша- ли эту загадку ранее, попытайтесь вспомнить решение и нашли ли вы его самостоятельно.
Стратегии решения задач 19
Рис. 1.1. Лисица, гусь и мешок кукурузы. Лодка может перевезти только один объект за раз.
Лисицу нельзя оставить на одном берегу с гусем, точно так же, как и гуся нельзя оставлять
на одном берегу с мешком кукурузы
Очень немногие могут решить эту загадку, по крайней мере без подсказки. Я тоже не смог. Вот как обычно идет рассуждение: по- скольку крестьянин может брать в лодку только один из объектов за раз, ему потребуется несколько поездок, чтобы перевезти все на дальний берег. Если крестьянин возьмет в первую поездке лисицу, то гусь останется с мешком с кукурузы — и склюет кукурузу. Точно так же, если бы крестьянин взял мешок с кукурузой в первую поездку, а лисица осталась бы в компании с гусем, то лисица съела бы гуся. Та- ким образом, крестьянин должен взять гуся в первую поездку, в ре- зультате чего получится конфигурация, показанная на рис. 1.2.
Рис. 1.2. Необходимый первый шаг решения задачи с лисицей, гусем и мешком кукурузы.
Однако складывается ощущение, что все последующие шаги обречены на провал
Пока что все идет нормально. Но во вторую поездку крестьянин должен взять лисицу или кукурузу. Однако, что бы крестьянин ни взял, этот объект должен быть оставлен на дальнем берегу с гусем, на то время, пока крестьянин возвращается к ближнему берегу за остав- шимся объектом. Это означает, что лиса и гусь останутся наедине
20 Глава 1
или что гусь и кукуруза будут оставлены вместе. Поскольку ни одна из этих ситуаций не приемлема, проблема кажется неразрешимой.
Повторимся, если вы уже встречали эту задачу, то, возможно, вы помните ключевой элемент решения. Как было объяснено выше, крестьянин должен взять в первую поездку гуся. Давайте предло- жим, что во вторую поездку крестьянин берет с собой лисицу. Одна- ко вместо того, чтобы оставить гуся с лисицей, крестьянин отво зит
гуся обратно на ближний берег. Затем крестьянин перевозит через реку мешок кукурузы, оставляя на дальнем берегу лисицу с кукурузой, и возвращается туда в четвертую поездку с гусем. Полное решение за- дачи показано на рис. 1.3.
Рис. 1.3. Пошаговое решение головоломки с лисицей, гусем и кукурузой
Эта задача сложна потому, что большинство людей не рассматри- вает возможность перевезти один из элементов обратно с дальнего берега на ближний. Некоторые могут посчитать загадку нечестной и сказать что-то наподобие: «Вы не говорили, что я могу перевезти что-то обратно!» Это правда, впрочем, как правда и то, что ничего в
Стратегии решения задач 21
описании задачи не указывает на то, что перевозить объекты обрат- но запрещено.
Представьте, насколько проще было бы решить эту головоломку, если бы возможность перевезти один из объектов обратно на ближ- ний берег была бы указана явно: У крестьянина есть лодка, которую он
может использовать для перевозки объектов в обоих направлениях, но в
лодке есть место только для самого крестьянина и одного из трех объектов.
Теперь, когда эта подсказка видна невооруженным глазом, большее количество людей смогут решить задачу. Это позволяет проиллюстри- ровать важный принцип решения задач: если вам не известны все дей- ствия, которые вы можете предпринять, то, возможно, вы не сможете решить задачу. Мы можем называть эти действия операциями. Создав список всех возможных операций, мы сможем решить любую задачу, проверяя все возможные сочетания операций до тех пор, пока одно из этих сочетаний не решит задачу. В общем, переформулирование задачи более формальным языком позволяет нам открыть решения, которые ускользнули бы от нас в противном случае.
Давайте забудем, что мы уже знаем решение, и попытаемся более формально поставить условие данной конкретной задачи. В первую очередь, перечислим основные ограничения:
1. Крестьянин может взять в лодку только один объект за раз.
2. Лисицу нельзя оставить вместе с гусем.
3. Гуся нельзя оставить вместе с кукурузой.
Эта задача — хороший пример важности ограничений. Если мы уберем одно из вышеприведенных ограничений, то головоломка ста- новится простой. Убрав первое ограничение, мы сможем запросто перевезти все три объекта через реку за одну поездку. Даже если мы можем взять только два объекта, то мы можем взять лисицу и кукуру- зу и перевезти их через реку, а затем вернуться за гусем. Убрав второе ограничение (но оставив в силе все прочие), то, нам нужно лишь быть внимательными и сначала взять гуся, затем переправить лисицу и, на- конец, вернуться за кукурузой. Таким образом, если мы забудем или проигнорируем ограничения, то получим «Кобаяси Мару».
Теперь давайте перечислим все операции. Существует несколько способов формулировки операций этой головоломки. Мы могли бы составить список конкретных действий, которые, как нам кажется, мы можем предпринять.
1. Операция: Перевезти лисицу на дальний берег реки.
2. Операция: Перевезти гуся на дальний берег реки.
3. Операция: Перевезти кукурузу на дальний берег реки.
Помните, что цель формальной переустановки задачи — получение некоего аналитического вывода для решения. Если мы раньше не реша- ли эту задачу и не находили возможную «скрытую» операцию, такую как перевозка гуся обратно на ближний берег, мы не сможем найти эту опе-