Вы находитесь на странице: 1из 27

СОДЕРЖАНИЕ

Введение__________________________________________________________1
1 Динамическое программирование__________________________________3
1.1 Понятия динамического программирования______________________3
1.2 Метод динамического программирования________________________6
1.3 Идеи метода динамического программирования__________________10
1.4 Общая структура динамического программирования_____________12
2 Практические задачи динамического программирования____________14
2.1 Последовательности__________________________________________14
2.2 Одномерное динамическое программирование____________________15
2.3 Двумерное динамическое программирование_____________________16
2.4 Задачи на подпоследовательности______________________________18
Заключение______________________________________________________24
Список использованной литературы__________________________________26
ВВЕДЕНИЕ

В наше время наука уделяет все большое внимание вопросам


организации и управления, это приводит к необходимости анализа сложных
целенаправленных процессов под углом зрения их структуры и организации.
Потребности практики вызвали к жизни специальные методы, которые удобно
объединять под названием «исследование операций». Под этим термином
понимается применение математических, количественных методов для
обоснования решений во всех областях целенаправленной человеческой
деятельности.
Целью исследования операций является выявление наилучшего способа
действия при решении той или иной задачи. Главная роль при этом отводится
математическому моделированию. Для построения математической модели
необходимо иметь строгое представление о цели функционирования
исследуемой системы и располагать информацией об ограничениях, которые
определяют область допустимых значений. Цель и ограничения должны быть
представлены в виде функций.
В моделях исследования операций переменные, от которых зависят
ограничения и целевая функция, могут быть дискретными (чаще всего
целочисленными) и континуальными (непрерывными). В свою очередь,
ограничения и целевая функция делятся на линейные и нелинейные.
Существуют различные методы решения данных моделей, наиболее
известными и эффективными из них являются методы линейного
программирования, когда целевая функция и все ограничения линейные. Для
решения математических моделей других типов предназначены методы
динамического программирования, целочисленного программирования,
нелинейного программирования, многокритериальной оптимизации и методы
сетевых моделей.
Практически все методы исследования операций порождают
вычислительные алгоритмы, которые являются итерационными по своей
природе. Это подразумевает, что задача решается последовательно
(итерационно), когда на каждом шаге (итерации) получаем решение,
постепенно сходящиеся к оптимальному решению.
Итерационная природа алгоритмов обычно приводит к объемным
однотипным вычисления В этом и заключается причина того, что эти
алгоритмы разрабатываются, в основном, для реализации с помощью
вычислительной техники.
1 Динамическое программирование
1.1 Понятия динамического программирования

Динамическое программирование – это математический метод поиска


оптимального управления, специально приспособленный к многошаговым
процессам. Рассмотрим пример такого процесса.
Пусть планируется деятельность группы предприятий на N лет. Здесь
шагом является один год. В начале 1-го года на развитие предприятий
выделяются средства, которые должны быть как-то распределены между этими
предприятиями. В процессе их функционирования выделенные средства
частично расходуются. Каждое предприятие за год приносит некоторый доход,
зависящий от вложенных средств. В начале года имеющиеся средства могут
перераспределяться между предприятиями: каждому из них выделяется какая-
то доля средств.
Ставится вопрос: как в начале каждого года распределять имеющиеся
средства между предприятиями, чтобы суммарный доход от всех предприятий
за N лет был максимальным?
Перед нами типичная задача динамического программирования, в
которой рассматривается управляемый процесс – функционирование группы
предприятий. Управление процессом состоит в распределении (и
перераспределении) средств. Управляющим воздействием (УВ) является
выделение каких-то средств каждому из предприятий в начале года.
УВ на каждом шаге должно выбираться с учетом всех его последствий в
будущем. УВ должно быть дальновидным, с учетом перспективы. Нет смысла
выбирать на рассматриваемом шаге наилучшее УВ, если в дальнейшем это
помешает получить наилучшие результаты других шагов. УВ на каждом шаге
надо выбирать “c заглядыванием в будущее”, иначе возможны серьезные
ошибки.
Действительно, предположим, что в рассмотренной группе предприятий
одни заняты выпуском предметов потребления, а другие производят для этого
машины. Причем целью является получение за N лет максимального объема
выпуска предметов потребления. Пусть планируются капиталовложения на
первый год. Исходя их узких интересов данного шага (года), мы должны были
бы все средства вложить в производство предметов потребления, пустить
имеющиеся машины на полную мощность и добиться к концу года
максимального объема продукции. Но правильным ли будет такое решение в
целом? Очевидно, нет. Имея в виду будущее, необходимо выделить какую-то
долю средств и на производство машин. При этом объем продукции за первый
год, естественно, снизится, зато будут созданы условия, позволяющие
увеличивать ее производство в последующие годы.
В формализме решения задач методом динамического программирования
будут использоваться следующие обозначения:
N – число шагов.
x k  ( x1k , x 2 k ,...., x nk )
– вектор, описывающий состояние системы на k-м
шаге.
x0 –
начальное состояние, т. е. cсостояние на 1-м шаге.
xN
– конечное состояние, т. е. состояние на последнем шаге.
Xk – область допустимых состояний на k-ом шаге.
u  (u1k , u 2 k ,..., u mk ) – вектор УВ на k-ом шаге, обеспечивающий переход
системы из состояния xk-1 в состояние xk.
Uk – область допустимых УВ на k-ом шаге.
Wk – величина выигрыша, полученного в результате реализации k-го
шага.
S – общий выигрыш за N шагов.
u *  (u1* , u 2* ,..., u N* )
– вектор оптимальной стратегии управления или ОУВ за
N шагов.
Sk+1( x k ) – максимальный выигрыш, получаемый при переходе из
xk x0
любого состояния в конечное состояние при оптимальной стратегии
управления начиная с (k+1)-го шага.
S1( x 0 ) – максимальный выигрыш, получаемый за N шагов при переходе

