Рефераты

VB, MS Access, VC++, Delphi, Builder C++ принципы(технология), алгоритмы программирования

алгоритмы для небольших сетей часто выполняются медленнее, чем более

простые алгоритмы. Тем не менее, для очень больших сетей или сетей, в

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

этих алгоритмов может стоить дополнительного усложнения.

Коррекция меток

Как и алгоритм установки меток, этот алгоритм начинает с обнуления значения

поля Dist корневого узла и помещает корневой узел в список возможных узлов.

При этом значения полей Dist остальных узлов устанавливаются равными

бесконечности. Затем для вставки в дерево кратчайшего маршрута выбирается

первый узел в списке возможных узлов.

После этого алгоритм проверяет узлы, соседние с выбранным, выясняя, будет

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

текущее значение поля Dist соседнего узла. Если это так, то поля Dist и

InLink соседнего узла обновляются так, чтобы кратчайший маршрут к соседнему

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

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

списку. Заметьте, что алгоритм не проверяет, попадал ли этот узел в список

раньше. Если путь от корня до соседнего узла становится короче, узел всегда

добавляется в список возможных узлов.

Алгоритм продолжает удалять узлы из списка возможных узлов, проверяя

соседние с ними узлы и добавляя соседние узлы в список до тех пор, пока

список не опустеет.

Если внимательно сравнить алгоритмы установки меток и коррекции меток, то

видно, что они похожи. Единственное отличие заключается в том, как каждый

из них выбирает элементы из списка возможных узлов для вставки в дерево

кратчайшего маршрута.

=====331

Алгоритм установки меток всегда выбирает связь, которая гарантированно

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

удаляется из списка возможных узлов, он навсегда помещается в дерево и

больше не попадает в список возможных узлов.

Алгоритм корректировки всегда выбирает первый узел из списка возможных

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

и InLink этого узла могут быть не наилучшими из возможных. В этом случае

алгоритм, в конце концов, найдет в списке узел, через который проходит

более короткий путь к выбранному узлу. Тогда алгоритм обновляет поля Dist и

InLink и снова помещает обновленный узел в список возможных узлов.

Алгоритм может использовать новый путь для создания других путей, которые

он мог пропустить раньше. Помещая обновленный узел снова в список

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

и будут найдены все такие пути.

Private Sub FindPathTree(root As PathCNode)

Dim candidates As New Collection

Dim node_dist As Integer

Dim new_dist As Integer

Dim node As PathCNode

Dim to_node As PathCNode

Dim link As PathCLink

If root Is Nothing Then Exit Sub

' Сбросить поля Marked и NodeStatus для всех узлов,

' и флаги Used и InPathTree для всех связей.

ResetPathTree

' Начать с корня дерева кратчайшего маршрута.

root.Dist = 0

Set root.InLink = Nothing

root.NodeStatus = NOW_IN_LIST

candidates.Add root

Do While candidates.Count > 0

' Добавить узел в дерево кратчайшего маршрута.

Set node = candidates(1)

candidates.Remove 1

node_dist = node.Dist

node.NodeStatus = NOT_IN_LIST

' Проверить соседние узлы.

For Each link In node.Links

If node Is link.Node1 Then

Set to_node = link.Node2

Else

Set to_node = link.Node1

End If

' Проверить, существует ли более короткий

' путь через этот узел.

new_dist = node_dist + link.Cost

If to_node.Dist > new_dist Then

' Путь лучше. Обновить значения Dist и InLink.

Set to_node.InLink = link

to_node.Dist = new_dist

' Добавить узел в список возможных узлов,

' если его там еще нет.

If to_node.NodeStatus = NOT_IN_LIST Then

candidates.Add to_node

to_node.NodeStatus = NOW_IN_LIST

End If

End If

Next link

Loop

' Пометить входящие связи, чтобы их было проще вывести.

For Each node In Nodes

If Not (node.InLink Is Nothing) Then _

node.InLink.InPathTree = True

Next node

' Перерисовать сеть.

DrawNetwork

End Sub

В отличие от алгоритма установки меток, этот алгоритм не может работать с

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

