LoginSignup
1
4

More than 5 years have passed since last update.

Longest path / Critical pathを求める

Posted at

はじめに

Longest path(最長経路)を求める方法を説明します。
Longest path は Project Management の Critical path(クリティカルパス)のことです。

考え方

Wikipeidaに記載されているとおり、

A longest path between two given vertices s and t in a weighted graph G is the same thing as a shortest path in a graph −G derived from G by changing every weight to its negation. Therefore, if shortest paths can be found in −G, then longest paths can also be found in G.[4]

負の重みを持ったweighted digraph の Shortest path(最短経路)を求めることによって
Longest path(最長経路)を求めます。

負の重みを持ったweighted digraph の Shortest path(最短経路)は
Bellman–Ford algorithmを使って求めます。

example

具体例で説明します。
A, B, C, D, Eの作業があります。作業はedgeで表現します。
点線はダミー作業です。ダミー作業は作業の前後関係を規定します。
graphvizを使った図を示します。

sample.dot
digraph g {
  node[shape=point];
  edge[arrowhead=vee];
  rankdir=LR;

  // define edge
  sa -> ea [label="A"];
  sb -> eb [label="B"];
  sc -> ec [label="C"];
  sd -> ed [label="D"];
  se -> ee [label="E"];

  // dummy edge s:start, f:finish
  s -> sa  [style=dashed]; // start -> A
  s -> sb  [style=dashed]; // start -> B
  ea -> sc [style=dashed]; // A -> C
  eb -> sc [style=dashed]; // B -> C
  eb -> sd [style=dashed]; // B -> D
  ec -> se [style=dashed]; // C -> E
  ed -> se [style=dashed]; // D -> E
  ee -> f  [style=dashed]; // E -> finish
}
console
dot -T png .\sample.dot -o sample.png ; .\sample.png

sample.png

QuickGraph

C# の Graphライブラリ QuickGraphを使って Longest path / Critical pathを求めます。
QuickGraph は Nugetを使ってInstallできます。
重みを負にしていることに注意してください。

sample.cs
using QuickGraph;
using QuickGraph.Algorithms.Observers;
using QuickGraph.Algorithms.ShortestPath;
using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    class Program
    {
        class Graph : AdjacencyGraph<string, Edge> { }
        class Edge : IEdge<string>
        {
            public Edge(string n, string s, string t, int w)
            {
                Name = n;
                Source = s;
                Target = t;
                Weight = w;
            }
            public string Name { get; set; }
            public string Source { get; set; }
            public string Target { get; set; }
            public int Weight { get; set; }
        }

        static void Main(string[] args)
        {
            Graph g = new Graph();

            // create start / finish vertex
            g.AddVertex("s"); // start
            g.AddVertex("f"); // finish

            // create edge / vertex
            g.AddVertex("sa");
            g.AddVertex("ea");
            g.AddEdge(new Edge("A", "sa", "ea", -6));
            g.AddVertex("sb");
            g.AddVertex("eb");
            g.AddEdge(new Edge("B", "sb", "eb", -5));
            g.AddVertex("sc");
            g.AddVertex("ec");
            g.AddEdge(new Edge("C", "sc", "ec", -8));
            g.AddVertex("sd");
            g.AddVertex("ed");
            g.AddEdge(new Edge("D", "sd", "ed", -2));
            g.AddVertex("se");
            g.AddVertex("ee");
            g.AddEdge(new Edge("E", "se", "ee", -10));

            // create dummy edge
            g.AddEdge(new Edge("d1", "s", "sa", 0)); // start -> A
            g.AddEdge(new Edge("d2", "s", "sb", 0)); // start -> B
            g.AddEdge(new Edge("d3", "ea", "sc", 0)); // A -> C
            g.AddEdge(new Edge("d4", "eb", "sc", 0)); // B -> C
            g.AddEdge(new Edge("d5", "eb", "sd", 0)); // B -> D
            g.AddEdge(new Edge("d6", "ec", "se", 0)); // C -> E
            g.AddEdge(new Edge("d7", "ed", "se", 0)); // D -> E
            g.AddEdge(new Edge("d8", "ee", "f", 0)); // E -> finish

            BellmanFordShortestPathAlgorithm<string, Edge> algo =
                new BellmanFordShortestPathAlgorithm<string, Edge>(g, e => e.Weight);
            VertexPredecessorRecorderObserver<string, Edge> pred =
                new VertexPredecessorRecorderObserver<string, Edge>();
            pred.Attach(algo);
            algo.Compute("s");
            IEnumerable<Edge> path;
            pred.TryGetPath("f", out path);
            int cost = 0;
            foreach (Edge e in path)
            {
                cost += e.Weight;
                Console.WriteLine("Name={0}, weight={1}, cost={2}", e.Name, e.Weight, cost);
            }
            Console.Read();
        }
    }
}

実行結果です。

console
Name=d1, weight=0, cost=0
Name=A, weight=-6, cost=-6
Name=d3, weight=0, cost=-6
Name=C, weight=-8, cost=-14
Name=d6, weight=0, cost=-14
Name=E, weight=-10, cost=-24
Name=d8, weight=0, cost=-24

Longest pathは start -> A -> C -> E -> finish です。
重みの合計は24です。

references

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4