системы из начального состояния x0 в конечное xN при реализации

оптимальной стратегии управления u*. Очевидно, что S = S1( x 0 ), если x0



фиксировано.
Метод динамического программирования опирается на условие
отсутствия последействия и условие аддитивности целевой функции.
xk
Условие отсутствия последействия. Состояние , в которое перешла

система за один k-й шаг, зависит от состояния x k 1 и выбранного УВ uk и не

зависит от того, каким образом система пришла в состояние x k 1 , то есть


x k  f k ( x k 1 , u k ).
(1)
x k 1
Аналогично, величина выигрыша Wk зависит от состояния и
uk
выбранного УВ , то есть
Wk  Wk ( x k 1 , u k ).

(2)
Условие аддитивности целевой функции. Общий выигрыш за N шагов
вычисляется по формуле
N
S   Wk ( x k 1 , u k ).
k 1

(3)
Определение. Оптимальной стратегией управления u* называется

совокупность УВ u1* , u 2* ,..., u N* , то есть u *  (u1* , u 2* ,..., u N* ) , в результате реализации

которых система за N шагов переходит из начального состояния x0 в конечное


xN
и при этом общий выигрыш S принимает наибольшее значение.
Условие отсутствия последействия позволяет сформулировать принцип
оптимальности Белмана.
Принцип оптимальности. Каково бы ни было допустимое состояние
x i 1  X i 1
системы перед очередным i-м шагом, надо выбрать допустимое УВ
u i U i на этом шаге так, чтобы выигрыш Wi на i-м шаге плюс оптимальный
выигрыш на всех последующих шагах был максимальным.
В качестве примера постановки задачи оптимального управления
продолжим рассмотрение задачи управления финансированием группы
 1 ,  2 ,...,  m
предприятий. Пусть в начале i-го года группе предприятий
u1i , u 2i ,..., u mi .
выделяются соответственно средства: совокупность этих

значений можно считать управлением на i-м шаге, то есть u i  ( u1i , u 2i ,..., u mi ) .

Управление u процессом в целом представляет собой совокупность всех

шаговых управлений, то есть u  (u1 , u 2 ,..., u N ) .

Управление может быть хорошим или плохим, эффективным или


неэффективным. Эффективность управления u оценивается показателем S.

u1 , u 2 ,..., u N
Возникает вопрос: как выбрать шаговые управления , чтобы
величина S обратилась в максимум?
Поставленная задача является задачей оптимального управления, а
управление, при котором показатель S достигает максимума, называется
u*
оптимальным. Оптимальное управление многошаговым процессом состоит
из совокупности оптимальных шаговых управлений:
u *  (u1* , u 2* ,..., u N* )

Таким образом, перед нами стоит задача: определить оптимальное


u i*
управление на каждом шаге (i=1,2,...N) и, значит, оптимальное управление
u*
всем процессом .

1.2 Метод динамического программирования

Большинство методов исследования операций связано в первую очередь с


