ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.10.2023
Просмотров: 419
Скачиваний: 1
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Решение задач с помощью рекурсии 179
нии сгенерировал данный клиент. Менеджер делегирует изучение остальных пяти файлов вице-менеджеру. Он обработает один файл и передаст остальные четыре заместителю менеджера. Этот про- цесс будет продолжаться до тех пор, пока мы не дойдем до шестого сотрудника, стажера, которому передадут один файл, и он должен будет просто обработать его, не имея возможности для дальнейше- го делегирования.
На рис. 6.4 показаны линии связи и разделения труда. Однако, как и в предыдущем примере, существует два различных подхода к реализации цепи сообщений.
Рис. 6.4. Нумерация шагов при использовании Подхода 1 (а) и Подхода 2 (б) для определе-
ния клиента, принесшего наибольшую прибыль
Подход 1
При использовании этого подхода, делегируя оставшиеся файлы, сотрудник также передает самое высокое значение прибыли, наблю- даемое до сих пор. Это значит, что сотрудник должен подсчитать общую прибыль в одном файле и сравнить полученное значение с предыдущим наибольшим значением прибыли, прежде чем делеги- ровать обработку оставшихся файлов другому сотруднику. Вот при- мер того, как это может происходить на практике.
1. МЕНЕДЖЕР подсчитывает прибыль от клиента №0001, которая составляет 172 000 долларов США.
2. МЕНЕДЖЕР ВИЦЕ-МЕНЕДЖЕРУ: «Самое высокое значение прибыли, которое мы наблюдали до сих пор, составляет 172 000
180 Глава 6
долларов США, клиент №0001. Изучите эти пять файлов и опре- делите максимальное значение прибыли».
3. ВИЦЕ-МЕНЕДЖЕР подсчитывает прибыль, полученную от кли- ента №0002, которая составляет 68 000 долларов США. Наиболь- шим из наблюдаемых до сих пор значений прибыли по-прежнему является 172 000 долларов США, клиент №0001.
4. ВИЦЕ-МЕНЕДЖЕР ЗАМЕСТИТЕЛЮ МЕНЕДЖЕРА: «Самое вы- сокое значение прибыли, которое мы наблюдали до сих пор, со- ставляет 172 000 долларов США, клиент №0001. Изучите эти че- тыре файла и определите максимальное значение прибыли».
5. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА подсчитывает прибыль, полу- ченную от клиента №0003, которая составляет 193 000 долларов
США. Наибольшим из наблюдаемых до сих пор значений прибы- ли теперь является 193 000 долларов США, клиент №0003.
6. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА ПОМОЩНИКУ МЕНЕДЖЕРА:
«Самое высокое значение прибыли, которое мы наблюдали до сих пор, составляет 193 000 долларов США, клиент №0003. Изучи- те эти три файла и определите максимальное значение прибыли».
7. ПОМОЩНИК МЕНЕДЖЕРА подсчитывает прибыль, полу- ченную от клиента №0004, которая составляет 13 000 долларов
США. Наибольшим из наблюдаемых до сих пор значений прибы- ли по-прежнему является 193 000 долларов США, клиент №0001.
8. ПОМОЩНИК МЕНЕДЖЕРА МЛАДШЕМУ МЕНЕДЖЕРУ: «Са- мое высокое значение прибыли, которое мы наблюдали до сих пор, составляет 193 000 долларов США, клиент №0003. Изучите эти два файла и определите максимальное значение прибыли».
9. МЛАДШИЙ МЕНЕДЖЕР подсчитывает прибыль, полученную от клиента №0005, которая составляет 256 000 долларов США.
Наибольшим из наблюдаемых до сих пор значений прибыли те- перь является 256 000 долларов США, клиент №0005.
10. МЛАДШИЙ МЕНЕДЖЕР СТАЖЕРУ: «Самое высокое значение прибыли, которое мы наблюдали до сих пор, составляет 256 000 долларов США, клиент №0005. Изучите последний файл и опре- делите максимальное значение прибыли».
11. СТАЖЕР подсчитывает прибыль, полученную от клиента
№0006, которая составляет 99 000 долларов США. Наибольшим из наблюдаемых до сих пор значений прибыли по-прежнему яв- ляется 256 000 долларов США, клиент №0005.
12. СТАЖЕР МЛАДШЕМУ МЕНЕДЖЕРУ: «Наибольшее значение прибыли составляет 256 000 долларов США, клиент №0005».
13. МЛАДШИЙ МЕНЕДЖЕР ПОМОЩНИКУ МЕНЕДЖЕРА: «Наи- большее значение прибыли составляет 256 000 долларов США, клиент №0005».
14. ПОМОЩНИК МЕНЕДЖЕРА ЗАМЕСТИТЕЛЮ МЕНЕДЖЕРА:
Решение задач с помощью рекурсии 181
«Наибольшее значение прибыли составляет 256 000 долларов
США, клиент №0005».
15. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА ВИЦЕ-МЕНЕДЖЕРУ: «Наи- большее значение прибыли составляет 256 000 долларов США, клиент №0005».
16. ВИЦЕ-МЕНЕДЖЕР МЕНЕДЖЕРУ: «Наибольшее значение при- были составляет 256 000 долларов США, клиент №0005».
В этом подходе, показанном на рис. 6.4 (а), используется хвостовая рекурсия. Каждый сотрудник обрабатывает один файл клиента и срав- нивает вычисленное значение прибыли, полученной от этого клиента, с самым высоким значением, которое было обнаружено до сих пор. За- тем этот сотрудник передает результат сравнения подчиненному. Рекур- сия — делегирование задач — происходит после других операций обра- ботки. Процесс работы каждого сотрудника включает следующие шаги:
1. подсчет значения прибыли в одном файле клиента;
2. сравнение этого значения с самым высоким значением при- были, наблюдаемым начальством в других файлах клиентов;
3. передача оставшихся файлов клиентов подчиненному вместе с наибольшим значением прибыли, наблюдаемым до сих пор;
4. когда подчиненный сообщит самое высокое значение прибы- ли из всех файлов клиентов, требуется передать это значение началь нику.
Подход 2
При использовании этого подхода сотрудник сначала откладыва- ет один файл, а затем передает оставшиеся файлы подчиненному.
В данном случае перед подчиненным не ставится задача подсчета наибольшего значения прибыли из всех файлов, а только из тех, ко- торые были ему переданы. Как и в первой задаче, это упрощает за- просы. При использовании тех же данных, что и в первом подходе, цепочка сообщений выглядит следующим образом:
1. МЕНЕДЖЕР ВИЦЕ-МЕНЕДЖЕРУ: «Изучите эти пять файлов и сообщите мне максимальное значение прибыли».
2. ВИЦЕ-МЕНЕДЖЕР ЗАМЕСТИТЕЛЮ МЕНЕДЖЕРА: «Изучите эти четыре файла и сообщите мне максимальное значение при- были».
3. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА ПОМОЩНИКУ МЕНЕДЖЕРА:
«Изучите эти три файла и сообщите мне максимальное значение прибыли».
4. ПОМОЩНИК МЕНЕДЖЕРА МЛАДШЕМУ МЕНЕДЖЕРУ: «Изучи- те эти два файла и сообщите мне максимальное значение прибыли».
5. МЛАДШИЙ МЕНЕДЖЕР СТАЖЕРУ: «Изучите этот файл и сооб- щите мне максимальное значение прибыли».
182 Глава 6 6. СТАЖЕР подсчитывает прибыль, полученную от клиента
№0006, которая составляет 99 000 долларов США. Это един- ственный файл, который видел СТАЖЕР, поэтому данное значе- ние прибыли является наибольшим.
7. СТАЖЕР МЛАДШЕМУ МЕНЕДЖЕРУ: «Наибольшее значение прибыли в моем файле составляет 99 000 долларов США, клиент
№0006».
8. МЛАДШИЙ МЕНЕДЖЕР подсчитывает прибыль, полученную от клиента №0005, которая составляет 256 000 долларов США.
Наибольшим из известных данному сотруднику значений явля- ется 256 000 долларов США, клиент №0005.
9. МЛАДШИЙ МЕНЕДЖЕР ПОМОЩНИКУ МЕНЕДЖЕРА: «Наи- большее значение прибыли в моих файлах составляет 256 000 долларов США, клиент №0005».
10. ПОМОЩНИК МЕНЕДЖЕРА подсчитывает прибыль, полу- ченную от клиента №0004, которая составляет 13 000 долларов
США. Наибольшим из известных данному сотруднику значений является 256 000 долларов США, клиент №0005.
11. ПОМОЩНИК МЕНЕДЖЕРА ЗАМЕСТИТЕЛЮ МЕНЕДЖЕРА:
«Наибольшее значение прибыли в моих файлах составляет 256 000 долларов США, клиент №0005».
12. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА подсчитывает прибыль, полу- ченную от клиента №0003, которая составляет 193 000 долларов
США. Наибольшим из известных данному сотруднику значений является 256 000 долларов США, клиент №0005.
13. ЗАМЕСТИТЕЛЬ МЕНЕДЖЕРА ВИЦЕ-МЕНЕДЖЕРУ: «Наиболь- шее значение прибыли в моих файлах составляет 256 000 долла- ров США, клиент №0005».
14. ВИЦЕ-МЕНЕДЖЕР подсчитывает прибыль, полученную от кли- ента №0002, которая составляет 68 000 долларов США. Наиболь- шим из известных данному сотруднику значений является 256 000 долларов США, клиент №0005.
15. ВИЦЕ-МЕНЕДЖЕР МЕНЕДЖЕРУ: «Наибольшее значение при- были составляет 256 000 долларов США, клиент №0005».
16. МЕНЕДЖЕР подсчитывает прибыль от клиента №0001, которая составляет 172 000 долларов США. Наибольшим из известных данному сотруднику значений является 256 000 долларов США, клиент №0005.
В этом подходе, показанном на рис. 6.4 (б), используется головная рекурсия. Каждый сотрудник по-прежнему должен подсчитать значе- ние прибыли в одном файле клиента, однако это действие откладыва- ется до тех пор, пока подчиненный не определит самое высокое зна- чение в остальных файлах. Процесс работы каждого из сотрудников включает следующие этапы:
Решение задач с помощью рекурсии 183
1. передача подчиненному сотруднику всех файлов клиентов, кро- ме одного;
2. получение от подчиненного сотрудника наибольшего значения прибыли среди этих файлов;
3. определение значения прибыли в одном файле клиента;
4. передача начальнику большего из этих значений прибыли.
Как и в задаче с подсчетом количества попугаев, головная рекур- сия позволяет каждому сотруднику передать подчиненному мини- мальный объем информации.
Большая рекурсивная идея
Теперь мы подошли к Большой Рекурсивной Идее. На самом деле, если вы прочитали все этапы решения приведенных выше задач, вы уже видели БРИ в действии.
Как это? В обеих задачах используется некая форма рекурсивно- го решения. Каждый человек в коммуникационной цепочке выпол- няет одни и те же действия со все уменьшающимся подмножеством исходных данных. Однако важно отметить, что эти задачи вообще не
предусматривают никакой рекурсии.
В первой задаче каждый сотрудник железной дороги запрашивает данные у сотрудника следующей станции вниз по линии, и, выполняя этот запрос, следующий сотрудник повторяет те же шаги, что и преды- дущий. Однако ничто в формулировке запроса не требует от сотрудни- ка выполнения этих конкретных шагов. Например, когда Арт позвонил
Белинде, используя подход 2, он попросил ее подсчитать общее количе- ство попугаев от ее станции до конца линии. Он не требовал примене- ния какого-то определенного метода для вычисления этого значения.
Если бы он подумал об этом, то мог бы понять, что Белинде придется произвести те же действия, что и ему, однако ему не нужно это учиты- вать. Для решения своей задачи Арту было достаточно того, чтобы Бе- линда сообщила правильный ответ на заданный им вопрос.
Точно так же, во второй проблеме каждый сотрудник старался де- легировать подчиненному максимально возможное количество работы.
Например, помощник менеджера может хорошо знать младшего менед- жера и ожидать, что младший менеджер передаст стажеру все файлы, кроме одного. Однако у помощника менеджера нет причин задумывать- ся о том, обработает ли младший менеджер все оставшиеся файлы или передаст некоторые из них своему подчиненному. Помощник менед- жера заботится только о том, чтобы младший менеджер сообщил ему правильный ответ. Поскольку помощник менеджера не собирается по- вторять действия младшего менеджера, помощник менеджера просто предполагает, что результат, полученный от младшего менеджера явля- ется правильным, и использует эти данные для решения общей задачи, которую помощник менеджера получил от заместителя менеджера.
184 Глава 6
В обеих задачах, когда сотрудники обращаются к другим сотрудни- кам за информацией, они думают о том, что, а не о том, как. Вопрос задан; ответ получен. Таким образом, Большая Рекурсивная Идея сво- дится к следующему: если вы следуете определенным правилам в про- цессе кодирования, вы можете сделать вид, что рекурсии не происходит.
Вы даже можете использовать дешевый трюк (описан ниже), чтобы перейти от итеративной реализации к рекурсивной реализации, явно не рассматривая то, как рекурсия на самом деле решает проблему. Со временем вы выработаете интуитивное понимание того, как работа- ют рекурсивные решения, однако, пока этого не произошло, вы може- те реализовать рекурсию и быть уверенными в своем коде.
Давайте применим эту концепцию на практике с помощью при- мера кода.
: # " " # D "
& "
Напишите рекурсивную функцию, которой в качестве параметров передан массив целых чисел и размер массива. Эта функция должна возвратить сумму целых чисел в массиве.
Вашей первой мыслью, вероятно, было то, что эту проблему лег- ко можно решить итеративно. Действительно, давайте начнем с ите- ративного решения этой проблемы: int iterativeArraySum(int integers[], int size) {
int sum = 0;
for (int i = 0; i < size; i++) {
sum += integers[i];
}
return sum;
}
Вы видели код, очень похожий на этот, в главе 3, поэтому эта функция должна быть простой для понимания. Следующим шагом будет написание кода, который находится посередине между итера- ционным решением и конечным требуемым рекурсивным решени- ем. Мы оставим итеративную функцию и добавим вторую функцию, которую будем называть диспетчером. Он передаст большую часть ра- боты ранее написанной итеративной функции и использует эту ин- формацию для решения общей задачи. Чтобы написать функцию- диспетчер, мы должны следовать двум правилам.
1. Диспетчер должен быть в состоянии полностью справиться с са- мым тривиальным случаем, не вызывая итеративную функцию.
2. При вызове итеративной функции диспетчер должен передать меньшую версию задачи.
Применяя первое правило к этой задаче, мы должны решить, что собой представляет самый тривиальный случай. Если значение size равно 0, то функции концептуально передается массив «null» с суммой
Решение задач с помощью рекурсии
1 ... 18 19 20 21 22 23 24 25 ... 34
185
элементов равной 0. Можно поспорить, что самый тривиальный случай должен иметь место, когда значение size равно 1. В этом случае в логи- ческом массиве будет только одно число, мы можем вернуть это число в качестве суммы. Любая из этих интерпретаций будет работать, одна- ко выбор в пользу первого варианта позволяет функции обрабатывать специальный случай. Обратите внимание на то, что в исходной итера- тивной функции не произойдет сбой при значении size равном нулю, поэтому поддержание этой гибкости является предпочтительным.
Чтобы применить второе правило к этой задаче, мы должны найти способ передачи меньшей версии задачи от диспетчера ите- ративной функции. Нет простого способа передачи меньшего мас- сива, однако мы можем легко передать меньшее значение size. Если диспетчеру передано значение size равное 10, то от функции требу- ется вычислить сумму 10 значений в этом массиве. Если диспетчер передает итеративной функции значение size равное 9, то он запра- шивает сумму первых 9 значений в массиве. Затем диспетчер может добавить значение одного оставшегося элемента массива (10-го) для вычисления суммы всех 10 значений. Обратите внимание на то, что уменьшение размера на 1 при вызове итеративной функции макси- мизирует работу итеративной функции и тем самым минимизиру- ет работу диспетчера. Этот подход всегда является предпочтитель- ным — как менеджеры DelegateCorp, функция-диспетчер старается избежать как можно большего количества работы.
Объединив эти идеи, мы получаем следующую функцию-диспет- чер для этой задачи: int arraySumDelegate(int integers[], int size) {
X if (size == 0) return 0;
Y int lastNumber = integers[size — 1];
int allButLastSum =
ZiterativeArraySum(integers, size — 1);
[ return lastNumber + allButLastSum;
}
Первое утверждение обеспечивает соблюдение первого прави- ла диспетчеров: оно проверяет тривиальный случай и полностью его обрабатывает, в данном примере, возвращая 0
X
. В противном случае управление переходит к оставшемуся коду, который обеспе- чивает соблюдение второго правила. Последнее число в массиве хранится в локальной переменной lastNumber
Y
, а затем сумма всех остальных значений в массиве вычисляется посредством вызова итеративной функции
Z
. Этот результат сохраняется в другой ло- кальной переменной allButLastSum, и, наконец, функция возвраща- ет сумму значений двух локальных переменных
[
Если мы создали функцию-диспетчер правильно, мы уже создали рекурсивное решение. Это Большая Рекурсивная Идея в действии. Пре- вращение этого итеративного решения в рекурсивное требует лишь од- ного простого шага: нужно сделать так, чтобы функция-делегат вызыва- ла саму себя там, где она ранее вызывала итеративную функцию. Затем мы можем полностью удалить итерационную функцию.