示例#1
0
文件: Algorithms.cs 项目: nojima/GMF
 /// <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;
 }
示例#2
0
文件: Dinic.cs 项目: nojima/GMF
 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;
 }
示例#3
0
文件: GraphIO.cs 项目: nojima/GMF
        // グラフを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;
            }
        }
示例#4
0
文件: GraphIO.cs 项目: nojima/GMF
 // グラフを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);
         }
     }
 }
示例#5
0
 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]);
     }
 }
示例#6
0
        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);
            }
        }
示例#7
0
文件: Compressor.cs 项目: nojima/GMF
 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;
 }
示例#8
0
文件: Utility.cs 项目: nojima/GMF
        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);
            }
        }
示例#9
0
文件: Dinic.cs 项目: nojima/GMF
        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;
        }
示例#10
0
        /// <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);
        }
示例#11
0
        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;
        }
示例#12
0
文件: Program.cs 项目: nojima/GMF
        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]);
            }
        }
示例#13
0
文件: GMF.cs 项目: nojima/GMF
        /// <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);
            }
        }
示例#14
0
文件: Algorithms.cs 项目: nojima/GMF
 /// <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;
 }
示例#15
0
        /// <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;
        }
示例#16
0
        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();
        }
示例#17
0
文件: Compressor.cs 项目: nojima/GMF
 public Compressor(DirectedGraph graph, double prob, Random random)
 {
     int[] mapping;
     CompressedGraph = Compress(graph, prob, random, out mapping);
     Mapping = mapping;
 }
示例#18
0
文件: Compressor.cs 项目: nojima/GMF
 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);
 }
示例#19
0
 private void AddVertexToResultGraph(int v, DirectedGraph graph, DirectedGraph result, Dictionary<int, int> index2index)
 {
     index2index.Add(v, result.Vertices.Count);
     result.AddVertex(graph.Vertices[v]);
 }
示例#20
0
文件: Graph.cs 项目: nojima/GMF
 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]);
     }
 }
示例#21
0
文件: Graph.cs 项目: nojima/GMF
 /// <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;
 }