задачами вполне определенного содержания. Классический аппарат математики
оказался малопригодным для решения многих задач оптимизации,
включающих большое число переменных и/или ограничений в виде неравенств.
Несомненна привлекательность идеи разбиения задачи большой размерности
на подзадачи меньшей размерности, включающие всего несколько переменных,
и последующего решения общей задачи по частям. Именно на этой идее
основан метод динамического программирования.
Динамическое программирование (ДП) представляет собой
математический метод, заслуга создания и развития которого принадлежит,
прежде всего, Беллману. Метод можно использовать для решения весьма
широкого круга задач, включая задачи распределения ресурсов, замены и
управления запасами, задачи о загрузке. Характерным для динамического
программирования является подход к решению задачи по этапам, с каждым из
которых ассоциирована одна управляемая переменная. Набор рекуррентных
вычислительных процедур, связывающих различные этапы, обеспечивает
получение допустимого оптимального решения задачи в целом при достижении
последнего этапа.
Происхождение названия динамическое программирование, вероятно,
связано с использованием методов ДП в задачах принятия решений через
фиксированные промежутки времени (например, в задачах управления
запасами). Однако методы ДП успешно применяются также для решения задач,
в которых фактор времени не учитывается. По этой причине более удачным
представляется термин многоэтапное программирование, отражающий
пошаговый характер процесса решения задачи.
Фундаментальным принципом, положенным в основу теории ДП,
является принцип оптимальности. По существу, он определяет порядок
поэтапного решения допускающей декомпозицию задачи (это более
приемлемый путь, чем непосредственное решение задачи в исходной
постановке) с помощью рекуррентных вычислительных процедур.
Динамическое программирование позволяет осуществлять оптимальное
планирование управляемых процессов. Под «управляемыми» понимаются
процессы, на ход которых мы можем в той или другой степени влиять.
Пусть предполагается к осуществлению некоторое мероприятие или
серия мероприятий («операция»), преследующая определенную цель.
Спрашивается: как нужно организовать (спланировать) операцию для того,
чтобы она была наиболее эффективной? Для того чтобы поставленная задача
приобрела количественный, математический характер, необходимо ввести в
рассмотрение некоторый численный критерий W, которым мы будем
характеризовать качество, успешность, эффективность операции. Критерий
эффективности в каждом конкретном случаи выбирается исходя из целевой
направленности операции и задачи исследования (какой элемент управления
оптимизируется и для чего).
Сформулируем общий принцип, лежащий в основе решения всех задач
динамического программирования («принцип оптимальности»):
«Каково бы ни было состояние системы S перед очередным шагом, надо
выбрать управление на этом шаге так, чтобы выигрыш на данном шаге плюс
оптимальный выигрыш на всех последующих шагах был максимальным».
Динамическое программирование – это поэтапное планирование
многошагового процесса, при котором на каждом этапе оптимизируется только
один шаг. Управление на каждом шаге должно выбираться с учетом всех его
последствий в будущем.
При постановке задач динамического программирования следует
руководствоваться следующими принципами:
1. Выбрать параметры (фазовые координаты), характеризующие
состояние S управляемой системы перед каждым шагом.
2. Расчленить операцию на этапы (шаги).
3. Выяснить набор шаговых управлений xi для каждого шага и
налагаемые на них ограничения.
4. Определить какой выигрыш приносит на i-ом шаге управление xi,
если перед этим система была в состоянии S, т.е. записать «функцию
выигрыша»:
Wi  f i ( S , xi ) . (4)
5. Определить, как изменяется состояние S системы S под влиянием
управление xi на i-ом шаге: оно переходит в новое состояние
S '   i ( S , xi )
. (5)
6. Записать основное рекуррентное уравнение динамического
программирования, выражающее условный оптимальный выигрыш Wi(S)
(начиная с i-го шага и до конца) через уже известную функцию Wi+1(S):
Wi ( S )  max f i ( S , xi )  Wi 1 ( i ( S , xi ))
xi
. (6)
Этому выигрышу соответствует условное оптимальное управление на i-м
шаге xi(S) (причем в уже известную функцию Wi+1(S) надо вместо S
S '   i ( S , xi )
подставить измененное состояние )
7. Произвести условную оптимизацию последнего (m-го) шага,
задаваясь гаммой состояний S, из которых можно за один шаг дойти до
конечного состояния, вычисляя для каждого из них условный оптимальный

