حل مساله بیشینه جریان — راهنمای جامع

۱۸۳۰ بازدید
آخرین به‌روزرسانی: ۱۹ تیر ۱۴۰۲
زمان مطالعه: ۱۰ دقیقه
حل مساله بیشینه جریان — راهنمای جامع

در این مطلب، روش حل مساله بیشینه جریان (Maximum Flow Problem) بیان و راهکار بیان شده برای آن در زبان‌های برنامه‌نویسی گوناگون شامل «سی‌پلاس‌پلاس» (++C)، «جاوا» (Java)، «پایتون» (Python) و «سی‌شارپ» (#C) پیاده‌سازی شده است. یک گراف داده شده که نشانگر یک «شبکه جریان» (Flow Network) است و در آن، هر «یال» (Edge) دارای ظرفیت است. همچنین، دو راس s (مبدا | Source) و t (مقصد | Sink) در گراف داده شده است. هدف پیدا کردن بیشینه جریان ممکنی است که از s به t با در نظر داشتن محدودیت‌های زیر، شکل می‌گیرد.

  • جریان روی یک یال از ظرفیت داده شده روی آن یال سر ریز نمی‌کند.
  • جریان ورودی برای هر راس، به جز s و t، مساوی با جریان خروجی است.

برای مثال، می‌توان گراف زیر را در نظر گرفت.

مساله بیشینه جریان -- راهنمای جامع

مساله بیشینه جریان -- راهنمای جامع

بیشینه جریان ممکن در گراف بالا برابر با ۲۳ است.

الگوریتم فورد–فالکرسون برای حل مساله بیشینه جریان

در ادامه، «الگوریتم فورد–فالکرسون» (Ford-Fulkerson Algorithm) برای حل مساله بیشینه جریان ارائه شده است.

  •  با جریان اولیه ۰ شروع کن.
  • تا هنگامی که یک مسیر تجمعی از مبدا به مقصد وجود دارد، این «مسیر-جریان» (Path-Flow) را به «جریان» اضافه کن.
  • جریان را بازگردان.

پیچیدگی زمانی الگوریتم بالا از درجه O(max_flow * E)‎ است. هنگامی که یک مسیر تجمعی وجود داشته باشد، یک حلقه اجرا می‌شود. در بدترین حالت، ممکن است ۱ واحد جریان در هر تکرار اضافه شود. بنابراین، پیچیدگی زمانی برابر با O(max_flow * E)‎ خواهد شد.

پیاده‌سازی الگوریتم ساده بالا

برای پیاده‌سازی الگوریتم بالا، ابتدا باید مفهوم «گراف باقیمانده» (Residual Graph) تعریف شود که برای درک این پیاده‌سازی مورد نیاز است. گراف باقیمانده از یک شبکه جریان، گرافی است که جریان اضافی ممکن را نشان می‌دهد. اگر مسیری از مبدا به مقصد وجود داشته باشد، این امکان وجود دارد که جریان اضافه شود. هر یال از گراف دارای مقداری است که به آن ظرفیت باقیمانده گفته می‌شود. ظرفیت باقیمانده برابر با ظرفیت اصلی یال‌ها منهای جریان کنونی است. ظرفیت باقیمانده اساسا ظرفیت کنونی یال‌ها است.

اکنون، پیرامون جزئیات پیاده‌سازی صحبت خواهد شد. اگر هیچ یالی بین دو راس از گراف باقیمانده وجود نداشته باشد، ظرفیت باقیمانده برابر با ۰ است. می‌توان با توجه به اینکه هیچ جریان اولیه‌ای وجود ندارد، به گراف باقیمانده مقداردهی اولیه کرد. همچنین، ظرفیت باقیمانده اولیه برابر با ظرفیت اصلی است. برای پیدا کردن مسیر تجمعی، می‌توان از الگوریتم «جستجوی اول سطح» (Breadth-First Search) یا «جستجوی اول عمق» (Depth-First Search) استفاده کرد. در پیاده‌سازی زیر، برای پیدا کردن مسیر تجمعی از BFS استفاده شده است.

با استفاده از BFS، می‌توان بررسی کرد که آیا مسیری از مبدا به مقصد وجود دارد یا خیر. همچنین، BFS، آرایه parent[]‎ را می‌سازد. با استفاده از آرایه parent[]‎، پیمایش در مسیر یافته شده انجام و جریان احتمالی از طریق این مسیر با پیدا کردن ظرفیت باقیمانده در طول مسیر پیدا می‌شود. در ادامه، جریان مسیر یافت شده به جریان کلی اضافه می‌شود. نکته مهم آن است که به روز رسانی ظرفیت باقیمانده‌ها در گراف باقیمانده مورد نیاز است. جریان مسیر از همه یال‌ها در طول مسیر کسر می‌شود و جریان مسیر در امتداد یال‌های معکوس اضافه می‌شود. نیاز به اضافه کردن جریان مسیر در طول یال‌های معکوس است، زیرا ممکن است بعدا نیاز به ارسال جریان در جهت معکوس باشد.

حل مساله بیشینه جریان در ++C

1// C++ program for implementation of Ford Fulkerson algorithm 
2#include <iostream> 
3#include <limits.h> 
4#include <string.h> 
5#include <queue> 
6using namespace std; 
7  
8// Number of vertices in given graph 
9#define V 6 
10  
11/* Returns true if there is a path from source 's' to sink 't' in 
12  residual graph. Also fills parent[] to store the path */
13bool bfs(int rGraph[V][V], int s, int t, int parent[]) 
14{ 
15    // Create a visited array and mark all vertices as not visited 
16    bool visited[V]; 
17    memset(visited, 0, sizeof(visited)); 
18  
19    // Create a queue, enqueue source vertex and mark source vertex 
20    // as visited 
21    queue <int> q; 
22    q.push(s); 
23    visited[s] = true; 
24    parent[s] = -1; 
25  
26    // Standard BFS Loop 
27    while (!q.empty()) 
28    { 
29        int u = q.front(); 
30        q.pop(); 
31  
32        for (int v=0; v<V; v++) 
33        { 
34            if (visited[v]==false && rGraph[u][v] > 0) 
35            { 
36                q.push(v); 
37                parent[v] = u; 
38                visited[v] = true; 
39            } 
40        } 
41    } 
42  
43    // If we reached sink in BFS starting from source, then return 
44    // true, else false 
45    return (visited[t] == true); 
46} 
47  
48// Returns the maximum flow from s to t in the given graph 
49int fordFulkerson(int graph[V][V], int s, int t) 
50{ 
51    int u, v; 
52  
53    // Create a residual graph and fill the residual graph with 
54    // given capacities in the original graph as residual capacities 
55    // in residual graph 
56    int rGraph[V][V]; // Residual graph where rGraph[i][j] indicates  
57                     // residual capacity of edge from i to j (if there 
58                     // is an edge. If rGraph[i][j] is 0, then there is not)   
59    for (u = 0; u < V; u++) 
60        for (v = 0; v < V; v++) 
61             rGraph[u][v] = graph[u][v]; 
62  
63    int parent[V];  // This array is filled by BFS and to store path 
64  
65    int max_flow = 0;  // There is no flow initially 
66  
67    // Augment the flow while tere is path from source to sink 
68    while (bfs(rGraph, s, t, parent)) 
69    { 
70        // Find minimum residual capacity of the edges along the 
71        // path filled by BFS. Or we can say find the maximum flow 
72        // through the path found. 
73        int path_flow = INT_MAX; 
74        for (v=t; v!=s; v=parent[v]) 
75        { 
76            u = parent[v]; 
77            path_flow = min(path_flow, rGraph[u][v]); 
78        } 
79  
80        // update residual capacities of the edges and reverse edges 
81        // along the path 
82        for (v=t; v != s; v=parent[v]) 
83        { 
84            u = parent[v]; 
85            rGraph[u][v] -= path_flow; 
86            rGraph[v][u] += path_flow; 
87        } 
88  
89        // Add path flow to overall flow 
90        max_flow += path_flow; 
91    } 
92  
93    // Return the overall flow 
94    return max_flow; 
95} 
96  
97// Driver program to test above functions 
98int main() 
99{ 
100    // Let us create a graph shown in the above example 
101    int graph[V][V] = { {0, 16, 13, 0, 0, 0}, 
102                        {0, 0, 10, 12, 0, 0}, 
103                        {0, 4, 0, 0, 14, 0}, 
104                        {0, 0, 9, 0, 0, 20}, 
105                        {0, 0, 0, 7, 0, 4}, 
106                        {0, 0, 0, 0, 0, 0} 
107                      }; 
108  
109    cout << "The maximum possible flow is " << fordFulkerson(graph, 0, 5); 
110  
111    return 0; 
112}

حل مساله بیشینه جریان در جاوا

1// Java program for implementation of Ford Fulkerson algorithm 
2import java.util.*; 
3import java.lang.*; 
4import java.io.*; 
5import java.util.LinkedList; 
6  
7class MaxFlow 
8{ 
9    static final int V = 6;    //Number of vertices in graph 
10  
11    /* Returns true if there is a path from source 's' to sink 
12      't' in residual graph. Also fills parent[] to store the 
13      path */
14    boolean bfs(int rGraph[][], int s, int t, int parent[]) 
15    { 
16        // Create a visited array and mark all vertices as not 
17        // visited 
18        boolean visited[] = new boolean[V]; 
19        for(int i=0; i<V; ++i) 
20            visited[i]=false; 
21  
22        // Create a queue, enqueue source vertex and mark 
23        // source vertex as visited 
24        LinkedList<Integer> queue = new LinkedList<Integer>(); 
25        queue.add(s); 
26        visited[s] = true; 
27        parent[s]=-1; 
28  
29        // Standard BFS Loop 
30        while (queue.size()!=0) 
31        { 
32            int u = queue.poll(); 
33  
34            for (int v=0; v<V; v++) 
35            { 
36                if (visited[v]==false && rGraph[u][v] > 0) 
37                { 
38                    queue.add(v); 
39                    parent[v] = u; 
40                    visited[v] = true; 
41                } 
42            } 
43        } 
44  
45        // If we reached sink in BFS starting from source, then 
46        // return true, else false 
47        return (visited[t] == true); 
48    } 
49  
50    // Returns tne maximum flow from s to t in the given graph 
51    int fordFulkerson(int graph[][], int s, int t) 
52    { 
53        int u, v; 
54  
55        // Create a residual graph and fill the residual graph 
56        // with given capacities in the original graph as 
57        // residual capacities in residual graph 
58  
59        // Residual graph where rGraph[i][j] indicates 
60        // residual capacity of edge from i to j (if there 
61        // is an edge. If rGraph[i][j] is 0, then there is 
62        // not) 
63        int rGraph[][] = new int[V][V]; 
64  
65        for (u = 0; u < V; u++) 
66            for (v = 0; v < V; v++) 
67                rGraph[u][v] = graph[u][v]; 
68  
69        // This array is filled by BFS and to store path 
70        int parent[] = new int[V]; 
71  
72        int max_flow = 0;  // There is no flow initially 
73  
74        // Augment the flow while tere is path from source 
75        // to sink 
76        while (bfs(rGraph, s, t, parent)) 
77        { 
78            // Find minimum residual capacity of the edhes 
79            // along the path filled by BFS. Or we can say 
80            // find the maximum flow through the path found. 
81            int path_flow = Integer.MAX_VALUE; 
82            for (v=t; v!=s; v=parent[v]) 
83            { 
84                u = parent[v]; 
85                path_flow = Math.min(path_flow, rGraph[u][v]); 
86            } 
87  
88            // update residual capacities of the edges and 
89            // reverse edges along the path 
90            for (v=t; v != s; v=parent[v]) 
91            { 
92                u = parent[v]; 
93                rGraph[u][v] -= path_flow; 
94                rGraph[v][u] += path_flow; 
95            } 
96  
97            // Add path flow to overall flow 
98            max_flow += path_flow; 
99        } 
100  
101        // Return the overall flow 
102        return max_flow; 
103    } 
104  
105    // Driver program to test above functions 
106    public static void main (String[] args) throws java.lang.Exception 
107    { 
108        // Let us create a graph shown in the above example 
109        int graph[][] =new int[][] { {0, 16, 13, 0, 0, 0}, 
110                                     {0, 0, 10, 12, 0, 0}, 
111                                     {0, 4, 0, 0, 14, 0}, 
112                                     {0, 0, 9, 0, 0, 20}, 
113                                     {0, 0, 0, 7, 0, 4}, 
114                                     {0, 0, 0, 0, 0, 0} 
115                                   }; 
116        MaxFlow m = new MaxFlow(); 
117  
118        System.out.println("The maximum possible flow is " + 
119                           m.fordFulkerson(graph, 0, 5)); 
120  
121    } 
122} 

حل مساله بیشینه جریان در پایتون

1# Python program for implementation of Ford Fulkerson algorithm 
2   
3from collections import defaultdict 
4   
5#This class represents a directed graph using adjacency matrix representation 
6class Graph: 
7   
8    def __init__(self,graph): 
9        self.graph = graph # residual graph 
10        self. ROW = len(graph) 
11        #self.COL = len(gr[0]) 
12          
13   
14    '''Returns true if there is a path from source 's' to sink 't' in 
15    residual graph. Also fills parent[] to store the path '''
16    def BFS(self,s, t, parent): 
17  
18        # Mark all the vertices as not visited 
19        visited =[False]*(self.ROW) 
20          
21        # Create a queue for BFS 
22        queue=[] 
23          
24        # Mark the source node as visited and enqueue it 
25        queue.append(s) 
26        visited[s] = True
27           
28         # Standard BFS Loop 
29        while queue: 
30  
31            #Dequeue a vertex from queue and print it 
32            u = queue.pop(0) 
33          
34            # Get all adjacent vertices of the dequeued vertex u 
35            # If a adjacent has not been visited, then mark it 
36            # visited and enqueue it 
37            for ind, val in enumerate(self.graph[u]): 
38                if visited[ind] == False and val > 0 : 
39                    queue.append(ind) 
40                    visited[ind] = True
41                    parent[ind] = u 
42  
43        # If we reached sink in BFS starting from source, then return 
44        # true, else false 
45        return True if visited[t] else False
46              
47      
48    # Returns tne maximum flow from s to t in the given graph 
49    def FordFulkerson(self, source, sink): 
50  
51        # This array is filled by BFS and to store path 
52        parent = [-1]*(self.ROW) 
53  
54        max_flow = 0 # There is no flow initially 
55  
56        # Augment the flow while there is path from source to sink 
57        while self.BFS(source, sink, parent) : 
58  
59            # Find minimum residual capacity of the edges along the 
60            # path filled by BFS. Or we can say find the maximum flow 
61            # through the path found. 
62            path_flow = float("Inf") 
63            s = sink 
64            while(s !=  source): 
65                path_flow = min (path_flow, self.graph[parent[s]][s]) 
66                s = parent[s] 
67  
68            # Add path flow to overall flow 
69            max_flow +=  path_flow 
70  
71            # update residual capacities of the edges and reverse edges 
72            # along the path 
73            v = sink 
74            while(v !=  source): 
75                u = parent[v] 
76                self.graph[u][v] -= path_flow 
77                self.graph[v][u] += path_flow 
78                v = parent[v] 
79  
80        return max_flow 
81  
82   
83# Create a graph given in the above diagram 
84  
85graph = [[0, 16, 13, 0, 0, 0], 
86        [0, 0, 10, 12, 0, 0], 
87        [0, 4, 0, 0, 14, 0], 
88        [0, 0, 9, 0, 0, 20], 
89        [0, 0, 0, 7, 0, 4], 
90        [0, 0, 0, 0, 0, 0]] 
91  
92g = Graph(graph) 
93  
94source = 0; sink = 5
95   
96print ("The maximum possible flow is %d " % g.FordFulkerson(source, sink)) 
97  
98#This code is contributed by Neelam Yadav 

حل مساله بیشینه جریان در #C

1// C# program for implementation  
2// of Ford Fulkerson algorithm 
3using System; 
4using System.Collections.Generic; 
5  
6public class MaxFlow 
7{ 
8    static readonly int V = 6; //Number of vertices in graph 
9  
10    /* Returns true if there is a path 
11    from source 's' to sink 't' in residual 
12    graph. Also fills parent[] to store the 
13    path */
14    bool bfs(int [,]rGraph, int s, int t, int []parent) 
15    { 
16        // Create a visited array and mark  
17        // all vertices as not visited 
18        bool []visited = new bool[V]; 
19        for(int i = 0; i < V; ++i) 
20            visited[i] = false; 
21  
22        // Create a queue, enqueue source vertex and mark 
23        // source vertex as visited 
24        List<int> queue = new List<int>(); 
25        queue.Add(s); 
26        visited[s] = true; 
27        parent[s] = -1; 
28  
29        // Standard BFS Loop 
30        while (queue.Count != 0) 
31        { 
32            int u = queue[0]; 
33                queue.RemoveAt(0); 
34  
35            for (int v = 0; v < V; v++) 
36            { 
37                if (visited[v] == false && rGraph[u, v] > 0) 
38                { 
39                    queue.Add(v); 
40                    parent[v] = u; 
41                    visited[v] = true; 
42                } 
43            } 
44        } 
45  
46        // If we reached sink in BFS  
47        // starting from source, then 
48        // return true, else false 
49        return (visited[t] == true); 
50    } 
51  
52    // Returns tne maximum flow 
53    // from s to t in the given graph 
54    int fordFulkerson(int [,]graph, int s, int t) 
55    { 
56        int u, v; 
57  
58        // Create a residual graph and fill  
59        // the residual graph with given  
60        // capacities in the original graph as 
61        // residual capacities in residual graph 
62  
63        // Residual graph where rGraph[i,j]  
64        // indicates residual capacity of  
65        // edge from i to j (if there is an  
66        // edge. If rGraph[i,j] is 0, then  
67        // there is not) 
68        int [,]rGraph = new int[V, V]; 
69  
70        for (u = 0; u < V; u++) 
71            for (v = 0; v < V; v++) 
72                rGraph[u, v] = graph[u, v]; 
73  
74        // This array is filled by BFS and to store path 
75        int []parent = new int[V]; 
76  
77        int max_flow = 0; // There is no flow initially 
78  
79        // Augment the flow while tere is path from source 
80        // to sink 
81        while (bfs(rGraph, s, t, parent)) 
82        { 
83            // Find minimum residual capacity of the edhes 
84            // along the path filled by BFS. Or we can say 
85            // find the maximum flow through the path found. 
86            int path_flow = int.MaxValue; 
87            for (v = t; v != s; v = parent[v]) 
88            { 
89                u = parent[v]; 
90                path_flow = Math.Min(path_flow, rGraph[u,v]); 
91            } 
92  
93            // update residual capacities of the edges and 
94            // reverse edges along the path 
95            for (v = t; v != s; v = parent[v]) 
96            { 
97                u = parent[v]; 
98                rGraph[u,v] -= path_flow; 
99                rGraph[v,u] += path_flow; 
100            } 
101  
102            // Add path flow to overall flow 
103            max_flow += path_flow; 
104        } 
105  
106        // Return the overall flow 
107        return max_flow; 
108    } 
109  
110    // Driver code 
111    public static void Main () 
112    { 
113        // Let us create a graph shown in the above example 
114        int [,]graph =new int[,] { {0, 16, 13, 0, 0, 0}, 
115                                    {0, 0, 10, 12, 0, 0}, 
116                                    {0, 4, 0, 0, 14, 0}, 
117                                    {0, 0, 9, 0, 0, 20}, 
118                                    {0, 0, 0, 7, 0, 4}, 
119                                    {0, 0, 0, 0, 0, 0} 
120                                }; 
121        MaxFlow m = new MaxFlow(); 
122  
123        Console.WriteLine("The maximum possible flow is " + 
124                        m.fordFulkerson(graph, 0, 5)); 
125  
126    } 
127} 
128  
129/* This code contributed by PrinciRaj1992 */

خروجی قطعه کدهای بالا به صورت زیر است.

The maximum possible flow is 23

پیاده‌سازی بالا از الگوریتم فورد–فالکرسون را «الگوریتم ادموندز کارپ» (Edmonds-Karp Algorithm) می‌گویند. ایده اصلی نهفته در پس استفاده از الگوریتم ادموندز کارپ، استفاده از BFS است؛ زیرا BFS همیشه مسیری با کمترین تعداد یال را انتخاب می‌کند.

هنگامی که BFS مورد استفاده قرار می‌گیرد، پیچیدگی زمانی بدترین حالت را می‌توان به O(VE2)‎ کاهش داد. پیاده‌سازی بالا از ارائه ماتریس مجاورت استفاده می‌کند و BFS دارای پیچیدگی زمانی از درجه O(V2)‎ است.

این مسأله با توجه به اینکه در بسیاری از موقعیت‌های عملی به وقوع می‌پیوندد، بسیار حائز است. از جمله مثال‌هایی برای مسأله بیشینه جریان می‌توان زد، افزایش حمل و نقل با توجه به محدودیت‌های ترافیکی داده شده و افزایش جریان بسته‌ها در شبکه‌های کامپیوتری است.

اگر نوشته بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۷ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
GeeksforGeeks
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *