Содержание:
Введение в динамическое программирование (ДП)
Само понятие «динамическое программирование» впервые было использовано в 1940-х годах Ричардом Беллманом для описания процесса нахождения решения задачи, где ответ на одну задачу может быть получен только после решения другой задачи, «предшествующей» ей.
Таким образом, американский математик и один из ведущих специалистов в области математики и вычислительной техники — Ричард Эрнст Беллман — стал прородителем динамического программирования.
Позднее формулировка понятия была доработана и усовершенствованна до современного вида самим же Беллманом.
Слово «программирование» в контексте «динамическое программирование» на самом деле к классическому пониманию программирования (написанию кода на языке программирования) практически никакого отношения не имеет. Слово «Программирование» имеет такой же смысл как в словосочетании «математическое программирование», которое является синонимом слова «оптимизация».
Поэтому программы будут использоваться в качестве оптимальной последовательности действий для получения решения задачи.
В общем же для начала, неформальное определение понятия динамического программирования может звучать так:
Задачи оптимизации, как правило, связаны с задачей максимизации или минимизации той или иной целевой функции (например, максимизировать вероятность того, что система не сломается, максимизировать мат. ожидание получения прибыли и т.д.).
Задачи комбинаторики, как правило, отвечают на вопрос, сколько существует объектов, обладающих теми или иными свойствами, или сколько существует комбинаторных объектов, обладающих заданными свойствами.
То есть, ДП решает не все задачи, а лишь некоторые, определенный класс подзадач. Но этот класс подзадачи используется во многих областях знаний: программирование, математика, лингвистика, статистика, теория игр, экономика, в компьютерных науках и т.п.
Задачи, решаемые при помощи динамического программирования, должны обладать свойством сооптимальности, о котором будет сказано в дальнейших уроках.
Неформальное объяснение свойства оптимальности у подзадач может быть продемонстрировано с помощью диаграммы:
Есть задача, которую мы хотим решить при помощи ДП, т.е. найти какой-то план ее решения. Допустим эта задача сложна и сразу решить мы ее не можем. Мы берем малую подзадачу и решаем сначала ее (для x1
). Затем используя это малое решение x1
, и не меняя структуру этого решения, решаем следующую задачу уже с x1
и x2
. И т.д.
Более подробно неформальное объяснение рассматривается ниже.
Примеры, решаемых при помощи динамического программирования задач
Сначала рассмотрим задачи оптимизации (задачи 1-5):
- Маршрут оптимальной длины
- Замена машины (минимизация расходов)
- Биржевой портфель
- Составление плана оптимального производства (логистика)
- Игры (вероятностные или не вероятностные)
- Графы и деревья
- Задача о размене монет или количество способов вернуть сдачу
Целевой функцией здесь является расстояние от А до Б. Т.е. наша цель — минимизировать расстояние.
А что является переменной выбора? Для того, чтобы найти кратчайший путь, надо каждый раз принимать решения. Т.е. в каждой точке или на каждом перекрестке необходимо принимать решения: куда повернуть или ехать прямо.
Целевая функция: минимизация расходов (либо на издержки на поддержку старого автомобиля, либо на покупку нового).
Переменная выбора: каждый год принимать решение продать машину или оставить.
Целевая функция: максимизация средних доходов, т.к. на бирже доход получается вероятностным путем, т.е. это статистический процесс, вероятностный.
Переменная выбора: то, какой портфель вложений будет: сколько акций и какой фирмы нам необходимо купить.
Целевая функция: максимизация прибыли.
Переменная выбора: выбор того, сколько необходимо изготовить стульев или столов, чтобы хватило рабочей силы.
Целевая функция: максимизация вероятности выигрыша или максимизация среднего выигрыша и т.д.
Переменная выбора здесь зависит от конкретной игры.
Задачи 1 — 5 — это примеры задач оптимизации.
Комбинаторика:
Это краткое описание задач для динамического программирования, которые подробно будут рассмотрены позднее.
Понятие динамического программирования
Неформальное объяснение оптимальности подзадач ДП.
Рассмотрим неформальную идею ДП.
Итак, возьмем пример с заводом, изготавливающим мебель.
Для достижения цели максимизации прибыли необходимо решить множество подзадач:
- сколько стульев произвести — переменная
X1
, - сколько столов произвести — переменная
X2
, - сколько нанять работников — переменная
X3
, - …
Хn
.
При большом количестве подзадач сложно понять, как решать такую задачу. Как правило, решить одну малую задачу проще, чем решить большую задачу, состоящую из маленьких.
Поэтому ДП предлагает следующее:
- берем одну подзадачу с переменной
X1
, об остальных подзадачах пока забываем. - После того, как найдем оптимальное решение для первой подзадачи, берем подзадачу для двух переменных
Х1
иХ2
, и решаем ее с помощью уже найденного решения для первой подзадачи. - Получаем решение уже для большей подзадачи, где фигурируют переменные
Х1
иХ2
. Затем, используя полученное решение, берем подзадачи, охватывающиеX1
,X2
иХ3
. - И так продолжаем пока не получим решение для всей общей задачи.
Например, завод производит только стулья. У директора стоит задача получения максимальной прибыли с продажи стульев.
Формальная идея ДП
Часто при постановке задачи кажущимся оптимальным решением является перебор всех возможных вариантов. Однако, вследствии очень большого количества таких вариантов и, как результат, перегрузки памяти компьютера, такой способ не всегда приемлем.
Кроме того, может возникнуть такой вопрос: для того чтобы найти, например, минимум или максимум, почему бы нам не найти производную? или не использовать множества Ла-Гранжа, или другие методы аппарата математического анализа? Зачем нужно ДП, если есть большой арсенал средств?
Дело в том, что:
В основе динамического программирования лежит идея решения поставленной задачи путем деления ее на отдельные части (подзадачи, этапы), решение этих подзадач и последующего объединения этих решений в одно общее решение. Часто большинство из подзадач абсолютно одинаковы.
При этом важно, что при решении более сложной задачи, мы не решаем заново маленькую подзадачу, а используем уже решенный ответ этой подзадачи.
На графике это может выглядеть так:
Когда мы решаем задачу с производными, множествами Ла-Гранжа и т.п., то мы работаем с непрерывными функциями. При решении же задач ДП мы будем работать в основном с дискретными функциями, поэтому говорить здесь о применении непрерывных функций неуместно.
По этой причине во многих задачах, но не во всех, применение аппарата математического анализа будет неприемлемым.
Простой пример решения задач при помощи ДП
Рассмотрим вариант решения задачи с помощью динамического программирования.
n
чисел: 1 + 2 + 3 + ... + n
В чем состоит якобы «сложность» данной задачи: в том, что необходимо сразу взять большое количество чисел и получить ответ.
Попробуем применить к задаче идеи ДП и решить ее, разбивая на простые подзадачи.
(В ДП всегда необходимо сначала определить начальные условия или условие)
- Начнем с суммы одного первого элемента, т.е. просто берем первый элемент:
F(1) = 1
- теперь с помощью решения для первого элемента, решим
F(2) = F(1) + 2 = 1 + 2 = 3
, т.е. надо взять сумму первого элемента и добавить к нему второй элемент F(3) = F(2) + 3 = 6
- по аналогии продолжаем и получаем целевую функцию:
F(n) = F(n-1) + An
Итак, что мы сделали: определили порядок и вычленили подзадачи, затем решили каждую из них, опираясь на решение предыдущей.
Простой пример, где пока неоправданно используется ДП (искусственно), демонстрирует принцип идей ДП.
Рассмотрим еще один пример.
n
ступенек, перед которой находится человек, который за 1 шаг умеет подниматься либо на следующую ступеньку, либо перепрыгивает через одну ступеньку. Вопрос: сколькими способами он может попасть на последнюю ступеньку?Решение:
Рассмотрим самые простые варианты (подзадачи):
- Если лесница состоит из 1 ступеньки:
- из 2 ступенек:
- из 3 ступенек:
f(a3) = 3
(1 — перешагнуть первую ступеньку, 2 — шагнуть на первую и перешагнуть вторую, 3 — прошагать все 3 ступеньки).
Рассмотрим пример из i
ступенек
Как мы можем попасть на i
ступеньку:
- с
i-1
ступеньки, а наi-1
ступеньку мы могли попастьai-1
способами - с
i-2
ступеньки, а наi-2
ступеньку мы могли попастьai-2
способами
Например, как попасть на 4-ю ступеньку:
- У нас есть пример шагов до второй ступеньки, к каждому способу которого прибавляем полный шаг через две ступеньки.
- У нас есть 3 способа попасть на третью ступеньку, если мы к каждому из них добавим маленький шажок на одну ступеньку выше, то получим 3 способа попасть на 4 ступеньку.
Т.о., общее количество способов попасть на i
ступеньку:
f(ai) = f(ai-1) + f(ai-2)
Определим начальные значения, с которых следует начинать решать задачу.
Если начинать с 1, то формула соответствует нахождению последовательности чисел Фибоначчи.
f(a1) = 1
А можем посчитать и a0
, начав с 0 , т.е. с нулевой ступеньки попасть на нулевую — такой способ 1, просто стоять на месте.
a0 = 1
Мы видим, что задача по сути комбинаторная (т.е. количество способов сделать что-либо) свелась к вычислению некоторой рекуррентной последовательности.
Дополните код:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var c:integer; procedure getKolSposob(i,n: integer); begin writeln (i+n,' '); inc(c); if ... then getKolSposob(...,...) end; begin c:=1; getKolSposob(0,1); end. |
Решение 15-го типа заданий ЕГЭ (Графы. Поиск количества путей).