Wm ( S )  max f m ( S , x m )
выигрыш по формуле xmi
(7)
8. Произвести условную оптимизацию (m-1)-го, (m-2)-го и т.д. шагов по
формуле (6), полагая в ней i=(m-1),(m-2),…, и для каждого из шагов указать
условное оптимальное управление xi(S), при котором максимум достигается.
Заметим, что если состояние системы в начальный момент известно (а это
обычно бывает так), то на первом шаге варьировать состояние системы не
нужно - прямо находим оптимальный выигрыш для данного начального
состояния S0. Это и есть оптимальный выигрыш за всю операцию
W *  W1 ( S 0 ) (8)
9. Произвести безусловную оптимизацию управления, «читая»
соответствующие рекомендации на каждом шаге. Взять найденное оптимальное
x1*  x1 ( S 0 ) ;
управление на первом шаге изменить состояние системы по
формуле (5); для вновь найденного состояния найти оптимальное управление
на втором шаге х2* и т.д. до конца.
Данные этапы рассматривались для аддитивных задач, в которых
выигрыш за всю операцию равен сумме выигрышей на отдельных шагах.
Метод динамического программирования применим также и к задачам с так
называемым «мультипликативным» критерием, имеющим вид произведения:
m
W   wi
i 1 (9)
(если только выигрыши wi положительны). Эти задачи решаются точно
так же, как задачи с аддитивным критерием, с той единственной разницей, что в
основном уравнении (6) вместо знака «плюс» ставится знак «умножения»:

Wi ( S )  max f i ( S , x i ) * Wi 1 ( i ( S , xi ))
xi

1.3 Идеи метода динамического программирования

Планируя многошаговый процесс, необходимо выбирать УВ на каждом


шаге с учетом его будущих последствий на еще предстоящих шагах. Однако, из
этого правила есть исключение. Среди всех шагов существует один, который
может планироваться без "заглядывания в будущее". Какой это шаг? Очевидно,
последний — после него других шагов нет. Этот шаг, единственный из всех,
можно планировать так, чтобы он как таковой принес наибольшую выгоду.
Спланировав оптимально этот последний шаг, можно к нему пристраивать
предпоследний, к предпоследнему — предпредпоследний и т.д.
Поэтому процесс динамического программирования на 1-м этапе раз-
ворачивается от конца к началу, то есть раньше всех планируется последний,

N-й шаг. А как его спланировать, если мы не знаем, чем кончился


предпоследний? Очевидно, нужно сделать все возможные предположения о
том, чем кончился предпоследний, (N — 1)-й шаг, и для каждого из них найти
такое управление, при котором выигрыш (доход) на последнем шаге был бы
максимален. Решив эту задачу, мы найдем условно оптимальное управление
(УОУ) на N-м шаге, т.е. управление, которое надо применить, если (N — 1)-й
шаг закончился определенным образом.
Предположим, что эта процедура выполнена, то есть для каждого исхода
(N — 1)-го шага мы знаем УОУ на N-м шаге и соответствующий ему
условно оптимальный выигрыш (УОВ). Теперь мы можем оптимизировать
управление на предпоследнем, (N — 1)-м шаге. Сделаем все возможные
предположения о том, чем кончился предпредпоследпий, то есть (N — 2)-й шаг,
и для каждого из этих предположений найдем такое управление на (N — 1)-м
шаге, чтобы выигрыш за последние два шага (из которых последний уже
оптимизирован) был максимален. Далее оптимизируется управ чтение на (N —
2)-м шаге, и т.д.
Одним словом, на каждом шаге ищется такое управление, которое
обеспечивает оптимальное продолжение процесса относительно достигнутого в
данный момент состояния. Этот принцип выбора управления называется
принципом оптимальности. Самоуправление, обеспечивающее оптимальное
продолжение процесса относительно заданного состояния, называется УОУ на
данном шаге.
Теперь предположим, что УОУ на каждом шаге нам известно: мы
знаем, что делать дальше, в каком бы состоянии ни был процесс к началу
каждого шага. Тогда мы можем найти уже не "условное", а действительно
оптимальное управление на каждом шаге.
Действительно, пусть нам известно начальное состояние процесса. Те-
перь мы уже знаем, что делать на первом шаге: надо применить УОУ,
найденное для первого шага и начального состояния. В результате этого
управления после первого шага система перейдет в другое состояние; но для
этого состояния мы знаем УОУ и т. д. Таким образом, мы найдем оптимальное
управление процессом, приводящее к максимально возможному выигрышу.

Таким образом, в процессе оптимизации управления методом динами-


ческого программирования многошаговый процесс "проходится" дважды:
— первый раз — от конца к началу, в результате чего находятся УОУ на
каждом шаге и оптимальный выигрыш (тоже условный) на всех шагах,
начиная с данного и до конца процесса;
— второй раз — от начала к концу, в результате чего находятся
оптимальные управления на всех шагах процесса.
Можно сказать, что процедура построения оптимального управления
методом динамического программирования распадается на две стадии:
предварительную и окончательную. На предварительной стадии для каждого
шага определяется УОУ, зависящее от состояния системы (достигнутого в
результате предыдущих шагов), и условно оптимальный выигрыш на всех
оставшихся шагах, начиная с данного, также зависящий от состояния. На
окончательной стадии определяется (безусловное) оптимальное управление для
каждого шага. Предварительная (условная) оптимизация производится по
шагам в обратном порядке: от последнего шага к первому; окончательная
(безусловная) оптимизация — также по шагам, но в естественном порядке: от
первого шага к последнему. Из двух стадий оптимизации несравненно более
важной и трудоемкой является первая. После окончания первой стадии
выполнение второй трудности не представляет: остается только "прочесть"
рекомендации, уже заготовленные на первой стадии.

1.4 Общая структура динамического программирования

Отыскание оптимальной стратегии принятия набора последовательных


решений, в большинстве случаях, производится следующим образом: сначала
осуществляется выбор последнего во времени решения, затем при движении в
направлении, обратном течению времени, выбираются все остальные решения
вплоть до исходного.
Для реализации такого метода необходимо выяснить все ситуации, в
которых может происходить выбор последнего решения. Обычно условия, в
которых принимается решение, называют «состоянием» системы. Состояние
системы – это описание системы, позволяющее, учитывая будущие решения,
предсказать ее поведение. Нет необходимости выяснять, как возникло то ил
иное состояние или каковы были предшествующие решения. Это позволяет
последовательно выбирать всего по одному решению в каждый момент
времени. Независимо от того, отыскивают оптимальные решения с помощью
табличного метода и последующего поиска или аналитическим путем, обычно
быстрее и выгоднее производить выбор по одному решению в один момент
времени, переходя затем к следующему моменту и т.д. К сожалению, таким
методом можно исследовать не все процессы принятия решений. Необходимым
условием применения метода динамического программирования является
аддитивность цен всех решений, а также независимость будущих результатов
от предыстории того или иного состояния.
Если число решений очень велико, то можно построить относительные
оценки состояний так, чтобы оценки, отвечающие каждой паре
последовательных решений, отличались друг от друга на постоянную величину,
представляющую собой средний «доход» на решение. Также можно выполнять
дисконтирование доходов от будущих решений. Необходимость в этом иногда
появляется в том случае, когда решение принимаются редко, скажем раз в году.
Тогда уже не нужно рассматривать последовательно 1,2,3…решения, чтобы
достичь решения с большим номером. Вместо этого можно непосредственно
оперировать функциональным уравнением, что, как правило, дает
существенную выгоду с точки зрения сокращения объема вычислений.
2 Практические задачи динамического программирования
2.1 Последовательности

Классической задачей на последовательности является следующая.


Последовательность Фибоначчи Fn задается формулами: F1 = 1, F2 = 1, 
Fn = Fn – 1 + Fn – 2 при n > 1. Необходимо найти Fn по номеру n.
Один из способов решения, который может показаться логичным и
эффективным, — решение с помощью рекурсии:

int F(int n) {
if (n < 2) return 1;
else return F(n - 1) + F(n - 2);}

Используя такую функцию, мы будем решать задачу «с конца» — будем


шаг за шагом уменьшать n, пока не дойдем до известных значений.
Но как можно заметить, такая, казалось бы, простая программа уже при n = 40
работает заметно долго. Это связано с тем, что одни и те же промежуточные
данные вычисляются по несколько раз — число операций нарастает с той же
скоростью, с какой растут числа Фибоначчи — экспоненциально.
Один из выходов из данной ситуации — сохранение уже найденных
промежуточных результатов с целью их повторного использования:

int F(int n) {
if (A[n] != -1) return A[n];
if (n < 2) return 1;
else {
A[n] = F(n - 1) + F(n - 2);
return A[n];
}
}
Приведенное решение является корректным и эффективным. Но для
данной задачи применимо и более простое решение:

F[0] = 1;
F[1] = 1;
for (i = 2; i < n; i++) F[i] = F[i - 1] + F[i - 2];

Такое решение можно назвать решением «с начала» — мы первым делом


заполняем известные значения, затем находим первое неизвестное значение
(F3), потом следующее и т.д., пока не дойдем до нужного.
Именно такое решение и является классическим для динамического
программирования: мы сначала решили все подзадачи (нашли все Fi для i < n),
затем, зная решения подзадач, нашли ответ (Fn = Fn – 1 + Fn – 2, Fn – 1 и Fn –
2 уже найдены).

2.2 Одномерное динамическое программирование

Чтобы лучше понять суть динамического программирования, сначала более


