Тушавин В.А.
24 ноября 2015 г.
Динамическое программирование позволяет находить оптимальное решение задачи путем её декомпозиции на несколько этапов.
Такой подход приводит одну большую по размерности задачу ко многих задачам, имеющим меньшую размреность. Это значительно сокращает объем вычислений и ускоряет процесс принятия управленческих решений. Вычисления производятся реккурентно в том смысле, что оптимальное решение одной подзадачи используется в качестве исходных данных для следующей.
Задача. Определить оптимальный маршрут из пункта 1 в пункт 10 по схеме маршрута движения
# Здесь и далее граф строится с помощью команды dot из пакета graphviz
# Программу можно скачать здесь: http://www.graphviz.org/
digraph ex01 {
rankdir=LR;
size="8,5"
node [shape = box];
"1" -> "2" [ label = "3" ];
"1" -> "3" [ label = "7" ];
"1" -> "4" [ label = "2" ];
"2" -> "5" [ label = "9" ];
"2" -> "6" [ label = "11" ];
"3" -> "5" [ label = "5" ];
"3" -> "6" [ label = "10" ];
"3" -> "7" [ label = "7" ];
"4" -> "6" [ label = "15" ];
"4" -> "7" [ label = "13" ];
"5" -> "8" [ label = "7" ];
"5" -> "9" [ label = "5" ];
"6" -> "8" [ label = "3" ];
"6" -> "9" [ label = "4" ];
"7" -> "8" [ label = "7" ];
"7" -> "9" [ label = "1" ];
"8" -> "10" [ label = "1" ];
"9" -> "10" [ label = "4" ];
}
Каждый квадрат на схеме изображает один из населенных пунктов, которые для удобства пронумерованы. Стоимость проезда из пункта i в пункт j обозначим через сij (обозначено на стрелках). Требуется определить такой путь из пункта 1 в пункт 10, общая стоимость которого является минимальной.
Решение: Воспользуемся формулой реккурентных соотношений Беллмана
где N --- количество этапов в решении;
Начинаем поиск от конечного пункта.
n=1
n=2
digraph ex02 {
rankdir=LR;
size="8,5"
node [shape = box];
"1" -> "2" [ label = "3" ];
"1" -> "3" [ label = "7" ];
"1" -> "4" [ label = "2" ];
"2" -> "5" [ label = "9" ];
"2" -> "6" [ label = "11" ];
"3" -> "5" [ label = "5" ];
"3" -> "6" [ label = "10" ];
"3" -> "7" [ label = "7" ];
"4" -> "6" [ label = "15" ];
"4" -> "7" [ label = "13" ];
# Упростили
"5" -> "8" [ label = "7" ];
"6" -> "8" [ label = "3" ];
"7" -> "9" [ label = "1" ];
"8" -> "10" [ label = "1" ];
"9" -> "10" [ label = "4" ];
}
n=3
digraph ex03 {
rankdir=LR;
size="8,5"
node [shape = box];
"1" -> "2" [ label = "3" ];
"1" -> "3" [ label = "7" ];
"1" -> "4" [ label = "2" ];
# Упростили на 3 этапе
"2" -> "6" [ label = "11" ];
"3" -> "7" [ label = "7" ];
"4" -> "7" [ label = "13" ];
# Упростили на 2 этапе
"6" -> "8" [ label = "3" ];
"7" -> "9" [ label = "1" ];
"8" -> "10" [ label = "1" ];
"9" -> "10" [ label = "4" ];
}
n=4
Таким образом оптимальный путь 1-2-6-8-10, затраты по которому составляют
digraph ex01 {
rankdir=LR;
size="8,5"
node [shape = box];
"1" -> "2" [ label = "3",style=bold,color=red ];
"1" -> "3" [ label = "7",style=dotted];
"1" -> "4" [ label = "2",style=dotted ];
"2" -> "5" [ label = "9",style=dotted ];
"2" -> "6" [ label = "11",style=bold,color=red ];
"3" -> "5" [ label = "5",style=dotted ];
"3" -> "6" [ label = "10",style=dotted ];
"3" -> "7" [ label = "7",style=dotted ];
"4" -> "6" [ label = "15",style=dotted ];
"4" -> "7" [ label = "13",style=dotted ];
"5" -> "8" [ label = "7",style=dotted ];
"5" -> "9" [ label = "5",style=dotted ];
"6" -> "8" [ label = "3",style=bold,color=red ];
"6" -> "9" [ label = "4",style=dotted ];
"7" -> "8" [ label = "7",style=dotted ];
"7" -> "9" [ label = "1",style=dotted ];
"8" -> "10" [ label = "1",style=bold,color=red ];
"9" -> "10" [ label = "4",style=dotted ];
}
Для решения задач связанных с поиском кратчайшего пути в графе в GNU R можно использовать функцию пакет igraph. В таком случае
library(igraph)
##
## Attaching package: 'igraph'
##
## Следующие объекты скрыты от 'package:stats':
##
## decompose, spectrum
##
## Следующий объект скрыт от 'package:base':
##
## union
Опишем граф как таблицу.
mytable<-data.frame(from=c(1,1,1,2,2,3,3,3,4,4,5,5,6,6,7,7,8,9),
to=c(2,3,4,5,6,5,6,7,6,7,8,9,8,9,8,9,10,10),
weight=c(3,7,2,9,11,5,10,7,15,13,7,5,3,4,7,1,1,4))
head(mytable)
## from to weight
## 1 1 2 3
## 2 1 3 7
## 3 1 4 2
## 4 2 5 9
## 5 2 6 11
## 6 3 5 5
Задаем граф, рисуем его и находим кратчайшие пути. В данном случае, поскольку все веса неотрицательны, по умолчанию будет использоваться алгоритм Дейкстры. Поскольку мы говорим о динамическом программировании, то надо явно указывать алгоритм Беллмана–Форда.
g<-graph.data.frame(mytable,directed =T )
plot(g)
distances(g,algorithm ="bellman-ford")
## 1 2 3 4 5 6 7 8 9 10
## 1 0 3 7 2 12 14 14 17 15 18
## 2 3 0 10 5 9 11 15 14 14 15
## 3 7 10 0 9 5 10 7 12 8 12
## 4 2 5 9 0 14 15 13 18 14 18
## 5 12 9 5 14 0 9 6 7 5 8
## 6 14 11 10 15 9 0 5 3 4 4
## 7 14 15 7 13 6 5 0 6 1 5
## 8 17 14 12 18 7 3 6 0 5 1
## 9 15 14 8 14 5 4 1 5 0 4
## 10 18 15 12 18 8 4 5 1 4 0
shortest_paths(g, 1, 10)$vpath
## [[1]]
## + 5/10 vertices, named:
## [1] 1 2 6 8 10
sessionInfo()
## R version 3.2.2 (2015-08-14)
## Platform: x86_64-apple-darwin13.4.0 (64-bit)
## Running under: OS X 10.9.5 (Mavericks)
##
## locale:
## [1] ru_RU.UTF-8/ru_RU.UTF-8/ru_RU.UTF-8/C/ru_RU.UTF-8/ru_RU.UTF-8
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] igraph_1.0.1
##
## loaded via a namespace (and not attached):
## [1] magrittr_1.5 formatR_1.2.1 tools_3.2.2 htmltools_0.2.6
## [5] yaml_2.1.13 stringi_1.0-1 rmarkdown_0.8.1 knitr_1.11
## [9] stringr_1.0.0 digest_0.6.8 evaluate_0.8