#python #graph-theory #network-flow
Вопрос:
В настоящее время я пытаюсь создать программу, которая находит максимальный поток через сеть при ограничении, что края с общим исходным узлом должны иметь один и тот же поток. Именно с этим ограничением у меня и возникают проблемы.
В настоящее время у меня есть код для получения всех маршрутов увеличения потока, но я не решаюсь кодировать увеличение, потому что не могу понять, как добавить ограничение. Я думаю, возможно, об алгоритме обратного отслеживания, который пытается назначить поток с помощью метода Форда-Фулкерсона, а затем пытается приспособиться к ограничению.
Итак, мой код:
Существует класс графа, который имеет в качестве атрибутов все вершины и ребра, а также методы для передачи падающих ребер в вершину и предыдущих ребер в вершину:
class WeightedEdge:
def __init__(self, from_vertex, to_vertex, capacity):
self.tail = from_vertex
self.head = to_vertex
self.capacity = capacity
class Graph:
def __init__(self, vertices, edges, capacities):
self.vertices = vertices
self.edges = [WeightedEdge(edges[i][0], edges[i][1], capacities[i]) for i in range(len(edges))]
self.__forward_list = {v : [] for v in vertices}
self.__reverse_list = {v: [] for v in vertices}
for i in range(len(edges)):
self.__forward_list[edges[i][0]].append((edges[i][1], capacities[i]))
self.__reverse_list[edges[i][1]].append((edges[i][0], capacities[i]))
def out_edges(self, vertex):
return [WeightedEdge(vertex, next_vertex, capacity) for (next_vertex, capacity) in self.__forward_list[vertex]]
def in_edges(self, vertex):
return [WeightedEdge(prev_vertex, vertex, capacity) for (prev_vertex, capacity) in self.__reverse_list[vertex]]
Каждое ребро-это объект, имеющий атрибут хвостовой вершины, атрибут емкости и атрибут головной вершины.
До сих пор это моя функция, которая получает маршруты увеличения потока:
def max_equal_split_flow(graph, source, sink):
def find_augmenting_routes(vertex):
route_buffer.append(vertex)
if vertex == sink:
flow_augmenting_routes.append([v for v in route_buffer])
return
else:
out_edges = graph.out_edges(vertex)
for edge in out_edges:
find_augmenting_routes(edge.head)
route_buffer.pop()
flow_augmenting_routes = []
route_buffer = []
find_augmenting_routes(source)
print(flow_augmenting_routes)
TLDR: Как я могу найти максимальный поток через сеть, когда все ребра, которые совместно используют исходный узел, должны иметь один и тот же поток?
Ответ №1:
Я бы предположил, что у вас есть входные данные, которые определяют максимальный поток через каждое ребро.
Алгоритм тогда:
- Поскольку ребра с общим источником должны иметь одинаковый фактический конечный поток, первым шагом должна быть небольшая предварительная обработка, чтобы уменьшить максимальный поток на каждом ребре до минимального потока из общего источника.
- примените стандартный алгоритм максимального потока.
- сначала выполните поиск по глубине от источника потока ( я предполагаю, что есть только один), пока не найдете узел с неравными потоками. Уменьшите максимальные потоки на всех ребрах от этого узла до минимального потока, назначенного алгоритмом. Повторно примените алгоритм.
Повторяйте шаг 3 до тех пор, пока не останется неравномерных потоков.
=====================
Второй алгоритм:
- Отрегулируйте пропускную способность каждого выходного ребра так, чтобы она была равна минимальной пропускной способности всех выходных ребер из общей вершины
- Выполните поиск по ширине сначала по графику. При добавлении ребра установите поток через ребро минимальным
- поток в исходный узел делится на количество выходящих ребер
- емкость края
- Когда поиск завершен, сумма поступает в узел назначения.
Для справки, вот код C , который я использую для поиска в глубину с использованием рекурсии
void cPathFinder::depthRecurse(int v)
{
// record new node on the search
myPred.push_back(v);
// remember this node has been visted
myPath[v] = 1;
// look for new adjacent nodes
for (int w : myGraph.adjacent(v))
if (!myPath[w])
{
// search from new node
depthRecurse(w);
}
}
Вот самый простой пример графика, который показывает, что регулировки пропускной способности выходящих ребер недостаточно для сбалансирования фактических оттоков
format flows
g
l src a 210
l a b 200
l a c 200
l b d 500
l c d 500
s src
e d
( если этот формат спецификации не сразу очевиден, он задокументирован здесь )
Узел » а » имеет два внешних края, пропускная способность которых была скорректирована для обеспечения баланса. Однако узел » src » не обеспечивает достаточного потока для заполнения краев, поэтому вывод
total flow 210
a -- b capacity 200 used 10
a -- c capacity 200 used 200
src -- a capacity 210 used 210
b -- d capacity 500 used 10
c -- d capacity 500 used 200
Это наводит на мысль о другом алгоритме № 3:
- Отрегулируйте пропускную способность каждого выходного ребра так, чтобы она была равна минимальной пропускной способности всех выходных ребер из общей вершины
- Примените стандартный алгоритм максимального потока.
- сначала выполните поиск по глубине от источника потока, пока не найдете узел с неравными потоками. Перераспределите потоки таким образом, чтобы они сбалансировались. Удалите внутренние ребра в узел и сделайте его новым источником потока с потоком, равным сумме удаленных внутренних ребер
- Примените алгоритм максимального потока, модифицированный для работы с несколькими источниками
- Повторяйте шаги 3 и 4, пока все потоки не уравновесятся.
Применение шагов 3 и 4 к предыдущему примеру дает
format multiflows
g
l a1 b 105
l a2 c 105
l b d 500
l c d 500
s a1
s a2
e d
b -- d capacity 500 used 105
a1 -- b capacity 105 used 105
c -- d capacity 500 used 105
a2 -- c capacity 105 used 105
total flow 210.000000
что соответствует требованиям.
=================================
Вот реализация PathFinder алгоритма Нестора
void cPathFinder::equiflows()
{
// source outflows
int outlinkpercent = 100 / node(myStart).outdegree();
for (auto amp;outlink : node(myStart).myLink)
outlink.second.myValue = outlinkpercent;
// function to call each time a node is visited in BFS
myVisitor.set(
[this](int v)
{
static int X = INT_MAX;
if( v == myEnd ) {
// destination reached, report maximum flow
std::cout << "maximum flow is " << X << "n";
return;
}
// sum the inputs
int sumInput = 0;
for (auto amp;inlink : inlinks(v)) {
sumInput = inlink.second.myValue;
}
// node outflows
int outValue = sumInput / node(v).outdegree();
for (auto amp;outlink : node(v).myLink) {
outlink.second.myValue = outValue;
// check if this outflow reduces maximum flow through network
int x = outlink.second.myCost * 100 / outValue;
if( x < X )
X = x;
}
});
// do a breadth first search, updating flows as we go along
breadth();
}
}
Комментарии:
1. Это звучит как хорошая идея, но только потому, что они обладают одинаковой емкостью, не означает, что через них будет проходить одинаковое количество, я думаю, вы на что-то наткнулись, хотя, что еще вы хотите знать? Если я смогу сначала решить проблему математически, как вопрос о потоках в сети, то кодирование будет вторичным.
2. Это «края с общим источником должны иметь одинаковый максимальный поток» или «края с общим источником должны иметь одинаковый фактический конечный поток»
3. Тот же конечный поток, поэтому, если у вас есть вершина A, которая соединена с B и C, если поток в A равен 10, то поток вдоль AB должен быть равен 5, а поток вдоль AC должен быть равен 5, потоки при необходимости могут быть плавающими.
4. Проблема с этим, однако, в том, что минимального потока нет, как бы я это нашел?
5. О, я неправильно понял то, что вы говорили, я только что перечитал это и думаю, что понимаю!