формально определим понятия задачи и подзадачи.
Пусть исходная задача заключается в нахождении некоторого числа T при
исходных данных n1, n2, ..., nk. То есть мы можем говорить о функции T(n1, n2,
..., nk), значение которой и есть необходимый нам ответ. Тогда подзадачами
будем считать задачи T(i1, i2,..., ik) при i1 < n1, i2 < n2,..., ik < nk.
Далее мы будем о говорить об одномерном, двумерном и многомерном
динамическом программировании при k = 1, k = 2, k > 2, соответственно.
Следующая задача одномерного динамического программирования встречается
в различных вариациях.
Задача 1. Посчитать число последовательностей нулей и единиц длины n,
в которых не встречаются две идущие подряд единицы.
При n < 32 полный перебор потребует нескольких секунд, а при n = 64 полный
перебор не осуществим в принципе. Для решения задачи методом
динамического программирования сведем исходную задачу к подзадачам.
При n = 1, n = 2 ответ очевиден. Допустим, что мы уже нашли Kn –
1, Kn – 2 — число таких последовательностей длины n – 1 и n – 2. Посмотрим,
какой может быть последовательность длины n. Если последний ее символ
равен 0, то первые n – 1 — любая правильная последовательность длины n – 1
(не важно, заканчивается она нулем или единицей — следом идет 0). Таких
последовательностей всего Kn – 1. Если последний символ равен 1, то
предпоследний символ обязательно должен быть равен 0 (иначе будет две
единицы подряд), а первые n – 2 символа — любая правильная
последовательность длины n – 2, число таких последовательностей равно Kn –
2.

Рис. 1 – Число последовательностей

Таким образом, K1 = 2, K2 = 3, Kn = Kn – 1 + Kn – 2 при n > 2. То есть


данная задача фактически сводится к нахождению чисел Фибоначчи.

2.3 Двумерное динамическое программирование

Классической задачей двумерного динамического программирования


является задача о маршрутах на прямоугольном поле.
В разных формулировках необходимо посчитать число маршрутов или найти
маршрут, который является лучшим в некотором смысле.
Приведем пару формулировок таких задач:
Задача 2. Дано прямоугольное поле размером n*m клеток. Можно
совершать шаги длиной в одну клетку вправо или вниз. Посчитать, сколькими
способами можно попасть из левой верхней клетки в правую нижнюю.
Задача 3. Дано прямоугольное поле размером n*m клеток. Можно
совершать шаги длиной в одну клетку вправо, вниз или по диагонали вправо-
вниз. В каждой клетке записано некоторое натуральное число. Необходимо
попасть из верхней левой клетки в правую нижнюю. Вес маршрута вычисляется
как сумма чисел со всех посещенных клеток. Необходимо найти маршрут с
минимальным весом.
Для всех таких задач характерным является то, что каждый отдельный
маршрут не может пройти два или более раз по одной и той же клетке.
Рассмотрим более подробно задачу 2. В некоторую клетку с
координатами (i,j) можно прийти только сверху или слева, то есть из клеток с
координатами (i – 1, j) и (i, j – 1):

Рис. 2 – Направление векторов в сетке

Таким образом, для клетки (i, j) число маршрутов A[i][j] будет равно 
A[i – 1][j] + A[i][j – 1], то есть задача сводится к двум подзадачам. В данной
реализации используется два параметра — i и j — поэтому применительно к
данной задаче мы говорим о двумерном динамическом программировании.
Теперь мы можем пройти последовательно по строкам (или по столбцам)
массива A, находя число маршрутов для текущей клетки по приведенной выше
формуле. Предварительно в A[0][0] необходимо поместить число 1.
В задаче 3 в клетку с координатами (i, j) мы можем попасть из клеток с
координатами (i – 1, j), (i, j – 1) и (i – 1, j – 1). Допустим, что для каждой из этих
трех клеток мы уже нашли маршрут минимального веса, а сами веса поместили
в W[i – 1][j], W[i][j – 1], W[i – 1][j – 1].
Чтобы найти минимальный вес для (i, j), необходимо выбрать
минимальный из весов W[i – 1][j], W[i][j – 1], W[i – 1][j – 1] и прибавить к нему
число, записанное в текущей клетке:
W[i][j] = min(W[i–1][j], W[i][j – 1], W[i – 1][j – 1]) + A[i][j];
Данная задача осложнена тем, что необходимо найти не только
минимальный вес, но и сам маршрут. Поэтому в другой массив мы
дополнительно для каждой клетки будем записывать, с какой стороны в нее
надо попасть.
На рисунке 3 приведен пример исходных данных и одного из шагов
алгоритма.

Рис. 3 – Шаги алгоритма и исходные данные

В каждую из уже пройденных клеток ведет ровно одна стрелка. Эта


стрелка показывает, с какой стороны необходимо прийти в эту клетку, чтобы
получить минимальный вес, записанный в клетке.
После прохождения всего массива необходимо будет проследить сам
маршрут из последней клетки, следуя по стрелкам в обратную сторону.

2.4 Задачи на подпоследовательности

Рассмотрим задачу о возрастающей подпоследовательности.


Задача 4. Дана последовательность целых чисел. Необходимо найти ее
самую длинную строго возрастающую подпоследовательность.
Начнем решать задачу с начала — будем искать ответ, начиная с первых
членов данной последовательности. Для каждого номера i будем искать
наибольшую возрастающую подпоследовательность, оканчивающуюся
элементом в позиции i. Пусть исходная последовательность хранится в массиве
A. В массиве L будем записывать длины максимальных
подпоследовательностей, оканчивающихся текущим элементом. Пусть мы
нашли все L[i] для 1 <= i <= k – 1. Теперь можно найти L[k] следующим
образом. Просматриваем все элементы A[i] для 1 <= i < k – 1. Если 
A[i] < A[k], то k-ый элемент может стать продолжением
подпоследовательности, окончившейся элементом A[i]. Длина полученной
подпоследовательности будет на 1 больше L[i]. Чтобы найти L[k], необходимо
перебрать все i от 1 до k – 1: 
L[k] = max(L[i]) + 1, где максимум берется по всем i таким, что A[i] < [k]
и 1 <= i < k.
Здесь максимум из пустого множества будем считать равным 0. В этом
случае текущий элемент станет единственным в выбранной
последовательности, а не будет продолжением одной из предыдущих. После
заполнения массива L длина наибольшей возрастающей
подпоследовательности будет равна максимальному элементу L.
Чтобы восстановить саму подпоследовательность, для каждого элемента также
сохранять номер предыдущего выбранного элемента, например, в массив N.
Рассмотрим решение этой задачи на примере последовательности 2, 8, 5,
9, 12, 6. Поскольку до 2 нет ни одного элемента, то максимальная
подпоследовательность содержит только один элемент — L[1] = 1, а перед ним
нет ни одного — N[1] = 0. Далее, 2 < 8, поэтому 8 может стать продолжением
последовательности с предыдущим элементом. Тогда L[2] = 2, N[2] = 1.
Рис. 4 – Заполнение массивов

Меньше A[3] = 5 только элемент A[1] = 2, поэтому 5 может стать


продолжением только одной подпоследовательности — той, которая содержит
2. Тогда L[3] = L[1] + 1 = 2, N[3] = 1, так как 2 стоит в позиции с номером 1.
Аналогично выполняем еще три шага алгоритма и получаем окончательный
результат.

Рис. 5 – Результат операций

Теперь выбираем максимальный элемент в массиве L и по массиву N


восстанавливаем саму подпоследовательность 2, 5, 9, 12. Еще одной
классической задачей динамического программирования является задача о
палиндромах.
Задача 5. Дана строка из заглавных букв латинского алфавита.
Необходимо найти длину наибольшего палиндрома, который можно получить
вычеркиванием некоторых букв из данной строки.
Обозначим данную строку через S, а ее символы — через S[i], 1 <= i <= n.
Будем рассматривать возможные подстроки данной строки с i-го по j-ый
символ, обозначим их через S(i, j). Длины максимальных палиндромов для
подстрок будем записывать в квадратный массив L: L[i][j] — длина
максимального палиндрома, который можно получить из подстроки S(i, j).
Начнем решать задачу с самых простых подстрок. Для строки из одного
символа (то есть подстроки вида S(i, i)) ответ очевиден — ничего вычеркивать
не надо, такая строка будет палиндромом. Для строки из двух символов S(i, i +
1) возможны два варианта: если символы равны, то мы имеем палиндром,
ничего вычеркивать не надо. Если же символы не равны, то вычеркиваем
любой.
Пусть теперь нам дана подстрока S(i, j). Если первый (S[i]) и последний
(S[j]) символы подстроки не совпадают, то один из них точно нужно
вычеркнуть. Тогда у нас останется подстрока S(i, j – 1) или S(i + 1, j) — то есть
мы сведем задачу к подзадаче: L[i][j] = max(L[i][j – 1], L[i + 1][j]). Если же
первый и последний символы равны, то мы можем оставить оба, но необходимо
знать решение задачи S(i + 1, j – 1):  L[i][j] = L[i + 1][j – 1] + 2.
Рассмотрим решение на примере строки ABACCBA. Первым делом
заполняем диагональ массива единицами, они будут соответствовать
подстрокам S(i, i) из одного символа. Затем начинаем рассматривать подстроки
длины два. Во всех подстроках, кроме S(4, 5), символы различны, поэтому в
соответствующие ячейки запишем 1, а в L[4][5] — 2.
Получается, что мы будем заполнять массив по диагоналям, начиная с
главной диагонали, ведущей из левого верхнего угла в правый нижний. Для
подстрок длины 3 получаются следующие значения: в подстроке ABA первая и
последняя буквы равны, поэтому L[1][3] = L[2][2] + 2. В остальных подстроках
первая и последняя буквы различны.
BAC: L[2][4] = max(L[2][3], L[3][4]) = 1.
ACC: L[3][5] = max(L[3][4], L[4][5]) = 2.
CCB: L[4][6] = max(L[4][5], L[5][6]) = 2.
CBA: L[5][7] = max(L[5][6], L[6][7]) = 1.
Продолжая далее аналогичные рассуждения, заполним все ячейки под
диагональю и в ячейке L[1][7] = 6 получим ответ.

