Динамическое программирование — различия между версиями
Строка 37: | Строка 37: | ||
==Оптимальность для подзадач== | ==Оптимальность для подзадач== | ||
− | Важнейшее свойство задач, которое позволяет решать их с помощью динамического программирования, это оптимальность для подзадач. В зависимости от формулировки задачи, будь то динамическое программирование на отрезке, на префиксе, на дереве, термин оптимальности для подзадач может быть различным, но, в целом, формулируется так: если есть оптимальное решение для некоторой подзадачи, которая возникает в процессе решения задачи, то именно его нужно использовать для решения задачи в целом | + | Важнейшее свойство задач, которое позволяет решать их с помощью динамического программирования, это оптимальность для подзадач. В зависимости от формулировки задачи, будь то динамическое программирование на отрезке, на префиксе, на дереве, термин оптимальности для подзадач может быть различным, но, в целом, формулируется так: если есть оптимальное решение для некоторой подзадачи, которая возникает в процессе решения задачи, то именно его нужно использовать для решения задачи в целом. |
==Принцип оптимальности на префиксе== | ==Принцип оптимальности на префиксе== | ||
Строка 45: | Строка 45: | ||
=== Примеры задач === | === Примеры задач === | ||
:* [[Кратчайший путь в ациклическом графе]] | :* [[Кратчайший путь в ациклическом графе]] | ||
+ | :* [[Задача о числе путей в ациклическом графе]] | ||
+ | |||
== Принцип оптимальности на подотрезках== | == Принцип оптимальности на подотрезках== | ||
Строка 59: | Строка 61: | ||
:* [[Задача о выводе в контекстно-свободной грамматике, алгоритм Кока-Янгера-Касами]] | :* [[Задача о выводе в контекстно-свободной грамматике, алгоритм Кока-Янгера-Касами]] | ||
:* [[Задача об оптимальном префиксном коде с сохранением порядка. Монотонность точки разреза]] | :* [[Задача об оптимальном префиксном коде с сохранением порядка. Монотонность точки разреза]] | ||
+ | :* [[Задача о наибольшей общей подпоследовательности]] | ||
+ | :* [[Задача о редакционном расстоянии, алгоритм Вагнера-Фишера]] | ||
+ | :* [[Задача о расстоянии Дамерау-Левенштейна]] | ||
+ | :* [[Задача о наибольшей общей подпоследовательности]] | ||
== Принцип оптимальности на подмножествах == | == Принцип оптимальности на подмножествах == | ||
+ | Требуется посчитать функцию <math>f(A)</math>, где <math>A</math> {{---}} некоторое множество. Принцип состоит в следующем: пусть для всех множеств <math>B</math> (где <math>B \in A</math>) известен оптимальный ответ для функции <math>f(B)</math>. Тогда будем вычислять <math>f(A)</math> через такие <math>f(B)</math>. | ||
+ | |||
+ | === Примеры задач === | ||
* [[Задача коммивояжера, ДП по подмножествам]] | * [[Задача коммивояжера, ДП по подмножествам]] | ||
+ | |||
+ | ==Мемоизация== | ||
+ | |||
+ | {{Определение | ||
+ | |definition = | ||
+ | '''Мемоизация''' (англ. memoization) — сохранение результатов выполнения функций для предотвращения повторных вычислений. | ||
+ | }} | ||
+ | |||
+ | Это один из способов оптимизации, применяемый для увеличения скорости выполнения компьютерных программ. Перед вызовом функции проверяется, вызывалась ли функция ранее: | ||
+ | *если не вызывалась, функция вызывается и результат её выполнения сохраняется; | ||
+ | *если вызывалась, используется сохранённый результат. | ||
+ | В качестве примера рассмотрим задачу о нахождении числа Фибоначчи под номером <math>n</math>. Без мемоизации: | ||
+ | |||
+ | '''int''' Fibonacci('''int''' n): | ||
+ | '''if''' n<=1 | ||
+ | '''return''' 1 | ||
+ | a=Fibonacci(n-1) | ||
+ | b=Fibonacci(n-2) | ||
+ | '''return''' a+b | ||
+ | |||
+ | С мемоизацией: | ||
+ | '''int[]''' Fib=-1 <font color=green>// массив, в котором под номером <math>i</math> хранится число Фибоначчи с номером <math>i</math></font> | ||
+ | '''int''' Fibonacci('''int''' n): | ||
+ | '''if''' n<=1 | ||
+ | '''return''' 1 | ||
+ | '''if''' Fib[n]!=-1 <font color=green>// проверка на то, не посчитали ли мы это число раньше</font> | ||
+ | '''return''' Fib[n] | ||
+ | Fib[n-1]=Fibonacci(n-1) | ||
+ | Fib[n-2]=Fibonacci(n-2) | ||
+ | '''return''' Fib[n-1]+Fib[n-2] | ||
==Источники информации== | ==Источники информации== | ||
Строка 71: | Строка 110: | ||
* [http://en.wikipedia.org/wiki/Greedy_algorithm Wikipedia {{---}} Greedy algorithm] | * [http://en.wikipedia.org/wiki/Greedy_algorithm Wikipedia {{---}} Greedy algorithm] | ||
* [https://en.wikipedia.org/wiki/Dynamic_programming Wikipedia {{---}} Dynamic programming] | * [https://en.wikipedia.org/wiki/Dynamic_programming Wikipedia {{---}} Dynamic programming] | ||
+ | * [https://en.wikipedia.org/wiki/Memoization Wikipedia {{---}} Memoization] | ||
* [https://ru.wikipedia.org/wiki/%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC Википедия {{---}} Жадный алгоритм] | * [https://ru.wikipedia.org/wiki/%D0%96%D0%B0%D0%B4%D0%BD%D1%8B%D0%B9_%D0%B0%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC Википедия {{---}} Жадный алгоритм] | ||
* [http://ru.wikipedia.org/wiki/%C4%E8%ED%E0%EC%E8%F7%E5%F1%EA%EE%E5_%EF%F0%EE%E3%F0%E0%EC%EC%E8%F0%EE%E2%E0%ED%E8%E5 Википедия {{---}} Динамическое программирование] | * [http://ru.wikipedia.org/wiki/%C4%E8%ED%E0%EC%E8%F7%E5%F1%EA%EE%E5_%EF%F0%EE%E3%F0%E0%EC%EC%E8%F0%EE%E2%E0%ED%E8%E5 Википедия {{---}} Динамическое программирование] | ||
+ | * [https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%BC%D0%BE%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F Википедия {{---}} Мемоизация] | ||
[[Категория:Дискретная математика и алгоритмы]] | [[Категория:Дискретная математика и алгоритмы]] | ||
[[Категория:Динамическое программирование]] | [[Категория:Динамическое программирование]] |
Версия 00:47, 3 января 2017
Определение: |
Динамическое программирование (англ. dynamic programming) — это когда у нас есть задача, которую непонятно как решать, и мы разбиваем ее на меньшие задачи, которые тоже непонятно как решать. |
<wikitex>
Содержание
Процесс разработки алгоритмов динамического программирования
В процессе составления алгоритмов динамического программирования, требуется следовать последовательности из четырёх действий:
- Описать структуру оптимального решения.
- Рекурсивно определить значение оптимального решения.
- Вычислить значение оптимального решения с помощью метода восходящего анализа.
- Составить оптимальное решение на основе полученной информации.
Оптимальная подструктура
Задача имеет оптимальную подструктуру, если её оптимальное решение может быть рационально составлено из оптимальных решений её подзадач.Наличие оптимальной подструктуры в задаче используется для определения применимости динамического программирования и жадных алгоритмов для решения оной. Например, задача по нахождению кратчайшего пути между некоторыми вершинами графа содержит в себе оптимальное решение подзадач.
Многие задачи, решаемые динамическим программированием, можно определить как поиск в заданном ориентированном ациклическом графе кратчайшего пути от одной вершины к другой.
Отсутствие оптимальной подструктуры
Иногда оптимальная подструктура может отсутствовать в задаче. Рассмотрим задачу, в которой имеется ориентированный граф $G = (V, E)$ и вершины $u, v \in V$, задачу по определению простого пути от вершины $u$ к вершине $v$, состоящий из максимального количества рёбер.
Рассмотрим путь $q \rightarrow r \rightarrow t$, который является самым длинным простым путем $q \rightsquigarrow t$. Является ли путь $q \rightarrow r$ самым длинным путем $q \rightsquigarrow r$? Нет, поскольку простой путь $q \rightarrow s \rightarrow t \rightarrow r$ длиннее. Является ли путь $r \rightarrow t$ самым длинным путем $r \rightsquigarrow t$? Снова нет, поскольку простой путь $r \rightarrow q \rightarrow s \rightarrow t$ длиннее. Таким образом, в задаче о поиске самого длинного невзвешенного пути не возникает никаких оптимальных подструктур. Для этой задачи до сих пор не найдено ни одного эффективного алгоритма, работающего по принципу динамического программирования. Фактически, это NP-полная задача, т.е. вряд ли ее можно решить в течение полиномиального времени.
Оптимальность для подзадач
Важнейшее свойство задач, которое позволяет решать их с помощью динамического программирования, это оптимальность для подзадач. В зависимости от формулировки задачи, будь то динамическое программирование на отрезке, на префиксе, на дереве, термин оптимальности для подзадач может быть различным, но, в целом, формулируется так: если есть оптимальное решение для некоторой подзадачи, которая возникает в процессе решения задачи, то именно его нужно использовать для решения задачи в целом.
Принцип оптимальности на префиксе
Рассмотрим некий необратимый процесс производства и представим его в виде ориентированного и ациклического графа. Процесс проходит некий ряд состояний. Началом производства (первым состоянием) обозначим вершину графа $S$, а конец производства (последнее состояние) $T$. Процесс требует оптимизации, т.е. требуется найти оптимальный путь $S \rightsquigarrow T$. Он проходит через вершину графа $U$. Префикс оптимального пути $S \rightsquigarrow U$ является оптимальным путём $S \rightsquigarrow U$. Теперь рассмотрим принцип оптимальности для динамического программирования на префиксе. Итак, имеем некоторый оптимальный путь $S \rightsquigarrow T$, который проходит через $U$. Пусть префикс $ \Delta U$, т.е. путь от $S \rightsquigarrow U$, неоптимален. Тогда заменим неоптимальную часть $S \rightsquigarrow U$ пути $S \rightsquigarrow T$ оптимальной, а путь $U \rightsquigarrow T$ добавим в конец. Получим оптимальный путь $S \rightsquigarrow T$. Принцип оптимальности для подзадач выполняется. Т.е. чтобы получить оптимальный путь из одной вершины графа в другую, префиксы меньших путей должны быть оптимальными.
Примеры задач
Принцип оптимальности на подотрезках
Требуется посчитать функцию $f(1, n)$. Принцип состоит в следующем: пусть для всех отрезков $i$, $j$ (где
Доказательство:
- Так
- Данное равенство следует из факта, что выгодно включить в максимальный подпалиндром символы $s(i)$ и $s(j)$.
Примеры задач
- Задача о расстановке знаков в выражении
- Задача о порядке перемножения матриц
- Задача о выводе в контекстно-свободной грамматике, алгоритм Кока-Янгера-Касами
- Задача об оптимальном префиксном коде с сохранением порядка. Монотонность точки разреза
- Задача о наибольшей общей подпоследовательности
- Задача о редакционном расстоянии, алгоритм Вагнера-Фишера
- Задача о расстоянии Дамерау-Левенштейна
- Задача о наибольшей общей подпоследовательности
Принцип оптимальности на подмножествах
Требуется посчитать функцию
, где — некоторое множество. Принцип состоит в следующем: пусть для всех множеств (где ) известен оптимальный ответ для функции . Тогда будем вычислять через такие .Примеры задач
Мемоизация
Определение: |
Мемоизация (англ. memoization) — сохранение результатов выполнения функций для предотвращения повторных вычислений. |
Это один из способов оптимизации, применяемый для увеличения скорости выполнения компьютерных программ. Перед вызовом функции проверяется, вызывалась ли функция ранее:
- если не вызывалась, функция вызывается и результат её выполнения сохраняется;
- если вызывалась, используется сохранённый результат.
В качестве примера рассмотрим задачу о нахождении числа Фибоначчи под номером
. Без мемоизации:int Fibonacci(int n): if n<=1 return 1 a=Fibonacci(n-1) b=Fibonacci(n-2) return a+b
С мемоизацией:
int[] Fib=-1 // массив, в котором под номеромхранится число Фибоначчи с номером int Fibonacci(int n): if n<=1 return 1 if Fib[n]!=-1 // проверка на то, не посчитали ли мы это число раньше return Fib[n] Fib[n-1]=Fibonacci(n-1) Fib[n-2]=Fibonacci(n-2) return Fib[n-1]+Fib[n-2]
Источники информации
- Т. Кормен. «Алгоритмы. Построение и анализ» второе издание, Глава 15
- T. H. Cormen. «Introduction to Algorithms» third edition, Chapter 15
Ссылки
- Wikipedia — Optimal substructure
- Wikipedia — Greedy algorithm
- Wikipedia — Dynamic programming
- Wikipedia — Memoization
- Википедия — Жадный алгоритм
- Википедия — Динамическое программирование
- Википедия — Мемоизация
</wikitex>