
- •1Динамическое программирование
- •1.1Введение
- •1.2Поиск максимальной суммы
- •1.3 Перемножение нескольких матриц
- •1.3.1Количество расстановок скобок
- •1.3.2Шаг 1: строение оптимальной расстановки скобок
- •1.3.3Шаг 2: рекуррентное соотношение
- •1.3.4Шаг 3: вычисление оптимальной стоимости
- •1.4Когда применимо динамическое программирование
- •1.4.1Оптимальность для подзадач
- •1.4.2Перекрывающиеся подзадачи
- •1.5Задача о замене оборудования
- •2Жадные алгоритмы
- •2.1Задача о выборе заявок
- •2.1.1Правильность алгоритма
- •2.2Когда применим жадный алгоритм
- •2.2.1Принцип жадного выбора
- •2.2.2Оптимальность для подзадач
- •2.2.3Жадный алгоритм или динамическое программирование?
2.2Когда применим жадный алгоритм
Общих рецептов определения, применимости жадного алгоритма нет, но существуют две особенности, характерные для задач, решаемых жадными алгоритмами. Это принцип жадного выбора и свойство оптимальности для подзадач.
2.2.1Принцип жадного выбора
Говорят, что к оптимизационной задаче применим принцип жадного выбора, если последовательность локально оптимальных (жадных) выборов дает глобально оптимальное решение. Различие между жадными алгоритмами и динамическим программированием можно пояснить так: на каждом шаге жадный алгоритм берёт «самый жирный кусок», а потом уже пытается сделать наилучший выбор среди оставшихся, каковы бы они ни были; алгоритм динамического программирования принимает решение, просчитав заранее последствия для всех вариантов.
Как доказать, что жадный алгоритм даёт оптимальное решение? Это не всегда тривиально, но в типичном случае такое доказательство следует схеме, использованной в задаче о заявках. Сначала мы доказываем, что жадный выбор на первом шаге не закрывает пути к оптимальному решению: для всякого решения есть другое, согласованное с жадным выбором и не худшее первого. Затем показывается, что подзадача, возникающая после жадного выбора на первом шаге, аналогична исходной, и рассуждение завершается по индукции.
2.2.2Оптимальность для подзадач
Говоря
иными словами, решаемые с помощью жадных
алгоритмов задачи обладают свойством
оптимальности для подзадач: оптимальное
решение всей задачи содержит в себе
оптимальные решения подзадач. (С этим
свойством мы уже встречались, говоря о
динамическом программировании.) Например,
задаче о заявках мы видели, что если
— оптимальный набор заявок, содержащий
заявку номер 1, то
— оптимальный набор заявок для меньшего
множества заявок
,
состоящего из тех заявок, для которых
.
2.2.3Жадный алгоритм или динамическое программирование?
И жадные алгоритмы, и динамическое программирование основываются на свойстве оптимальности для подзадач, поэтому может возникнуть искушение применить динамическое программирование в ситуации, где хватило бы жадного алгоритма или напротив, применить жадный алгоритм к задаче, в которой он не даст оптимума. Проиллюстрируем возможные ловушки на примере двух вариантов классической оптимизационной задачи.
Дискретная
задача о рюкзаке состоит в следующем.
На складе хранится
вещей. Вещь номер
стоит
гривен и весит
килограммов (
- целые числа). Необходимо набрать в
рюкзак товара на максимальную сумму,
причём максимальный вес, который можно
унести в рюкзаке, равен
(число
тоже целое). Что нужно положить в
рюкзак?
Непрерывная задача о рюкзаке отличается от дискретной тем, что можно дробить товары на части и укладывать в рюкзак эти части, а не обязательно вещи целиком (если в дискретной задаче мы имеем дело с золотыми слитками, то в непрерывной — с золотым песком).
Обе
задачи о рюкзаке обладают свойством
оптимальности для подзадач. В самом
деле, рассмотрим дискретную задачу.
Вынув вещь номер
из оптимально загруженного рюкзака,
получим решение задачи о рюкзаке с
максимальным весом
и набором из
вещи (все вещи, кроме
-ой).
Аналогичное рассуждение проходит и для
непрерывной задачи: вынув из оптимально
загруженного рюкзака, в котором лежит
килограммов товара номер
,
весь этот товар, получим оптимальное
решение непрерывной задачи, в которой
максимальный вес равен
(вместо
),
а количество
-го
товара равно
(вместо
).
Хотя
две задачи о рюкзаке и похожи, жадный
алгоритм даёт оптимум в непрерывной
задаче о рюкзаке и не даёт в дискретной.
В самом деле, решение непрерывной задачи
о рюкзаке с помощью жадного алгоритма
выглядит так. Вычислим цены (в расчёте
на килограмм) всех товаров (цена товара
номер
равна
).
Сначала берём по максимуму самого
дорогого товара; если весь этот товар
кончился, а рюкзак не заполнен, то берём
следующий по цене товар, затем следующий,
и так далее, пока не наберём вес
.
Поскольку товары надо предварительно
отсортировать по ценам, на что уйдёт
время
,
время работы описанного алгоритма будет
.
Чтобы убедиться в том, что аналогичный жадный алгоритм не обязан давать оптимум в дискретной задаче о рюкзаке, рассмотрим следующий пример. Грузоподъемность рюкзака 50 кг. На складе есть три вещи, весящие 10, 20 и 30 кг и стоящие 60, 100 и 120 гривен соответственно. Цена их в расчёте на единицу веса равна 6, 5 и 4. Жадный алгоритм для начала положит в рюкзак вещь номер 1, а затем 2. Это даст 160 гривен. Но оптимальное решение включает предметы номер 2 и 3 и дает 220 гривен.
Для непрерывной задачи с теми же исходными данными жадный алгоритм даст правильное решение. В дискретной задаче, положив предмет номер 1, мы лишаемся возможности заполнить рюкзак «под завязку», а пустое место снижает цену товаров в рюкзаке. Здесь, чтобы решить, стоит ли класть вещь в рюкзак, следует рассмотреть две подзадачи: если данная вещь заведомо лежит в рюкзаке и если вещи заведомо нет. Тем самым дискретная задача порождает множество перекрывающихся подзадач – типичный признак того, что может пригодиться динамическое программирование. И действительно, в данном случае оно применимо.
Введем
величину
- максимальную стоимость множества
товаров, чьи номера не превышают
(
),
а суммарная масса точно равна
(
).
Тогда максимальная стоимость товаров,
которые можно положить в рюкзак, есть
.
Определим формулы для расчета .
Для
значение
при
равно 0. Если
(т.е. товар номер 1 помещается в рюкзак),
то следует положить
- это стоимость единственного товара с
номером 1, который положили в рюкзак.
Для
значение
рассчитывается следующим образом. Если
положить товар
в рюкзак (а это можно сделать, если
),
то максимальная набираемая стоимость
составит
.
Если же товар
в рюкзак не класть, то максимальная
набираемая стоимость есть
.
Таким образом, получим формулу
.
Получаем следующий алгоритм. Проходим
по
от 1 до
включительно. Для каждого
перебираем
от 0 до
и применяем выведенные выше формулы.
Перебрав все
,
выбираем
- это максимальная стоимость. Запомним
,
которое приносим максимум
.
Теперь, перебирая
от
до 1, определяем, какие товары нужно было
положить в рюкзак. Для каждого
выполняем следующие действия. Если
,
то товар
в рюкзак класть не следует. Иначе если
,
то товар
в рюкзак класть также не следует. Иначе
кладем товар
в рюкзак и полагаем
.