Рис. 6 – Квадратичный массив

Если же в задаче необходимо вывести не длину, а сам палиндром, то


дополнительно к массиву длин мы должны построить массив переходов — для
каждой ячейки запомнить, какой из случаев был реализован (на рисунке 7 для
наглядности вместо числовых значений, кодирующих переходы, нарисованы
соответствующие стрелки).
Рис. 7 – Направление движений при вычислениях
ЗАКЛЮЧЕНИЕ

Можно выделить, по крайней мере, четыре аспекта применения


динамических методов в решении практических проблем.
1. Совершенствование системы экономической информации.
Динамические методы позволяют упорядочить систему экономической
информации, выявлять недостатки в имеющейся информации и вырабатывать
требования для подготовки новой информации или ее корректировки.
Разработка и применение экономико-математических моделей указывают пути
совершенствования экономической информации, ориентированной на
решение определенной системы задач планирования и управления. Прогресс
в информационном обеспечении планирования и управления опирается на
бурно развивающиеся технические и программные средства информатики.
2. Интенсификация и повышение точности экономических расчетов.
Формализация экономических задач и применение ЭВМ многократно
ускоряют типовые, массовые расчеты, повышают точность и сокращают
трудоемкость, позволяют проводить многовариантные экономические
обоснования сложных мероприятий, недоступные при господстве "ручной"
технологии.
3. Углубление количественного анализа экономических проблем.
Благодаря применению метода динамического программирования
значительно усиливаются возможности конкретного количественного анализа,
изучение многих факторов, оказывающих влияние на экономические процессы,
количественная оценка последствий изменения условий развития
экономических объектов и т.п.
Сфера практического применения данных методов ограничивается
возможностями и эффективностью формализации экономических проблем и
ситуаций, а также состоянием информационного, математического,
технического обеспечения используемых моделей. Стремление во что бы то ни
стало, применить математическую модель может не дать хороших результатов
из-за отсутствия хотя бы некоторых необходимых условий.
В соответствии с современными научными представлениями системы
разработки и принятия хозяйственных решений должны сочетать формальные
и неформальные методы, взаимоусиливающие и взаимодополняющие друг
друга. Формальные методы являются, прежде всего, средством научно
обоснованной подготовки материала для действий человека в процессах
управления. Это позволяет продуктивно использовать опыт и интуицию
человека, его способности решать плохо формализуемые задачи.
СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ

1 Абчук В. А. «Экономико-математические методы» -С.-П.: Союз, 2009.


2 Кузнецов Ю. Н. Математическое программирование. –М.: Наука,2006.
3 Вентцель Е. С. Исследование операций. –М.: Наука,2006.
4 Вентцель Е. С. Элементы динамического программирования. –М.:
Наука,2007.
5 Акоф Р., Сасиени М. Основы исследования операций. –М.: Мир,2011.
6 Вентцель Е. С. Исследование операций: задачи, принципы,
методология. –М.: Наука,2008.
7 Карманов В. Т. Математическое программирование. –М.:Наука,2006.
8 Зайченко Ю. П. Исследование операций. –К.: Высшая школа,2005.
9 Аоки М. Введение в методы оптимизации. –М.: Наука,2007.
10 Беллман Р., Дрейфус С. Прикладные задачи динамического
программирования. –М.: Наука,2005.
11 Муну М. Математическое программирование. Теория алгоритмов. –
М.: Наука,2010.

Вам также может понравиться