
- •5.1. Вопросы для промежуточного и итогового контроля
- •6. Критерии оценки знаний
- •Список рекомендуемой литературы
- •Теоретическая часть
- •Тема 1. Экстремальные задачи и методы их решения.
- •Выбор метода решения - один из важнейших этапов при решении задач оптимизации. Можно выделить следующие группы методов:
- •Тема 3. Классические задачи, решаемые методом динамического программирования.
- •План решения задачи методом динамического программирования.
- •Определение. Последовательность Фибоначчи определяется следующим образом:
- •Примеры задач, решаемых при помощи динамического программирования. Наибольшая общая подпоследовательность. (ноп, Longest Common Subsequence, lcs)
- •Решение
- •Примеры задач, решаемых при помощи динамического программирования. Наибольшая возрастающая подпоследовательность. (нвп, Longest Increasing Subsequence, lis)
- •Решение
- •Оптимизация метода динамического программирования (на примере задачи нахождения числа сочетаний )
- •Тема 4. Системы и процессы.
- •Тема 5. Дискретные и непрерывные задачи.
- •Тема 6. Динамическое программирование и линейное программирование
- •Тема 7. Задачи поиска оптимальных путей на графах с неотрицательными весами ребер.
- •Тема 8. Динамическое программирование в экономических задачах.
- •Алгоритм обратной прогонки. Алгоритм прямой прогонки.
- •Алгоритм обратной прогонки
- •Алгоритм прямой прогонки
- •Тема 9. Оптимальное управление.
- •Тема 10. Особенности решения задач динамического программирования. Примеры.
- •Тема 11. Применение метода динамического программирования к задачам синтеза расписаний обслуживания.
- •Однопроцессорные задачи синтеза расписаний обслуживания конечных детерминированных потоков заявок
- •Задачи синтеза расписаний обслуживания для систем с параллельными и последовательными процессорами
- •Задачи оптимального обслуживания стационарных объектов, расположенных в одномерной зоне
- •Тема 12. Труднорешаемые задачи. Полиномиально разрешимые конкретизации, приближенные и эвристические алгоритмы
- •Полиномиально разрешимые и np-трудные задачи
- •Полиномиально разрешимые подклассы труднорешаемых задач
- •Принципы построения приближенных и эвристических алгоритмов
- •Эвристические алгоритмы для задач синтеза расписаний обслуживания
- •Глоссарий
- •Сборник заданий, задач, примеров, упражнений и т.Д.
- •Задача 7. Максимальная подпоследовательность. Дана числовая последовательность, требуется найти длину наибольшей возрастающей подпоследовательности.
Тема 3. Классические задачи, решаемые методом динамического программирования.
План лекции:
Динамическое программирование.
Формальная формулировка задачи динамического программирования.
Принцип оптимальности Беллмана.
История.
Классические задачи динамического программирования.
План решения задачи методом динамического программирования.
Примеры типичных задач, решаемых при помощи динамического программирования.
Числа Фибоначчи.
Черепашка.
Подпалиндром.
Наибольшая общая подпоследовательность.
Наибольшая возрастающая подпоследовательность.
Оптимизация метода динамического программирования.
Краткое содержание лекции
Динамическое программирование
Динамическое программирование в математике и теории вычислительных систем – способ решения сложных задач путём разбиения их на более простые подзадачи. Он применим к задачам с оптимальной подструктурой, выглядящим как набор перекрывающихся подзадач, сложность которых чуть меньше исходной. В этом случае время вычислений, по сравнению с «наивными» методами, можно значительно сократить. Ключевая идея в динамическом программировании достаточно проста. Как правило, чтобы решить поставленную задачу, требуется решить отдельные части задачи (подзадачи), после чего объединить решения подзадач в одно общее решение. Часто многие из этих подзадач одинаковы. Подход динамического программирования состоит в том, чтобы решить каждую подзадачу только один раз, сократив тем самым количество вычислений, что полезно в случаях, когда число повторяющихся подзадач экспоненциально велико.
Метод динамического программирования сверху – это простое запоминание результатов решения тех подзадач, которые могут повторно встретиться в дальнейшем. Динамическое программирование снизу включает в себя переформулирование сложной задачи в виде рекурсивной последовательности более простых подзадач.
Формальная формулировка задачи динамического программирования
Дано:
множество состояний
в том числе начальное и конечное
множество возможных переходов из одного состояния в другое
с каждым переходом связывается числовой параметр
интерпретируется как затраты, выгода, расстояние, время и т.п.
Найти:
оптимальную последовательность переходов (путь) из начального состояния в конечное
максимум или минимум суммы числовых параметров
предполагается, что хотя бы один путь из начального состояния в конечное существует
Пример. Найти оптимальный путь от узла 0 к узлу 18.
Принцип оптимальности Беллмана
Если вершины A и B лежат на оптимальном пути между вершинами 0 и X, то часть оптимального пути от 0 до X между вершинами A и B непременно является оптимальным путём от A до B.
Следствие
Чтобы найти оптимальный путь от 0 до A, достаточно исследовать продолжения к A всех оптимальных путей до вершин, предшествующих A
Продолжения неоптимальных путей к предшествующим вершинам можно не просчитывать: они никогда не дадут оптимального пути к A
Принцип Беллмана позволяет построить простую и эффективную вычислительную процедуру для решения задач динамического программирования
История
Словосочетание «динамическое программирование» впервые было использовано в 1940-х годах Р. Беллманом для описания процесса нахождения решения задачи, где ответ на одну задачу может быть получен только после решения задачи, «предшествующей» ей. В 1953 г. он уточнил это определение до современного. Первоначально эта область была основана, как системный анализ и инжиниринг, которая была признана IEEE. Вклад Беллмана в динамическое программирование был увековечен в названии уравнения Беллмана, центрального результата теории динамического программирования, который переформулирует оптимизационную задачу в рекурсивной форме.
Идея динамического программирования
Рис. Нахождение кратчайшего пути в графе из одной вершины в другую, используя оптимальную подструктуру; прямая линия обозначает простое ребро; волнистая линия обозначает кратчайший путь между вершинами, которые она соединяет (промежуточные вершины пути не показаны); жирной линией обозначен итоговый кратчайший путь.
Рис. Граф подзадач (ребро означает, что одна задача зависит от решения другой) для чисел Фибоначчи (граф — ациклический).
Оптимальная подструктура в динамическом программировании означает, что оптимальное решение подзадач меньшего размера может быть использовано для решения исходной задачи.Например, кратчайший путь в графе из одной вершины (обозначим s) в другую (обозначим t) может быть найден так: сначала считаем кратчайший путь из всех вершин, смежных с s, до t, а затем, учитывая веса ребер, которыми s соединена со смежными вершинами, выбираем лучший путь до t (через какую вершину лучше всего пойти). В общем случае мы можем решить задачу, в которой присутствует оптимальная подструктура, проделывая следующие три шага.
Разбиение задачи на подзадачи меньшего размера.
Нахождение оптимального решения подзадач рекурсивно, проделывая такой же трехшаговый алгоритм.
Использование полученного решения подзадач для конструирования решения исходной задачи.
Подзадачи решаются делением их на подзадачи ещё меньшего размера, пока не приходят к тривиальному случаю задачи, решаемой за константное время.
Перекрывающиеся подзадачи в динамическом программировании означают подзадачи, которые используются для решения некоторого количества задач (не одной) большего размера (то есть мы несколько раз проделываем одно и то же). Примером является вычисление последовательности Фибоначчи, F3 = F2 + F1 и F4 = F3 + F2 – даже в таком тривиальном случае вычисления всего двух чисел Фибоначчи мы уже посчитали F2 дважды. Если продолжать дальше и посчитать F5, то F2 посчитается ещё два раза, так как для вычисления F5 будут нужны опять F3 и F4. В результате простой рекурсивный подход будет расходовать время на вычисление решения для задач, которые он уже решал.
Чтобы избежать такого хода событий будем сохранять решения подзадач, которые уже решены, и когда снова потребуется решение подзадачи, просто достанем его из памяти. Этот подход называется кэширование. Можно проделывать и дальнейшие оптимизации – например, если мы точно уверены, что решение подзадачи нам больше не потребуется, можно выкинуть его из памяти, освободив её для других нужд, или если процессор простаивает и мы знаем, что решение некоторых, ещё не посчитанных подзадач, нам понадобится в дальнейшем, мы можем решить их заранее.
Подводя итоги вышесказанного можно сказать, что динамическое программирование пользуется следующими свойствами задачи:
перекрывающиеся подзадачи;
оптимальная подструктура;
возможность запоминания решения часто встречающихся подзадач.
Динамическое программирование обычно придерживается двух подходов к решению задач:
нисходящее динамическое программирование: задача разбивается на подзадачи меньшего размера, они решаются и затем комбинируются для решения исходной задачи. Используется запоминание для решений часто встречающихся подзадач.
восходящее динамическое программирование: все подзадачи, которые впоследствии понадобятся для решения исходной задачи просчитываются заранее и затем используются для построения решения исходной задачи. Этот способ лучше нисходящего программирования в смысле размера необходимого стека и количества вызова функций, но иногда бывает нелегко заранее выяснить, решение каких подзадач нам потребуется в дальнейшем.
Известны сериальное динамическое программирование, включённое во все учебники по исследованию операций, и несериальное динамическое программирование (НСДП). Обычное динамическое программирование является частным случаем несериального динамического программирования, когда граф взаимосвязей переменных – просто путь. НСДП, являясь естественным и общим методом для учета структуры задачи оптимизации, рассматривает множество ограничений и/или целевую функцию как рекурсивно вычислимую функцию. Это позволяет находить решение поэтапно, на каждом из этапов используя информацию, полученную на предыдущих этапах, причём эффективность этого алгоритма прямо зависит от структуры графа взаимосвязей переменных. Если этот граф достаточно разрежен, то объём вычислений на каждом этапе может сохраняться в разумных пределах.
Одним из основных свойств задач, решаемых с помощью динамического программирования, является аддитивность. Неаддитивные задачи решаются другими методами. Например, многие задачи по оптимизации инвестиций компании являются неаддитивными и решаются с помощью сравнения стоимости компании при проведении инвестиций и без них.
Классические задачи динамического программирования
Задача о наибольшей общей подпоследовательности: даны две последовательности, требуется найти самую длинную общую подпоследовательность.
Задача поиска наибольшей увеличивающейся подпоследовательности: дана последовательность, требуется найти самую длинную неубывающую подпоследовательность.
Задача о редакционном расстоянии (Расстояние Левенштейна): даны две строки, требуется найти минимальное количество стираний, замен и добавлений символов, преобразующих одну строку в другую.
Задача о вычислении чисел Фибоначчи
Задача о порядке перемножения матриц: даны матрицы A1, ..., An, требуется минимизировать количество скалярных операций для их перемножения.
Задача о выборе траектории
Задача последовательного принятия решения
Задача об использовании рабочей силы
Задача управления запасами
Задача о ранце: из неограниченного множества предметов со свойствами «стоимость» и «вес», требуется отобрать некое число предметов таким образом, чтобы получить максимальную суммарную стоимость при ограниченном суммарном весе.
Алгоритм Флойда-Уоршелла: найти кратчайшие расстояния между всеми вершинами взвешенного ориентированного графа.
Алгоритм Беллмана – Форда: найти кратчайший путь во взвешенном графе между двумя заданными вершинами.
Максимальное независимое множество вершин в дереве: дано дерево, найти максимальное множество вершин, никакие две из которых не связаны ребром.