/// <summary> /// グラフを連結成分分解する /// </summary> /// <param name="graph"></param> /// <param name="ids"></param> /// <param name="edgeUsed">その辺を使うか否かを表す配列.nullの場合は全部使う.</param> /// <returns> /// グラフの連結成分数を返す /// また,ids[v]にvが属する連結成分のIDを格納する /// </returns> public static int WeaklyConnectedComponents(DirectedGraph graph, out int[] ids, bool[] edgeUsed = null) { int n = graph.Vertices.Count; ids = new int[n]; for (int i = 0; i < n; ++i) { ids[i] = -1; } int currId = 0; var stack = new Stack<int>(); for (int i = 0; i < n; ++i) { if (ids[i] != -1) { continue; } stack.Push(i); ids[i] = currId; while (stack.Count > 0) { int v = stack.Pop(); foreach (Edge e in graph.OutEdges[v]) { if (edgeUsed != null && !edgeUsed[e.Index]) { continue; } int w = e.Dst; if (ids[w] == -1) { ids[w] = currId; stack.Push(w); } } foreach (Edge e in graph.InEdges[v]) { if (edgeUsed != null && !edgeUsed[e.Index]) { continue; } int w = e.Src; if (ids[w] == -1) { ids[w] = currId; stack.Push(w); } } } currId += 1; } return currId; }
private static double Augment(DirectedGraph graph, double[] cap, double[] flow, int[] level, bool[] finished, int u, int t, double cur) { if (u == t || cur < SMALL) return cur; if (finished[u]) return 0; finished[u] = true; foreach (var e in graph.OutEdges[u]) { if (level[e.Dst] > level[u]) { double f = Augment(graph, cap, flow, level, finished, e.Dst, t, Math.Min(cur, cap[e.Index] - flow[e.Index])); if (f >= SMALL) { flow[e.Index] += f; finished[u] = false; return f; } } } foreach (var e in graph.InEdges[u]) { if (level[e.Dst] > level[u]) { double f = Augment(graph, cap, flow, level, finished, e.Dst, t, Math.Min(cur, flow[e.Index])); if (f >= SMALL) { flow[e.Index] -= f; finished[u] = false; return f; } } } return 0; }
// グラフをCSVから読み込む public static DirectedGraph LoadCSV(string fileName) { using (var reader = new StreamReader(fileName)) { string line; var id2index = new Dictionary<int, int>(); var g = new DirectedGraph(); for (int lineNo = 1; (line = reader.ReadLine()) != null; ++lineNo) { if (line.Length == 0) { continue; } string[] fields = line.Split(','); if (fields.Length < 2) { throw new Exception(string.Format("{0}:{1} Fields Too Few", fileName, lineNo)); } int src = int.Parse(fields[0]); int dst = int.Parse(fields[1]); if (!id2index.ContainsKey(src)) { id2index.Add(src, g.Vertices.Count); g.AddVertex(new Vertex(src)); } if (!id2index.ContainsKey(dst)) { id2index.Add(dst, g.Vertices.Count); g.AddVertex(new Vertex(dst)); } g.AddEdge(new Edge(id2index[src], id2index[dst])); } return g; } }
// グラフをCSVに書きこむ public static void SaveCSV(DirectedGraph g, string fileName) { using (var writer = new StreamWriter(fileName)) { foreach (Edge e in g.Edges) { writer.WriteLine("{0},{1}", g.Vertices[e.Src].OriginalId, g.Vertices[e.Dst].OriginalId); } } }
private static void OutputFlow(StreamWriter writer, DirectedGraph graph, double[] cap, double[] gain, double[] flow) { for (int e = 0; e < graph.Edges.Count; ++e) { writer.WriteLine("{0},{1},{2},{3},{4}", graph.Vertices[graph.Edges[e].Src].OriginalId, graph.Vertices[graph.Edges[e].Dst].OriginalId, cap[e], gain[e], flow[e]); } }
public static void Run(DirectedGraph graph, long graphLoadTime, int s, int t, double[] cap, double[] gain, double prob, string output, double eps) { int n = graph.Vertices.Count, m = graph.Edges.Count; Stopwatch stopwatch = new Stopwatch(); long compressionTime = 0; long gmfTime = 0; double minValue = 1e10; double[] minFlow = null; DirectedGraph minGraph = null; for (int i = 0; i < 4; ++i) { Trace.WriteLine("Compressing the given graph..."); stopwatch.Restart(); Compressor compressor = new Compressor(graph, prob, new Random()); compressionTime += stopwatch.ElapsedMilliseconds; var compressedGraph = compressor.CompressedGraph; var mapping = compressor.Mapping; Trace.WriteLine("Compressed Vertex Count: " + compressedGraph.Vertices.Count); Trace.WriteLine("Compressed Edge Count: " + compressedGraph.Edges.Count); Trace.WriteLine("Calculating the generalized maximum flow..."); stopwatch.Restart(); double[] flow; double value = FleischerWayne.GeneralizedMaximumFlow(compressor.CompressedGraph, cap, gain, mapping[s], mapping[t], eps, out flow); gmfTime += stopwatch.ElapsedMilliseconds; if (value < minValue) { minValue = value; minFlow = flow; minGraph = compressor.CompressedGraph; } } Trace.WriteLine("Writing the results"); Utility.CreateDirectoryIfNotExists(output); using (var writer1 = new StreamWriter(output + "/Flow.csv")) { OutputFlow(writer1, minGraph, cap, gain, minFlow); } using (var writer2 = new StreamWriter(output + "/Value.txt")) { writer2.WriteLine(minValue); } using (var writer3 = new StreamWriter(output + "/Sunnary.txt")) { writer3.WriteLine("VertexCount: " + n); writer3.WriteLine("EdgeCount: " + m); writer3.WriteLine("Source: " + graph.Vertices[s].OriginalId); writer3.WriteLine("Sink: " + graph.Vertices[t].OriginalId); writer3.WriteLine("Prob: " + prob); writer3.WriteLine("Compressed Vertex Count: " + minGraph.Vertices.Count); writer3.WriteLine("Compressed Edge Count: " + minGraph.Edges.Count); writer3.WriteLine("Graph Load Time [ms]: " + graphLoadTime); writer3.WriteLine("Compression Time [ms]: " + compressionTime); writer3.WriteLine("GMF Time [ms]: " + gmfTime); } }
private bool[] RandomSample(DirectedGraph graph, double prob, Random random) { int m = graph.Edges.Count; var result = new bool[m]; for (int i = 0; i < m; ++i) { if (random.NextDouble() < prob) { result[i] = true; } } return result; }
public static void FindSourceAndSink(int source, int sink, DirectedGraph graph, out int s, out int t) { s = -1; t = -1; for (int i = 0; i < graph.Vertices.Count; ++i) { if (graph.Vertices[i].OriginalId == source) { s = i; } if (graph.Vertices[i].OriginalId == sink) { t = i; } } if (s == -1 || t == -1 || s == t) { Console.Error.WriteLine("Error: 始点または終点が不正です."); Environment.Exit(1); } }
public static double MaximumFlow( DirectedGraph graph, double[] cap, int s, int t, out double[] flow) { if (s == t) { throw new ArgumentException("始点と終点が同じ頂点です."); } int n = graph.Vertices.Count; int m = graph.Edges.Count; flow = new double[m]; double total = 0; for (bool cont = true; cont; ) { cont = false; var level = new int[n]; for (int i = 0; i < n; ++i) level[i] = -1; level[s] = 0; var Q = new Queue<int>(); Q.Enqueue(s); for (int d = n; Q.Count > 0 && level[Q.Peek()] < d; ) { int u = Q.Dequeue(); if (u == t) d = level[u]; foreach (var e in graph.OutEdges[u]) { if (cap[e.Index] - flow[e.Index] >= SMALL && level[e.Dst] == -1) { Q.Enqueue(e.Dst); level[e.Dst] = level[u] + 1; } } foreach (var e in graph.InEdges[u]) { if (flow[e.Index] >= SMALL && level[e.Dst] == -1) { Q.Enqueue(e.Dst); level[e.Dst] = level[u] + 1; } } } var finished = new bool[n]; for (double f = 1; f > 0; ) { f = Augment(graph, cap, flow, level, finished, s, t, double.PositiveInfinity); if (f < SMALL) break; total += f; cont = true; } } return total; }
/// <summary> /// graphからk個の頂点をランダムに選択し,選ばれた頂点と,それらが誘導する辺のみを持つグラフを返す /// </summary> public DirectedGraph Sample(DirectedGraph graph, int sampledVertexCount, Random random) { var choice = new HashSet<int>(); for (int i = 0; i < sampledVertexCount; ++i) { for (; ; ) { int n = random.Next(graph.Vertices.Count); if (!choice.Contains(n)) { Trace.WriteLine("Choose: " + graph.Vertices[n].OriginalId.ToString()); choice.Add(n); break; } } } return graph.Induce(choice); }
public DirectedGraph Sample(DirectedGraph graph, int sampledVertexCount, double burningProbability, Random random) { var result = new DirectedGraph(); var index2index = new Dictionary<int, int>(); var visit = new bool[graph.Vertices.Count]; while (result.Vertices.Count < sampledVertexCount) { // 開始点を決める int start; do { start = random.Next(graph.Vertices.Count); } while (visit[start]); var stack = new Stack<int>(); stack.Push(start); AddVertexToResultGraph(start, graph, result, index2index); visit[start] = true; while (stack.Count > 0 && result.Vertices.Count < sampledVertexCount) { int v = stack.Pop(); var outEdges = graph.OutEdges[v]; for (int i = 0; i < outEdges.Count; ++i) { if (random.NextDouble() >= burningProbability) { break; } // i番目以降の辺を一つ選んでi番目と入れ替える int j = random.Next(i, outEdges.Count); Edge t = outEdges[i]; outEdges[i] = outEdges[j]; outEdges[j] = t; // 結果に追加 int dst = outEdges[i].Dst; if (!visit[dst]) { AddVertexToResultGraph(dst, graph, result, index2index); } result.AddEdge(new Edge(index2index[v], index2index[dst])); // 再帰的に探索 if (!visit[dst]) { visit[dst] = true; stack.Push(dst); } } } } return result; }
static void Main(string[] args) { DirectedGraph graph = new DirectedGraph(); for (int i = 0; i < 6; ++i) graph.AddVertex(new Vertex()); double[] cap = new double[8]; int m = 0; graph.AddEdge(new Edge(0, 1)); cap[m++] = 1; graph.AddEdge(new Edge(0, 2)); cap[m++] = 1; graph.AddEdge(new Edge(1, 2)); cap[m++] = 1; graph.AddEdge(new Edge(1, 3)); cap[m++] = 1; graph.AddEdge(new Edge(2, 4)); cap[m++] = 0.8; graph.AddEdge(new Edge(3, 5)); cap[m++] = 1; graph.AddEdge(new Edge(4, 3)); cap[m++] = 1; graph.AddEdge(new Edge(4, 5)); cap[m++] = 0.5; double[] flow; double value = Dinic.MaximumFlow(graph, cap, 0, 5, out flow); Console.WriteLine("|f| = {0}", value); foreach (var e in graph.Edges) { Console.WriteLine("f({0}, {1}) = {2}", e.Src, e.Dst, flow[e.Index]); } }
/// <summary> /// GMFを計算して結果を出力する /// </summary> /// <param name="input"></param> /// <param name="output"></param> /// <param name="graphLoadTime"></param> /// <param name="graph"></param> /// <param name="cap"></param> /// <param name="gain"></param> /// <param name="s"></param> /// <param name="t"></param> /// <param name="eps"></param> public static void Run(string input, string output, long graphLoadTime, DirectedGraph graph, double[] cap, double[] gain, int s, int t, double eps, string method = "FleischerWayne") { var stopwatch = new Stopwatch(); stopwatch.Start(); double[] flow = null; double value; if (method == "FleischerWayne") { value = FleischerWayne.GeneralizedMaximumFlow(graph, cap, gain, s, t, eps, out flow); } else if (method == "Greedy") { value = GreedyGMF.GeneralizedMaximumFlow(graph, cap, gain, s, t, ref flow); } else if (method == "GreedyImproved") { GreedyGMF.ConstructInitialFlow(graph, cap, gain, s, t, out flow); value = GreedyGMF.GeneralizedMaximumFlow(graph, cap, gain, s, t, ref flow); } else { throw new ArgumentException("method " + method + " は定義されていません."); } var gmfTime = stopwatch.ElapsedMilliseconds; stopwatch.Stop(); Trace.WriteLine("Writing the results"); Utility.CreateDirectoryIfNotExists(output); using (var writer1 = new StreamWriter(output + "/Flow.csv")) { OutputFlow(writer1, graph, cap, gain, flow); } using (var writer2 = new StreamWriter(output + "/Value.txt")) { writer2.WriteLine(value); } using (var writer3 = new StreamWriter(output + "/Summary.txt")) { writer3.WriteLine("Input: " + input); writer3.WriteLine("Vertex Count: " + graph.Vertices.Count); writer3.WriteLine("Edge Count: " + graph.Edges.Count); writer3.WriteLine("Source: " + graph.Vertices[s].OriginalId); writer3.WriteLine("Sink: " + graph.Vertices[t].OriginalId); writer3.WriteLine("Method: " + method); writer3.WriteLine("Eps: " + eps); writer3.WriteLine("Graph Load Time [ms]: " + graphLoadTime); writer3.WriteLine("GMF Time [ms]: " + gmfTime); writer3.WriteLine("Value: " + value); } }
/// <summary> /// グラフを mapping に従って縮約したものを返す. /// </summary> /// <param name="graph">縮約するグラフ</param> /// <param name="mapping">mapping[i] は 縮約元のグラフのi番目の頂点の縮約先の頂点</param> /// <returns>縮約されたグラフ</returns> public static DirectedGraph Contract(DirectedGraph graph, int[] mapping) { var result = new DirectedGraph(); int nVertices = mapping.Max() + 1; for (int i = 0; i < nVertices; ++i) { result.AddVertex(new Vertex()); } for (int i = 0; i < mapping.Length; ++i) { result.Vertices[mapping[i]] = graph.Vertices[i]; // copy vertex } for (int u = 0; u < graph.Vertices.Count; ++u) { int src = mapping[u]; foreach (Edge e in graph.OutEdges[u]) { int dst = mapping[e.Dst]; if (src != dst) { result.AddEdge(new Edge(src, dst)); } } } return result; }
/// <summary> /// Random Walk Sampling という手法を用いてサンプリングを行う /// </summary> /// <param name="graph"></param> /// <param name="sampledVertexCount"></param> /// <param name="random"></param> /// <returns></returns> public DirectedGraph Sample(DirectedGraph graph, int sampledVertexCount, Random random) { int n = graph.Vertices.Count; int m = graph.Edges.Count; var result = new DirectedGraph(); var visit = new bool[n]; var used = new bool[m]; var index2index = new Dictionary<int, int>(); while (result.Vertices.Count < sampledVertexCount) { int start = random.Next(graph.Vertices.Count); if (!visit[start]) { AddVertexToResultGraph(start, graph, result, index2index); visit[start] = true; } int v = start; for (int step = 0; step < n && result.Vertices.Count < sampledVertexCount; ++step) { if (graph.OutDegree(v) == 0 || random.NextDouble() < 0.15) { v = start; } else { int choice = random.Next(graph.OutDegree(v)); Edge e = graph.OutEdges[v][choice]; v = e.Dst; if (!visit[v]) { AddVertexToResultGraph(v, graph, result, index2index); visit[v] = true; } if (!used[e.Index]) { result.AddEdge(new Edge(index2index[e.Src], index2index[e.Dst])); used[e.Index] = true; } } } } return result; }
static void Main(string[] args) { UndirectedGraph <int> undirected = new UndirectedGraph <int>(); for (int i = 0; i < 7; i++) { undirected.AddVertex(new Vertex <int>(i)); } undirected.AddEdge(undirected[0], undirected[1]); undirected.AddEdge(undirected[1], undirected[2]); undirected.AddEdge(undirected[1], undirected[3]); undirected.AddEdge(undirected[2], undirected[3]); undirected.AddEdge(undirected[2], undirected[4]); undirected.AddEdge(undirected[4], undirected[6]); undirected.AddEdge(undirected[6], undirected[5]); Console.WriteLine("Undirected Depth-First Search:"); undirected.DepthFirstSearch(undirected[0]); Console.WriteLine("\nUndirected Breadth-First Traversal:"); undirected.BreadthFirstTraversal(undirected[0]); DirectedGraph <int> directed = new DirectedGraph <int>(); directed.AddVertex(new DVertex <int>(0)); //0 directed.AddVertex(new DVertex <int>(8)); //1 directed.AddVertex(new DVertex <int>(5)); //2 directed.AddVertex(new DVertex <int>(9)); //3 directed.AddVertex(new DVertex <int>(7)); //4 directed.AddEdge(directed[0], directed[1], 10); //0->8 directed.AddEdge(directed[0], directed[2], 5); //0->5 directed.AddEdge(directed[1], directed[2], 2); //8->5 directed.AddEdge(directed[1], directed[3], 1); //8->9 directed.AddEdge(directed[2], directed[1], 3); //5->8 directed.AddEdge(directed[2], directed[3], 9); //5->9 directed.AddEdge(directed[2], directed[4], 2); //5->7 directed.AddEdge(directed[3], directed[4], 4); //9->7 directed.AddEdge(directed[4], directed[3], 6); //7->9 directed.AddEdge(directed[4], directed[0], 7); //7->0 Console.WriteLine("\nDirected Depth-First Search:"); directed.DepthFirstSearch(directed[0]); Console.WriteLine("\nDirected Breadth-First Traversal:"); directed.BreadthFirstTraversal(directed[0]); PriorityQueue <int> queue = new PriorityQueue <int>(Comparer <int> .Create((a, b) => a.CompareTo(b))); for (int i = 10; i > 0; i--) { queue.Enqueue(i); Console.WriteLine(queue.ToString()); } Console.WriteLine(); for (int i = 0; i < 10; i++) { queue.Dequeue(); Console.WriteLine(queue.ToString()); } var vertices = (Stack <DVertex <int> >)directed.Dijkstra(directed[0], directed[4]); int count = vertices.Count; for (int i = 0; i < count; i++) { Console.Write($"{vertices.Pop().Value}{(i == count - 1 ? "\n" : "->") }"); } DirectedGraph <string> houseThing = new DirectedGraph <string>(); //https://brilliant.org/wiki/dijkstras-short-path-finder/ houseThing.AddVertex(new DVertex <string>("Home")); //0 houseThing.AddVertex(new DVertex <string>("A")); //1 houseThing.AddVertex(new DVertex <string>("B")); //2 houseThing.AddVertex(new DVertex <string>("C")); //3 houseThing.AddVertex(new DVertex <string>("D")); //4 houseThing.AddVertex(new DVertex <string>("E")); //5 houseThing.AddVertex(new DVertex <string>("F")); //6 houseThing.AddVertex(new DVertex <string>("School")); //7 houseThing.AddEdge(houseThing[0], houseThing[1], 3); houseThing.AddEdge(houseThing[0], houseThing[2], 2); houseThing.AddEdge(houseThing[0], houseThing[3], 5); houseThing.AddEdge(houseThing[1], houseThing[4], 3); houseThing.AddEdge(houseThing[2], houseThing[4], 1); houseThing.AddEdge(houseThing[2], houseThing[5], 6); houseThing.AddEdge(houseThing[3], houseThing[5], 2); houseThing.AddEdge(houseThing[4], houseThing[6], 4); houseThing.AddEdge(houseThing[5], houseThing[6], 1); houseThing.AddEdge(houseThing[5], houseThing[7], 4); houseThing.AddEdge(houseThing[6], houseThing[7], 2); var housePath = (Stack <DVertex <string> >)houseThing.Dijkstra(houseThing[0], houseThing[7]); count = housePath.Count; for (int i = 0; i < count; i++) { Console.Write($"{housePath.Pop().Value}{(i == count - 1 ? "\n" : "->") }"); } Console.ReadKey(); }
public Compressor(DirectedGraph graph, double prob, Random random) { int[] mapping; CompressedGraph = Compress(graph, prob, random, out mapping); Mapping = mapping; }
private DirectedGraph Compress(DirectedGraph graph, double prob, Random random, out int[] mapping) { bool[] selected = RandomSample(graph, prob, random); Graph.Algorithms.WeaklyConnectedComponents(graph, out mapping, selected); return Graph.Algorithms.Contract(graph, mapping); }
private void AddVertexToResultGraph(int v, DirectedGraph graph, DirectedGraph result, Dictionary<int, int> index2index) { index2index.Add(v, result.Vertices.Count); result.AddVertex(graph.Vertices[v]); }
public void CopyTo(DirectedGraph other) { for (int i = 0; i < other.Vertices.Count; ++i) { other.InEdges[i].Clear(); other.OutEdges[i].Clear(); } other.Vertices.Clear(); other.Edges.Clear(); other.Vertices.AddRange(this.Vertices); other.Edges.AddRange(this.Edges); for (int i = 0; i < Vertices.Count; ++i) { other.InEdges[i].AddRange(this.InEdges[i]); other.OutEdges[i].AddRange(this.OutEdges[i]); } }
/// <summary> /// 指定された頂点集合から誘導される部分グラフを返す /// </summary> /// <param name="vertices"></param> /// <returns></returns> public DirectedGraph Induce(IEnumerable<int> vertices) { var g = new DirectedGraph(); var index2index = new Dictionary<int, int>(); foreach (int v in vertices) { index2index.Add(v, g.Vertices.Count); g.AddVertex(Vertices[v]); } foreach (Edge e in Edges) { if (index2index.ContainsKey(e.Src) && index2index.ContainsKey(e.Dst)) { g.AddEdge(new Edge(index2index[e.Src], index2index[e.Dst])); } } return g; }