такой цикл, то алгоритм бесконечно перемещается по связям внутри него. При

каждом обходе цикла расстояние до входящих в него узлов уменьшается, при

этом алгоритм снова помещает узлы в список возможных узлов, и снова может

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

них также уменьшится, и так далее. Этот процесс будет продолжаться до тех

пор, пока расстояние до этих узлов не достигнет нижнего граничного значения

-32.768, если длина пути задана целым числом. Если известно, что в сети

имеются циклы с отрицательной ценой, то проще всего просто использовать для

работы с ней метод установки, а не коррекции меток.

Программа PathC использует этот алгоритм коррекции меток для вычисления

кратчайшего маршрута. Она аналогична программе PathS, но использует метод

коррекции, а не установки меток.

=======333

Варианты метода коррекции меток

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

возможных узлов. Он также может вставить узел в список всего за один или

два шага. Недостаток этого алгоритма заключается в том, что когда он

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

хороший выбор. Если алгоритм выбирает узел до того, как его поля Dist и

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

значения этих полей и снова поместить узел в список возможных узлов. Чем

чаще алгоритм помещает узлы назад в список возможных узлов, тем больше

времени это занимает.

Варианты этого алгоритма пытаются повысить качество выбора узлов без

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

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

списка возможных узлов. Если узел раньше не попадал в список возможных

узлов, алгоритм, как обычно, добавляет его в конец списка. Если узел уже

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

вставляет его в начало списка. При этом повторное обращение к узлу

выполняется практически сразу, возможно при следующем же обращении к

списку.

Идея, заключенная в таком подходе, состоит в том, чтобы если алгоритм

совершает ошибку, она исправлялась как можно быстрее. Если ошибка не будет

исправлена в течение достаточно долгого времени, алгоритм может

использовать неправильную информацию для построения длинных ложных путей,

которые затем придется исправлять. Благодаря быстрому исправлению ошибок,

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

перестроить. В наилучшем случае, если все соседние узлы все еще находятся в

списке возможных узлов, повторная проверка этого узла до проверки соседей

предотвратит построение неверных путей.

Другие задачи поиска кратчайшего маршрута

Описанные выше алгоритмы поиска кратчайшего маршрута вычисляли все

кратчайшие пути из корневого узла до всех остальных узлов в сети.

Существует множество других типов задачи нахождения кратчайшего маршрута. В

этом разделе обсуждаются три из них: двухточечный кратчайший маршрут (point-

to-point shortest path), кратчайший маршрут для всех пар(all pairs shortest

path) и кратчайший маршрут со штрафами за повороты.

Двухточечный кратчайший маршрут

В некоторых приложениях может понадобиться найти кратчайший маршрут между

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

не важны. Простой способ решить эту задачу — вычислить полное дерево

кратчайшего маршрута при помощи метода установки или коррекции меток, а

затем выбрать из дерева кратчайший путь между двумя точками.

Другой способ заключается в использовании метода установки меток, который

останавливался бы, когда будет найден путь к конечному узлу. Алгоритм

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

которые обязательно должны в нем находиться, следовательно, в тот момент,

когда алгоритм добавит конечный узел в дерево, будет найден искомый

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

когда алгоритм удаляет конечный узел из списка возможных узлов.

=======334

Единственное изменение требуется внести в часть алгоритма установки меток,

которая выполняется сразу же после того, как алгоритм находит в списке

возможных узлов узел с наименьшим значением Dist. Перед удалением узла из

списка возможных узлов, алгоритм должен проверить, не является ли этот узел

искомым. Если это так, то дерево кратчайшего маршрута уже содержит

кратчайший маршрут между начальным и конечным узлами, и алгоритм может

закончить работу.

' Найти ближайший к корню узел в списке возможных узлов.

:

' Проверить, является ли этот узел искомым.

If node = destination Then Exit Do

' Добавить этот узел в дерево кратчайшего маршрута.

:

На практике, если две точки в сети расположены далеко друг от друга, то

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

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

того, что в каждом цикле выполнения алгоритма проверяется, достигнут ли

искомый узел. С другой стороны, если узлы расположены рядом, то выполнение

этого алгоритма может потребовать намного меньше времени, чем построение

полного дерева кратчайшего маршрута.

Для некоторых сетей, таких как сеть улиц, можно оценить, насколько близко

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

сеть содержит все улицы южной Калифорнии, и две точки расположены на

расстоянии 10 миль, следует использовать версию, которая останавливается

после того, как найдет конечный узел. Если же точки удалены друг от друга

на 100 миль, возможно, меньше времени займет вычисление полного дерева

кратчайшего маршрута.

Вычисление кратчайшего маршрута для всех пар

В некоторых приложениях может потребоваться быстро найти кратчайший маршрут

между всеми парами узлов в сети. Если нужно вычислить большую часть из N2

возможных путей, может быть быстрее вычислить все возможные пути вместо

того, чтобы находить только те, которые нужны.

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

InLinks. В ячейке Dist(I, J) находится кратчайший маршрут из узла I в узел

J, а в ячейке InLinks(I, J) — связь, которая ведет к узлу J в кратчайшем

пути из узла I в узел J. Эти значения аналогичны значениям Dist и InLink в

классе узла в предыдущем алгоритме.

Один из способов найти все кратчайшие маршруты заключается в том, чтобы

построить деревья кратчайшего маршрута с корнем в каждом из узлов сети при

помощи одного из предыдущих алгоритмов, и затем сохранить результаты в

массивах Dists и InLinks.

========335

Другой метод вычисления всех кратчайших маршрутов последовательно строит

пути, используя все больше и больше узлов. Вначале алгоритм находит все

кратчайшие маршруты, которые используют только первый узел и узлы на концах

пути. Другими словами, для узлов J и K алгоритм находит кратчайший маршрут

между этими узлами, который использует только узел с номером 1 и узлы J и

K, если такой путь существует

Затем алгоритм находит все кратчайшие маршруты, которые используют только

два первых узла. Затем он строит пути, используя первые три узла, первые

четыре узла, и так далее до тех пор, пока не будут построены все кратчайшие

маршруты, используя все узлы. В этот момент, поскольку кратчайшие маршруты

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

сети.

Заметьте, что кратчайший маршрут между узлами J и K, использующий только

первые I узлов, включает узел I, только если Dist(J, K) > Dist(J, I) +

Dist(I, K). Иначе кратчайшим маршрутом будет предыдущий кратчайший маршрут,

который использовал только первые I - 1 узлов. Это означает, что когда

алгоритм рассматривает узел I, требуется только проверить выполнение

условия Dist(J, K) > Dist(J, I) + Dist(I, K). Если это условие выполняется,

алгоритм обновляет кратчайший маршрут из узла J в узел K. Иначе старый

кратчайший маршрут между этими двумя узлами остался бы таковым.

Штрафы за повороты

В некоторых сетях, в особенности сетях улиц, бывает полезно добавить штраф

и запреты на повороты (turn penalties) В сети улиц автомобиль должен

замедлить движение перед тем, как выполнить поворот. Поворот налево может

занимать больше времени, чем поворот направо или движение прямо. Некоторые

повороты могут быть запрещены или невозможны из-за наличия разделительной

полосы. Эти аспекты можно учесть, вводя в сеть штрафы за повороты.

Небольшое число штрафов за повороты

Часто важны только некоторые штрафы за повороты. Может понадобиться

предотвратить выполнение запрещенных или невозможных поворотов и присвоить

штрафы за повороты лишь на нескольких ключевых перекрестках, не определяя

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

узел, для которого заданы штрафы, на несколько узлов, которые будут неявно

учитывать штрафы.

Предположим, что требуется добавить один штраф за поворот на перекрестке

налево и другой штраф за поворот направо. На рис. 12.12 показан

перекресток, на котором требуется применить эти штрафы. Число рядом с

каждой связью соответствует ее цене. Требуется применить штрафы за вход в

узел A по связи L1, и затем выход из него по связям L2 или L3.

Для применения штрафов к узлу A, разобьем этот узел на два узла, по одному

для каждой из покидающих его связей. В данном примере, из узла A выходят

две связи, поэтому узел A разбивается на два узла A1 и A2, и связи,

выходящие из узла A, заменяются соответствующими связями, выходящими из

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

узлов соответствует входу в узел A и повороту в сторону соответствующей

связи.

======336

@Рис. 12.12. Перекресток

Затем связь L1, входящая в узел A, заменяется на две связи, входящие в

каждый из двух узлов A1 и A2. Цена этих связей равна цене исходной связи L1

плюс штрафу за поворот в соответствующем направлении. На рис. 12.13 показан

перекресток, на котором введены штрафы за поворот. На этом рисунке штраф за

поворот налево из узла A равен 5, а за поворот направо —2.

Помещая информацию о штрафах непосредственно в конфигурацию сети, мы

избегаем необходимости модифицировать алгоритмы поиска кратчайшего

маршрута. Эти алгоритмы будут находить правильные кратчайшие маршруты с

учетом штрафов за повороты.

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

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

маршрут между узлами I и J, но узел I оказался разбит на несколько узлов.

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

узел и использовать его в качестве корня дерева кратчайшего маршрута.

Соединим этот узел связями с нулевой ценой с каждым из узлов, получившихся

после разбиения узла I. Тогда, если построить дерево кратчайшего маршрута с

корнем в ложном узле, то при этом будут найдены все кратчайшие маршруты,

содержащие любой из этих узлов. На рис. 12.14 показан перекресток с рис.

12.13, связанный с ложным корневым узлом.

@Рис. 12.13. Перекресток со штрафами за повороты

=======337

@Рис. 12.14. Перекресток, связанный с ложным корнем

Обрабатывать случай поиска пути к узлу, который был разбит на несколько

узлов, проще. Если требуется найти кратчайший маршрут между узлами I и J, и

узел J был разбит на несколько узлов, то вначале, как обычно, нужно найти

дерево кратчайшего маршрута с корнем в узле I. Затем проверяются все узлы,

на которые был разбит узел J и находится ближайший из них к корню дерева.

Путь к этому узлу и есть кратчайший маршрут к исходному узлу J.

Большое число штрафов за повороты

Предыдущий метод будет не слишком эффективным, если вы хотите ввести штрафы

за повороты для большинства узлов в сети. Лучше будет создать совершенно

новую сеть, которая будет включать информацию о штрафах.

. Для каждой связи между узлами A и B в исходной сети в новой сети

создается узел AB;

. Если в исходной сети соответствующие связи были соединены, то полученные

узлы также соединяются между собой. Например, предположим, что в

исходной сети одна связь соединяла узлы A и B, а другая — узлы B и C.

Тогда в новой сети нужно создать связь, соединяющую узел AB с узлом BC;

. Цена новой связи складывается из цены второй связи в исходной сети и

штрафа за поворот. В этом примере цена связи между узлом AB и узлом BC

будет равна цене связи, соединяющей узлы B и C в исходной сети плюс

штрафу за поворот при движении из узла A в узел B и затем в узел C.

На рис. 12.15 изображена небольшая сеть и соответствующая новая сеть,

представляющая штрафы за повороты. Штраф за поворот налево равен 3, за

поворот направо — 2, а за «поворот» прямо — нулю. Например, так как поворот

из узла B в узел E — это левый поворот в исходной сети, штраф для связи

между узлами BE и EF в новой сети равен 3. Цена связи, соединяющей узлы E и

F в исходной сети, равна 3, поэтому полная цена новой связи равна 3 + 3 =

6.

=======338

@Рис. 12.15. Сеть и соответствующая ей сеть со штрафами за повороты

Предположим теперь, что требуется найти для исходной сети дерево

кратчайшего маршрута с корнем в узле D. Чтобы сделать это, создадим в новой

сети ложный корневой узел, затем построим связи, соединяющие этот узел со

всеми связями, которые покидают узел D в исходной сети. Присвоим этим

связям ту же цену, которую имеют соответствующие связи в исходной сети. На

рис. 12.16 показана новая сеть с рис. 12.15 с ложным корневым узлом,

соответствующим узлу D. Дерево кратчайшего маршрута в этой сети нарисовано

жирной линией.

Чтобы найти кратчайший маршрут из узла D в узел C, необходимо проверить все

узлы в новой сети, которые соответствуют связям, заканчивающимся в узле C.

В этом примере это узлы BC и FC. Ближайший к ложному корню узел

соответствует кратчайшему маршруту к узлу C в исходной сети. Узлы в

кратчайшем маршруте в новой сети соответствуют связям в кратчайшем маршруте

в исходной сети.

@Рис. 12.16. Дерево кратчайшего маршрута в сети со штрафами за повороты

========339

На рис. 12.16 кратчайший маршрут начинается с ложного корня, идет в узел

DE, затем узлы EF и FC и имеет полную цену 16. Этот путь соответствует пути

D, E, F, C в исходной сети. Прибавив один штраф за левый поворот E, F, C,

получим, что цена этого пути в исходной сети также равна 16.

Заметьте, что вы не нашли бы этот путь, если бы построили дерево

кратчайшего маршрута в исходной сети. Без учета штрафов за повороты,

кратчайшим маршрутом из узла D в узел C был бы путь D, E, B, C с полной

ценой 12. С учетом штрафов цена этого пути равна 17.

Применения метода поиска кратчайшего маршрута

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

Очевидным примером является поиск кратчайшего маршрута между двумя точками

в уличной сети. Многие другие приложения используют метод поиска

кратчайшего маршрута менее очевидными способами. Следующие разделы

описывают некоторые из этих приложений.

Разбиение на районы

Предположим, что имеется карта города, на которую нанесены все пожарные

депо. Может потребоваться определить для каждой точки города ближайшее к

ней депо. На первый взгляд это кажется трудной задачей. Можно попытаться

рассчитать дерево кратчайшего маршрута с корнем в каждом узле сети, чтобы

найти, какое депо расположено ближе всего к каждому из узлов. Или можно

построить дерево кратчайшего маршрута с корнем в каждом из пожарных депо и

записать расстояние от каждого из узлов до каждого из депо. Но существует

намного более быстрый метод.

Создадим ложный корневой узел и соединим его с каждым из пожарных депо

связями с нулевой ценой. Затем найдем дерево кратчайшего маршрута с корнем

в этом ложном узле. Для каждой точки в сети кратчайший маршрут из ложного

корневого узла к этой точке пройдет через ближайшее к этой точке пожарное

депо. Чтобы найти ближайшее к точке пожарное депо, нужно просто

проследовать по кратчайшему маршруту от этой точки к корню, пока на пути не

встретится одно из депо. Построив всего одно дерево кратчайшего маршрута,

можно найти ближайшие пожарные депо для каждой точки в сети.

Программа District использует этот алгоритм для разбиения сети на районы.

Так же, как и программа PathC и другие программы, описанные в этой главе,

она позволяет загружать, редактировать и сохранять на диске ориентированные

сети с ценой связей. Если вы не добавляете и не удаляете узлы или связи, вы

можете выбрать депо для разделения на районы. Добавьте узлы к списку

пожарных депо щелчком левой кнопки мыши, затем щелкните правой кнопкой в

любом месте формы, и программа разобьет сеть на районы.

На рис. 12.17 показано окно программы, на котором изображена сеть с тремя

депо. Депо в узлах 3, 18 и 20 обведены жирными кружочками. Разбивающие сеть

на районы деревья кратчайшего маршрута изображены жирными линиями.

=====340

@Рис. 12.17. Программа District

Составление плана работ с использованием метода критического пути

Во многих задачах, в том числе в больших программных проектах, определенные

действия должны быть выполнены раньше других. Например, при строительстве

дома до установки фундамента нужно вырыть котлован, фундамент должен

застыть до того, как начнется возведение стен, каркас дома должен быть

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

водопровода и кровельные работы и так далее.

Некоторые из этих задач могут выполняться одновременно, другие должны

выполняться последовательно. Например, можно одновременно проводить

электричество и прокладывать водопровод.

Критическим путем (critical path) называется одна из самых длинных

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

проекта. Важность задач, лежащих на критическом пути, определяется тем, что

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

проекта в целом. Если заложить фундамент на неделю позже, то и здание будет

завершено на неделю позже. Для определения заданий, которые находятся на

критическом пути, можно использовать модифицированный алгоритм поиска

кратчайшего маршрута.

Вначале создадим сеть, которая представляет временные соотношения между

задачами проекта. Пусть каждой задаче соответствует узел. Нарисуем связь

между задачей I и задачей J, если задача I должна быть выполнена до начала

задачи J, и присвоим этой связи цену, равную времени выполнения задачи I.

После этого создадим два ложных узла, один из которых будет соответствовать

началу проекта, а другой — его завершению. Соединим начальный узел связями

с нулевой ценой со всеми узлами в проекте, в которые не входит ни одна

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

начинать немедленно, не ожидая завершения других задач.

Затем создадим ложные связи нулевой длины, соединяющие все узлы, из которых

не выходит не одной связи, с конечным узлом. Эти узлы представляют задачи,

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

будут выполнены, проект будет завершен.

Найдя самый длинный маршрут между начальным и конечным узлами сети, мы

получим критический путь проекта. Входящие в него задачи будут критичными

для выполнения проекта.

========341

@Таблица 12.1. Этапы сборки дождевальной установки

Рассмотрим, например, упрощенный проект сборки дождевальной установки,

состоящий из пяти задач. В табл. 12.1 приведены задачи и временные

соотношения между ними. Сеть для этого проекта показана на рис. 12.18.

В этом простом примере легко увидеть, что самый длинный маршрут в сети

выполняет следующую последовательность задач: выкопать канавы, смонтировать

трубы, закопать их. Это критические задачи, и если в выполнении какой-либо

из них наступит задержка, выполнение проекта также задержится.

Длина этого критического пути равна ожидаемому времени завершения проекта.

В данном случае, если все задачи будут выполнены вовремя, выполнение

проекта займет пять дней. При этом предполагается также, что если это

возможно, несколько задач будут выполняться одновременно. Например, один

человек может копать канавы, пока другой будет закупать трубы.

В более значительном проекте, таком как строительство небоскреба или съемка

фильма, могут содержаться тысячи задач, и критические пути при этом могут

быть совсем не очевидны.

Планирование коллективной работы

Предположим, что требуется набрать несколько сотрудников для ответов на

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

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

сотрудников отвечал на звонки с 9 утра до 5 вечера. В табл. 12.2 приведены

рабочие часы сотрудников, и их почасовая оплата.

@Рис. 12.18. Сеть задач сборки дождевальной установки

======342

@Таблица 12.2. Рабочие часы сотрудников и их почасовая оплата

Для построения соответствующей сети, создадим один узел для каждого

рабочего часа. Соединим эти узлы связями, каждая из которых соответствует

рабочим часам какого-либо сотрудника. Если сотрудник может работать с 9 до

11, нарисуем связь между узлом 9:00 и узлом 11:00, и присвоим этой связи

цену, равную зарплате, получаемой данным сотрудником за соответствующее

время. Если сотрудник получает 6,5 долларов в час, и отрезок времени

составляет два часа, то цена связи равна 13 долларам. На рис. 12.19

показана сеть, соответствующая данным из табл. 12.2.

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

сотрудников с наименьшей суммарной зарплатой. Каждая связь в пути

соответствует работе сотрудника в определенный промежуток времени. В данном

случае кратчайший маршрут из узла 9:00 в узел 5:00 проходит через узлы

11:00, 12:00 и 3:00. Этому соответствует следующий график работы: сотрудник

A работает с 9:00 до 11:00, сотрудник D работает с 11:00 до 12:00, затем

сотрудник A снова работает с 12:00 до 3:00 и сотрудник E работает с 3:00 до

5:00. Полная зарплата всех сотрудников при таком графике составляет 52,15

доллара.

@Рис. 12.19. Сеть графика работы коллектива

======343

Максимальный поток

Во многих сетях связи имеют кроме цены, еще и пропускную способность

(capacity). Через каждый узел сети может проходить поток (flow), который не

превышает ее пропускной способности. Например, по улицам может проехать

только определенной число машин. Сеть с заданными пропускными способностями

ее связей называется нагруженной сетью (capacitated network). Если задана

нагруженная сеть, задача о максимальном потоке заключается в определении

наибольшего возможного потока через сеть из заданного источника (source) в

заданный сток (sink).

На рис. 12.20 показана небольшая нагруженная сеть. Числа рядом со связями в

этой сети — это не цена связи, а ее пропускная способность. В этом примере

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

направляются по пути A, B, E,F и еще две — по пути A, C, D, F.

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

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

найденное решение. Алгоритм завершает работу, если нельзя улучшить

имеющееся решение.

Для поиска путей способов увеличения полного потока, алгоритм проверяет

остаточную пропускную способность (residual capacity) связей. Остаточная

пропускная способность связи между узлами I и J равна максимальному

дополнительному потоку, который можно направить из узла I в узел J,

используя связь между I и J и связь между J и I. Этот суммарный поток может

включать дополнительный поток по связи I-J, если в этой связи есть резерв

пропускной способности, или исключать часть потока из связи J-I, если по

этой связи идет поток.

Например, предположим, что в сети, соединяющей узлы A и C на рис. 12.20,

существует поток, равный 2. Так как пропускная способность этой связи равна

3, то к этой связи можно добавить единицу потока, поэтому остаточная

пропускная способность этой связи равна 1. Хотя сеть, показанная на рис.

12.20 не имеет связи C-A, для этой связи существует остаточная пропускная

способность. В данном примере, так как по связи A-C идет поток, равный 2,

то можно удалить до двух единиц этого потока. При этом суммарный поток из

узла C в узел A увеличился бы на 2, поэтому остаточная пропускная

способность связи C-A равна 2.

@Рис. 12.20. Нагруженная сеть

========344

@Рис. 12.21. Потоки в сети

Сеть, состоящая из всех связей с положительной остаточной пропускной

способностью, называется остаточной сетью (residual network). На рис. 12.21

показана сеть с рис. 12.20, каждой связи в которой присвоен поток. Для

каждой связи, первое число равно потоку через связь, а второе — ее

пропускной способности. Надпись «1/2», например, означает, что поток через

связь равен 1, и ее пропускная способность равна 2. Связи, поток через

которые больше нуля, нарисованы жирными линиями.

На рис. 12.22 показана остаточная сеть, соответствующая потокам на рис.

12.21. Нарисованы только связи, которые действительно могут иметь

остаточную пропускную способность. Например, между узлами A и D не

нарисовано ни одной связи. Исходная сеть не содержит связи A-D или D-A,

поэтому эти связи всегда будут иметь нулевую остаточную пропускную

способность.

Одно из свойств остаточных сетей состоит в том, что любой путь,

использующий связи с остаточной пропускной способностью больше нуля,

который связывает источник со стоком, дает способ увеличения потока в сети.

Так как этот путь дает способ увеличения или расширения потока в сети, он

называется расширяющим путем (augmenting path). На рис. 12.23 показана

остаточная сеть с рис. 12.22 с расширяющим путем, нарисованным жирной

линией.

Чтобы обновить решение, используя расширяющий путь, найдем наименьшую

остаточную пропускную способность в пути. Затем скорректируем потоки в пути

в соответствии с этим значением. Например, на рис. 12.23 наименьшая

остаточная пропускная способность сетей в расширяющем пути равна 2. Чтобы

обновить потоки в сети, к любой связи I-J на пути добавляется поток 2, а из

всех обратных им связей J-I вычитается поток 2.

@Рис. 12.22. Остаточная сеть

========345

@Рис. 12.23. Расширяющий путь через остаточную сеть

Вместо того, чтобы корректировать потоки, и затем перестраивать остаточную

сеть, проще просто скорректировать остаточную сеть. Затем после завершения

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

связей в исходной сети.

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

расширяющему пути. Вычтем 2 из остаточной пропускной способности всех

связей I-J вдоль пути, и добавим 2 к остаточной пропускной способности

