<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=128.69.233.190&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=128.69.233.190&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/128.69.233.190"/>
		<updated>2026-06-11T14:37:22Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=75098</id>
		<title>Динамическое программирование</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%94%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5&amp;diff=75098"/>
				<updated>2020-10-24T14:14:26Z</updated>
		
		<summary type="html">&lt;p&gt;128.69.233.190: исправил мемоизацию Fibonacci. запоминать нужно текущее значение, а не предыдущие&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
''Динамическое программирование — это когда у нас есть задача, которую непонятно как решать, и мы разбиваем ее на меньшие задачи, которые тоже непонятно как решать. (с) А.Кумок''&lt;br /&gt;
&lt;br /&gt;
==Процесс разработки алгоритмов динамического программирования==&lt;br /&gt;
В процессе составления алгоритмов динамического программирования, требуется следовать последовательности из четырёх действий:&lt;br /&gt;
# Описать структуру оптимального решения.&lt;br /&gt;
# Рекурсивно определить значение оптимального решения.&lt;br /&gt;
# Вычислить значение оптимального решения с помощью метода восходящего анализа.&lt;br /&gt;
# Составить оптимальное решение на основе полученной информации.&lt;br /&gt;
&lt;br /&gt;
==Оптимальная подструктура==&lt;br /&gt;
Задача имеет оптимальную подструктуру, если её оптимальное решение может быть рационально составлено из оптимальных решений её подзадач. [[Файл:FG.png|150px|thumb|Граф подзадач для чисел Фибоначчи]] &lt;br /&gt;
Наличие оптимальной подструктуры в задаче используется для определения применимости динамического программирования и жадных алгоритмов для решения оной. Например, &lt;br /&gt;
задача по нахождению кратчайшего пути между некоторыми вершинами графа содержит в себе оптимальное решение подзадач.&lt;br /&gt;
&lt;br /&gt;
Многие задачи, решаемые динамическим программированием, можно определить как поиск в заданном ориентированном ациклическом графе [[Кратчайший_путь_в_ациклическом_графе|кратчайшего пути]] от одной вершины к другой.&lt;br /&gt;
[[Файл:ULP.JPG|thumb|right|150px|Задача о самом длинном невзвешенном пути]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Отсутствие оптимальной подструктуры===&lt;br /&gt;
Иногда оптимальная подструктура может отсутствовать в задаче.  &lt;br /&gt;
Рассмотрим задачу, в которой имеется ориентированный граф $G = (V, E)$ и вершины $u, v \in V$, задачу по определению простого пути от вершины $u$ к вершине $v$, состоящий из максимального количества рёбер. &lt;br /&gt;
&lt;br /&gt;
Рассмотрим путь $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$ длиннее. &lt;br /&gt;
Таким образом, в задаче о поиске самого длинного невзвешенного пути не возникает никаких оптимальных подструктур. Для этой задачи до сих пор не найдено ни одного эффективного алгоритма, работающего по принципу динамического программирования. Фактически, это [[Классы NP, coNP, Σ₁, Π₁|NP-полная задача]], т.е. вряд ли ее можно решить в течение полиномиального времени. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Оптимальность для подзадач==&lt;br /&gt;
Важнейшее свойство задач, которое позволяет решать их с помощью динамического программирования, это оптимальность для подзадач. В зависимости от формулировки задачи, будь то динамическое программирование на отрезке, на префиксе, на дереве, термин оптимальности для подзадач может быть различным, но, в целом, формулируется так: если есть оптимальное решение для некоторой подзадачи, которая возникает в процессе решения задачи, то именно его нужно использовать для решения задачи в целом.&lt;br /&gt;
&lt;br /&gt;
===Принцип оптимальности на префиксе===&lt;br /&gt;
[[Файл:ST.jpg|200px|thumb|right]]&lt;br /&gt;
Рассмотрим некий необратимый процесс производства и представим его в виде ориентированного и ациклического графа. Процесс проходит некий ряд состояний. Началом производства (первым состоянием) обозначим вершину графа $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$. Принцип оптимальности для подзадач выполняется. Т.е. чтобы получить оптимальный путь из одной вершины графа в другую, префиксы меньших путей должны быть оптимальными.&lt;br /&gt;
&lt;br /&gt;
В качестве примера рассмотрим следующую задачу: пусть дан ациклический ориентированный взвешенный граф, требуется найти вес кратчайшего пути из u в v. Воспользуемся принципом оптимальности на префиксе.&amp;lt;br /&amp;gt;&lt;br /&gt;
Пусть &amp;lt;tex&amp;gt;d&amp;lt;/tex&amp;gt; — функция, где &amp;lt;tex&amp;gt;d(i)&amp;lt;/tex&amp;gt; — вес кратчайшего пути из &amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;. Ясно, что &amp;lt;tex&amp;gt;d(u)&amp;lt;/tex&amp;gt; равен &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;. Пусть &amp;lt;tex&amp;gt;w(i, j)&amp;lt;/tex&amp;gt; {{---}} вес ребра из &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;. Будем обходить граф в порядке [[Использование_обхода_в_глубину_для_топологической_сортировки | топологической сортировки]]. Получаем следующие соотношения: &amp;lt;br /&amp;gt;&lt;br /&gt;
: &amp;lt;tex&amp;gt; d(i) = \min\limits_{\mathop{j:j \rightsquigarrow i}} (d(j) + w(j, i)) &amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Так как мы обходим граф в порядке [[Использование_обхода_в_глубину_для_топологической_сортировки |топологической сортировки]], то на &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ом шаге всем &amp;lt;tex&amp;gt;d(j)&amp;lt;/tex&amp;gt; (&amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; такие, что существует ребро из &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;) уже присвоены оптимальные ответы, и, следовательно, &amp;lt;tex&amp;gt;d(i)&amp;lt;/tex&amp;gt; также будет присвоен оптимальный ответ.&lt;br /&gt;
==== Примеры задач ====&lt;br /&gt;
:* [[Кратчайший путь в ациклическом графе]]&lt;br /&gt;
:* [[Задача о числе путей в ациклическом графе]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Принцип оптимальности на подотрезках===&lt;br /&gt;
Требуется посчитать функцию $f(1, n)$. Принцип состоит в следующем: пусть для всех отрезков $i$, $j$ (где &amp;lt;tex&amp;gt; u \leqslant i \leqslant j \leqslant v &amp;lt;/tex&amp;gt;) известен оптимальный ответ для функции $f(i, j)$. Тогда мы будем вычислять $f(u, v)$ через такие $f(i, j)$. В качестве примера рассмотрим следующую классическую задачу: дана строка длины n, нужно найти максимальный подпалиндром (подпоследовательность максимальной длины, которая является палиндромом). Пусть $d(i, j)$ - ответ на задачу для подстроки, начинающаяся с символа $i$ и заканчивающаяся в символе $j$. Ясно, что $d(i, j) = 0$ для всех $i, j,$ таких что $i &amp;gt; j$ и $d(i, i) = 1$ для всех $i$. Пусть нам нужно посчитать значение для $d(i, j)$, причем значение $d$ для всех $l, r$, таких что &amp;lt;tex&amp;gt; i \leqslant l \leqslant r \leqslant j &amp;lt;/tex&amp;gt; уже посчитаны и они оптимальны. Рассмотрим два случая: &amp;lt;br /&amp;gt;&lt;br /&gt;
# &amp;lt;tex&amp;gt; s(i) \neq s(j) &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; d(i, j) = \max(d(i, j - 1), d(i + 1, j)) &amp;lt;/tex&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
# &amp;lt;tex&amp;gt; s(i) = s(j) &amp;lt;/tex&amp;gt;, тогда &amp;lt;tex&amp;gt; d(i, j) = d(i + 1, j - 1) + 2 &amp;lt;/tex&amp;gt; &amp;lt;br /&amp;gt;&lt;br /&gt;
Доказательство:&amp;lt;br /&amp;gt;&lt;br /&gt;
# Так &amp;lt;tex&amp;gt;s(i) \neq s(j)&amp;lt;/tex&amp;gt;, символы  $s(i)$ и $s(j)$ не могут входить в максимальный подпалиндром одновременно, то есть либо $s(i)$ входят в максимальный подпалиндром(тогда его длина $d[i, j - 1]$), либо $s(j)$ входит в максимальный подпалиндром (тогда его длина $d[i + 1, j]$), либо оба не входят в максимальный подпалиндром (тогда его длина $= d[i, j - 1] = d[i + 1, j]$). &amp;lt;br /&amp;gt;&lt;br /&gt;
# Данное равенство следует из факта, что выгодно включить в максимальный подпалиндром символы $s(i)$ и $s(j)$. &lt;br /&gt;
&lt;br /&gt;
==== Примеры задач ====&lt;br /&gt;
:* [[Задача о расстановке знаков в выражении ]]&lt;br /&gt;
:* [[Задача о порядке перемножения матриц]]&lt;br /&gt;
:* [[Задача о выводе в контекстно-свободной грамматике, алгоритм Кока-Янгера-Касами]]&lt;br /&gt;
:* [[Задача об оптимальном префиксном коде с сохранением порядка. Монотонность точки разреза]]&lt;br /&gt;
:* [[Задача о наибольшей общей подпоследовательности]]&lt;br /&gt;
:* [[Задача о редакционном расстоянии, алгоритм Вагнера-Фишера]]&lt;br /&gt;
:* [[Задача о расстоянии Дамерау-Левенштейна]]&lt;br /&gt;
:* [[Задача о наибольшей общей подпоследовательности]]&lt;br /&gt;
&lt;br /&gt;
=== Принцип оптимальности на подмножествах ===&lt;br /&gt;
Требуется посчитать функцию &amp;lt;math&amp;gt;f(A)&amp;lt;/math&amp;gt;, где &amp;lt;math&amp;gt;A&amp;lt;/math&amp;gt; {{---}} некоторое множество. Принцип состоит в следующем: пусть для всех множеств &amp;lt;math&amp;gt;B&amp;lt;/math&amp;gt; (где &amp;lt;math&amp;gt;B \in A&amp;lt;/math&amp;gt;) известен оптимальный ответ для функции &amp;lt;math&amp;gt;f(B)&amp;lt;/math&amp;gt;. Тогда будем вычислять &amp;lt;math&amp;gt;f(A)&amp;lt;/math&amp;gt; через такие &amp;lt;math&amp;gt;f(B)&amp;lt;/math&amp;gt;. В качестве примера рассмотрим задачу о коммивояжере.&lt;br /&gt;
&lt;br /&gt;
Обозначим &amp;lt;tex&amp;gt;d[i][mask]&amp;lt;/tex&amp;gt; как наименьшую стоимость пути из вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt; в вершину &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;, проходящую (не считая вершины &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;) единожды по всем тем и только тем вершинам &amp;lt;tex&amp;gt;j&amp;lt;/tex&amp;gt;, для которых &amp;lt;tex&amp;gt;mask_j = 1&amp;lt;/tex&amp;gt; (т.е. &amp;lt;tex&amp;gt;d[i][mask]&amp;lt;/tex&amp;gt; уже  найденный оптимальный путь от &amp;lt;tex&amp;gt;i&amp;lt;/tex&amp;gt;-ой вершины до &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;-ой, проходящий через те вершины, где &amp;lt;tex&amp;gt;mask_j=1&amp;lt;/tex&amp;gt;. Если &amp;lt;tex&amp;gt;mask_j=0&amp;lt;/tex&amp;gt;,то эти вершины еще не посещены). Тогда воспользуемся принципом оптимальности на подмножествах. Стоимостью минимального гамильтонова цикла в исходном графе будет значение &amp;lt;tex&amp;gt; d[0][2^n-1]&amp;lt;/tex&amp;gt; — стоимость пути из &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;-й вершины в &amp;lt;tex&amp;gt;0&amp;lt;/tex&amp;gt;-ю, при необходимости посетить все вершины.&lt;br /&gt;
&lt;br /&gt;
==== Примеры задач ====&lt;br /&gt;
* [[Задача коммивояжера, ДП по подмножествам]]&lt;br /&gt;
&lt;br /&gt;
==Мемоизация==&lt;br /&gt;
&lt;br /&gt;
{{Определение&lt;br /&gt;
|definition =&lt;br /&gt;
'''Мемоизация''' (англ. memoization) — сохранение результатов выполнения функций для предотвращения повторных вычислений.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
Это один из способов оптимизации, применяемый для увеличения скорости выполнения компьютерных программ. Перед вызовом функции проверяется, вызывалась ли функция ранее:&lt;br /&gt;
*если не вызывалась, функция вызывается и результат её выполнения сохраняется;&lt;br /&gt;
*если вызывалась, используется сохранённый результат.&lt;br /&gt;
В качестве примера рассмотрим задачу о нахождении числа Фибоначчи под номером &amp;lt;math&amp;gt;n&amp;lt;/math&amp;gt;. Без мемоизации:&lt;br /&gt;
&lt;br /&gt;
 '''int''' Fibonacci('''int''' n): &lt;br /&gt;
   '''if''' n &amp;lt;= 1&lt;br /&gt;
     '''return''' 1&lt;br /&gt;
   a = Fibonacci(n - 1)&lt;br /&gt;
   b = Fibonacci(n - 2)&lt;br /&gt;
   '''return''' a + b&lt;br /&gt;
&lt;br /&gt;
С мемоизацией:&lt;br /&gt;
 '''int''' Fibonacci('''int''' n): &lt;br /&gt;
   '''if''' n &amp;lt;= 1&lt;br /&gt;
     '''return''' 1&lt;br /&gt;
   '''if''' fib[n] == -1 &amp;lt;font color=green&amp;gt;// проверка на то, не посчитали ли мы это число раньше; посчитанные числа хранятся в массиве fib&amp;lt;/font&amp;gt;&lt;br /&gt;
     fib[n] = Fibonacci(n - 1) + Fibonacci(n - 2)&lt;br /&gt;
   '''return''' fib[n]&lt;br /&gt;
&lt;br /&gt;
==См.также==&lt;br /&gt;
* [[Динамическое программирование по профилю]]&lt;br /&gt;
* [[Динамика по поддеревьям]]&lt;br /&gt;
&lt;br /&gt;
==Источники информации==&lt;br /&gt;
* Т. Кормен. «Алгоритмы. Построение и анализ» второе издание, Глава 15&lt;br /&gt;
* T. H. Cormen. «Introduction to Algorithms» third edition, Chapter 15&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Optimal_substructure Wikipedia {{---}} Optimal substructure ]&lt;br /&gt;
* [http://en.wikipedia.org/wiki/Greedy_algorithm Wikipedia {{---}} Greedy algorithm]&lt;br /&gt;
* [https://en.wikipedia.org/wiki/Dynamic_programming Wikipedia {{---}} Dynamic programming]&lt;br /&gt;
* [https://en.wikipedia.org/wiki/Memoization Wikipedia {{---}} Memoization]&lt;br /&gt;
* [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 Википедия {{---}} Жадный алгоритм]&lt;br /&gt;
* [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 Википедия {{---}} Динамическое программирование]&lt;br /&gt;
* [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 Википедия {{---}} Мемоизация]&lt;br /&gt;
[[Категория:Дискретная математика и алгоритмы]]&lt;br /&gt;
[[Категория:Динамическое программирование]]&lt;br /&gt;
&amp;lt;/wikitex&amp;gt;&lt;/div&gt;</summary>
		<author><name>128.69.233.190</name></author>	</entry>

	</feed>