соответствующей связи J-I. На рис. 12.24 показана скорректированная

остаточная сеть для этого примера.

Если больше нельзя найти ни одного расширяющего пути, то можно использовать

остаточную сеть для вычисления потоков в исходной сети. Для каждой связи

между узлами I и J, если остаточный поток между узлами I и J меньше, чем

пропускная способность связи, то поток должен равняться пропускной

способности минус остаточный поток. В противном случае поток должен быть

равен нулю.

Например, на рис. 12.24 остаточный поток из узла A в узел C равен 1 и

пропускная способность связи A-C равна 3. Так как 1 меньше 3, то поток

через узел будет равен 3 - 1 = 2. На рис. 12.25 показаны потоки в сети,

соответствующие остаточной сети на рис. 12.24.

@Рис. 12.24. Скорректированная остаточная сеть

========346

@Рис. 12.25. Максимальные потоки

Полученный алгоритм еще не содержит метода для поиска расширяющих путей в

остаточной сети. Один из возможных методов аналогичен методу коррекции

меток для алгоритма кратчайшего маршрута. Вначале поместим узел-источник в

список возможных узлов. Затем, если список возможных узлов не пуст, будем

удалять из него по одному узлу. Проверим все соседние узлы, соединенные с

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

нуля. Если соседний узел еще не был помещен в список возможных узлов,

добавить его в список. Продолжить этот процесс до тех пор, пока список

возможных узлов не опустеет.

Этот метод имеет два отличия от метода поиска кратчайшего маршрута

коррекцией меток. Во-первых, этот метод не прослеживает связи с нулевой

остаточной пропускной способностью. Алгоритм же кратчайшего маршрута

проверяет все пути, независимо от их цены.

Во-вторых, этот алгоритм проверяет все узлы не больше одного раза. Алгоритм

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

помещать их снова в список возможных узлов, если он позднее найдет более

короткий путь от корня к этому узлу. При поиске расширяющего пути нет

необходимости проверять его длину, поэтому не нужно обновлять пути и

помещать узлы назад в список возможных узлов.

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

программе на Visual Basic. Этот код предназначен для работы с

неориентированными сетями, похожими на те, которые использовались в других

программах примеров, описанных в этой главе. После завершения работы

алгоритма он присваивает связи цену, равную потоку через нее, взятому со

знаком минус, если поток течет в обратном направлении. Другими словами,

если сеть содержит объект, представляющий связь I-J, а алгоритм определяет,

что поток должен течь в направлении связи J-I, то потоку через связь I-J

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

связь J-I, взятому со знаком минус. Это позволяет программе определять

направление потока, используя существующую структуру узлов.

=======347

Private Sub FindMaxFlows()

Dim candidates As Collection

Dim Residual() As Integer

Dim num_nodes As Integer

Dim id1 As Integer

Dim id2 As Integer

Dim node As FlowNode

Dim to_node As FlowNode

Dim from_node As FlowNode

Dim link As FlowLink

Dim min_residual As Integer

If SourceNode Is Nothing Or SinkNode Is Nothing _

Then Exit Sub

' Задать размер массива остаточной пропускной способности.

num_nodes = Nodes.Count

ReDim Residual(1 To num_nodes, 1 To num_nodes)

' Первоначально значения остаточной пропускной способности

' равны значениям пропускной способности.

For Each node In Nodes

id1 = node.Id

For Each link In node.Links

If link.Node1 Is node Then

Set to_node = link.Node2

Else

Set to_node = link.Node1

End If

id2 = to_node.Id

Residual(id1, id2) = link.Capacity

Next link

Next node

' Повторять до тех пор, пока больше

' не найдется расширяющих путей.

Do

' Найти расширяющий путь в остаточной сети.

' Сбросить значения NodeStatus и InLink всех узлов.

For Each node In Nodes

node.NodeStatus = NOT_IN_LIST

Set node.InLink = Nothing

Next node

' Начать с пустого списка возможных узлов.

Set candidates = New Collection

Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15


© 2010 БИБЛИОТЕКА РЕФЕРАТЫ