패키지, 저장소 및 앱 카테고리 검색

directed_graph 표지
패키지 상세v1.1.0

directed_graph

일반적인 방향 그래프와 가중치가 있는 방향 그래프에 정점의 정렬 및 위상 정렬을 가능하게 하는 알고리즘이 포함되어 있습니다.

AndroidiOSWebmacOSWindowsLinux
30일 다운로드
2.4만
좋아요
70
Pub 점수
160

패키지 설치

flutter pub add directed_graph

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

일반적인 방향성 및 가중 방향 그래프를 제공하는 Dart 패키지로, 정점 정렬 및 위상 정렬을 위한 내장 알고리즘을 포함합니다. 효율적인 그래프 탐색과 종속성 해결을 가능하게 하여 계층적 또는 순차적 관계를 모델링하는 데 이상적입니다. 이 패키지는 Android, iOS, Web, macOS, Windows, Linux 등 여러 플랫폼을 지원합니다. 외부 종속성이 없이 강력한 그래프 구조가 필요한 개발자를 위해 설계되었습니다. 알고리즘은 일반적인 사용 사례에서 정확성과 성능을 최적화했습니다.

사용 사례

  • 의존성 해결
  • 작업 스케줄링
  • 데이터 흐름 분석
  • 네트워크 토폴로지 모델링
  • 코드 컴파일 순서
  • 그래프 시각화

주요 기능

  • 방향성 및 가중 그래프를 지원합니다
  • 위상 정렬 알고리즘
  • 정점 순서 도구
  • 크로스 플랫폼 호환성
  • 경량이며 종속성 없음
  • 제네릭 타입 지원

적합한 대상

  • 작업 스케줄러를 구축하는 개발자
  • 엔지니어가 종속성 관리 중
  • 그래프 이론에 관한 교육 프로젝트
  • 워크플로우 오케스트레이션을 필요로 하는 애플리케이션
  • 코드 분석 또는 빌드 시스템을 위한 도구

고려 사항

  • 매우 큰 그래프에 최적화되지 않음
  • 방향성 그래프 작업에 국한
  • 내장된 시각화 없음
  • 주기 감지에 대한 수동 처리가 필요합니다
  • 중간 규모의 데이터 세트에 가장 적합

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

Directed Graph

Dart

Introduction

An integral part of storing, manipulating, and retrieving numerical data are data structures or as they are called in Dart: collections. Arguably the most common data structure is the list. It enables efficient storage and retrieval of sequential data that can be associated with an index.

A more general (non-linear) data structure where an element may be connected to one, several, or none of the other elements is called a graph.

Graphs are useful when keeping track of elements that are linked to or are dependent on other elements. Examples include: network connections, links in a document pointing to other paragraphs or documents, foreign keys in a relational database, file dependencies in a build system, etc.

The package directed_graph contains an implementation of a Dart graph that follows the recommendations found in graphs-examples and is compatible with the algorithms provided by graphs. It includes methods that enable:

  • adding/removing vertices and edges,
  • sorting of vertices and edges.

The library provides access to algorithms for finding:

  • the shortest path between vertices,
  • the path with the lowest/highest weight (for weighted directed graphs),
  • all paths connecting two vertices,
  • the shortest paths from a vertex to all connected vertices,
  • cycles,
  • a topological ordering of the graph vertices,
  • a reverse topological ordering of the graph vertices.

The class GraphCrawler can be used to retrieve paths or walks connecting two vertices.

Terminology

Elements of a graph are called vertices (or nodes) and neighbouring vertices are connected by edges. The figure below shows a directed graph with unidirectional edges depicted as arrows. Graph edges are emanating from a vertex and ending at a vertex. In a weighted directed graph each edge is assigned a weight.

Directed Graph Image

  • In-degree of a vertex: Number of edges ending at this vertex. For example, vertex H has in-degree 3.
  • Out-degree of a vertex: Number of edges starting at this vertex. For example, vertex F has out-degree 1.
  • Source: A vertex with in-degree zero is called (local) source. Vertices A and D in the graph above are local sources.
  • Directed Edge: An ordered pair of connected vertices (vi, vj). For example, the edge (A, C) starts at vertex A and ends at vertex C.
  • Path: A path [vi, ..., vn] is an ordered list of at least two connected vertices where each inner vertex is distinct. The path [A, E, G] starts at vertex A and ends at vertex G.
  • Cycle: A cycle is an ordered list of connected vertices where each inner vertex is distinct and the first and last vertices are identical. The sequence [F, I, K, F] completes a cycle.
  • Walk: A walk is an ordered list of at least two connected vertices. [D, F, I, K, F] is a walk but not a path since the vertex F is listed twice.
  • DAG: An acronym for Directed Acyclic Graph, a directed graph without cycles.
  • Topological ordering: An ordered set of all vertices in a graph such that vi occurs before vj if there is a directed edge (vi, vj). A topological ordering of the graph above is: {A, D, B, C, E, K, F, G, H, I, L}. Hereby, dashed edges were disregarded since a cyclic graph does not have a topological ordering.
  • Quasi-topological ordering: An ordered sub-set of graph vertices such that vi occurs before vj if there is a directed edge (vi, vj). For example, the set { A, D, E, G } represents a valid quasi-topological ordering, even though the edges (I, K) and (L, L) render the total graph cyclic.
    • For a quasi-topological ordering to exist, any two vertices belonging to the sub-set must not be mutually connected. That is, if there is a path [vi, ..., vj] then there must not be a path [vj, ..., vi] and vice versa. Note:
    • The paths [vi, ..., vj] and [vj, ..., vi] may include vertices that are not in the sub-set, but belong to the graph.
    • A quasi-topological ordering many exist even if the total graph is cyclic.

Note: In the context of this package, the definition of edge might be more lax compared to a rigorous mathematical definition. For example, self-loops, that is edges connecting a vertex to itself are explicitly allowed.

Usage

To use this library include directed_graph as a dependency in your pubspec.yaml file. The example below shows how to construct an object of type DirectedGraph.

The graph classes provided by this library are generic with type argument T extends Object (that is T must be non-nullable). The graphs extend Iterable making it possible to iterate over the graph vertices.

Graph vertices can be sorted if T is Comparable or if a custom comparator function is provided. Note: If T is Comparable and no comparator is provided, then the following default comparator is automatically provided:

(T left, T right) =>  (left as Comparable).compareTo(right);

In the example below, a custom comparator is used to sort vertices in lexicographical order.

import 'package:directed_graph/directed_graph.dart';
void main() {
  int comparator(String s1, String s2) => s1.compareTo(s2);
  int inverseComparator(String s1, String s2) => -comparator(s1, s2);

  // Constructing a graph from vertices.

  final graph = DirectedGraph<String>({
    'a': {'b', 'h', 'c', 'e'},
    'b': {'h'},
    'c': {'h', 'g'},
    'd': {'e', 'f'},
    'e': {'g'},
    'f': {'i'},
    //g': {'a'},
    'i': {'l'},
    'k': {'g', 'f'},
  }, comparator: comparator);

  print('Example Directed Graph...');
  print('graph.toString():');
  print(graph);

  print('\nIs Acylic:');
  print(graph.isAcyclic);

  print('\nStrongly connected components:');
  print(graph.stronglyConnectedComponents());

  print('\nLocal sources:');
  print(graph.localSources());

  print('\nshortestPath(d, l):');
  print(graph.shortestPath('d', 'l'));

  print('\nshortestPaths(a)');
  print(graph.shortestPaths('a'));

  print('\nInDegree(C):');
  print(graph.inDegree('c'));

  print('\nOutDegree(C)');
  print(graph.outDegree('c'));

  print('\nVertices sorted in lexicographical order:');
  print(graph.sortedVertices);

  print('\nVertices sorted in inverse lexicographical order:');
  graph.comparator = inverseComparator;
  print(graph.sortedVertices);
  graph.comparator = comparator;

  print('\nInDegreeMap:');
  print(graph.inDegreeMap);

  print('\nSorted Topological Ordering:');
  print(graph.topologicalOrdering(sorted: true));

  print('\nTopological Ordering:');
  print(graph.topologicalOrdering());

  print('\nReverse Topological Ordering:');
  print(graph.reverseTopologicalOrdering());

  print('\nReverse Topological Ordering, sorted: true');
  print(graph.reverseTopologicalOrdering(sorted: true));

  print('\nLocal Sources:');
  print(graph.localSources());

  print('\nAdding edges: i -> k and i -> d');

  // Add edge to render the graph cyclic
  graph.addEdge('i', 'k');
  //graph.addEdge('l', 'l');
  graph.addEdge('i', 'd');

  print('\nCyclic graph:');
  print(graph);

  print('\nCycle:');
  print(graph.cycle());

  print('\nCycle vertex:');
  print(graph.cycleVertex);

  print('\ngraph.isAcyclic: ');
  print(graph.isAcyclic);

  print('\nShortest Paths:');
  print(graph.shortestPaths('a'));

  print('\nEdge exists: a->b');
  print(graph.edgeExists('a', 'b'));

  print('\nStrongly connected components:');
  print(graph.stronglyConnectedComponents());

  print('\nStrongly connected components, sorted:');
  print(
    graph.stronglyConnectedComponents(sorted: true, comparator: comparator),
  );

  print('\nStrongly connected components, sorted, inverse:');
  print(
    graph.stronglyConnectedComponents(
      sorted: true,
      comparator: inverseComparator,
    ),
  );

  print('\nQuasi-Topological Ordering:');
  print(graph.quasiTopologicalOrdering({'d', 'e', 'a', 'g'}));

  print('\nQuasi-Topological Ordering, sorted:');
  print(graph.quasiTopologicalOrdering({'d', 'e', 'a', 'g'}, sorted: true));

  print('\nReverse-Quasi-Topological Ordering, sorted:');
  print(graph.reverseQuasiTopologicalOrdering({'d', 'e', 'a', 'g'}, sorted: true));
}

Click to show the console output.
$ dart example/bin/directed_graph_example.dart
Example Directed Graph...
graph.toString():
{
 'a': {'b', 'h', 'c', 'e'},
 'b': {'h'},
 'h': {},
 'c': {'h', 'g'},
 'e': {'g'},
 'g': {},
 'd': {'e', 'f'},
 'f': {'i'},
 'i': {'l'},
 'l': {},
 'k': {'g', 'f'},
}

Is Acylic:
true

Strongly connected components:
[{h}, {b}, {g}, {c}, {e}, {a}, {l}, {i}, {f}, {d}, {k}]

Local sources:
[{a, d, k}, {b, c, e, f}, {g, h, i}, {l}]

shortestPath(d, l):
[d, f, i, l]

shortestPaths(a)
{b: [b], h: [h], c: [c], e: [e], g: [c, g]}

InDegree(C):
1

OutDegree(C)
2

Vertices sorted in lexicographical order:
{a, b, c, d, e, f, g, h, i, k, l}

Vertices sorted in inverse lexicographical order:
{l, k, i, h, g, f, e, d, c, b, a}

InDegreeMap:
{a: 0, b: 1, h: 3, c: 1, e: 2, g: 3, d: 0, f: 2, i: 1, l: 1, k: 0}

Sorted Topological Ordering:
{a, b, c, d, e, h, k, f, g, i, l}

Topological Ordering:
{a, b, c, d, e, h, k, f, i, g, l}

Reverse Topological Ordering:
{l, g, i, f, k, h, e, d, c, b, a}

Reverse Topological Ordering, sorted: true
{h, b, g, c, e, a, l, i, f, d, k}

Local Sources:
[{a, d, k}, {b, c, e, f}, {g, h, i}, {l}]

Adding edges: i -> k and i -> d

Cyclic graph:
{
 'a': {'b', 'h', 'c', 'e'},
 'b': {'h'},
 'h': {},
 'c': {'h', 'g'},
 'e': {'g'},
 'g': {},
 'd': {'e', 'f'},
 'f': {'i'},
 'i': {'l', 'k', 'd'},
 'l': {},
 'k': {'g', 'f'},
}

Cycle:
[f, i, k, f]

Cycle vertex:
f

graph.isAcyclic:
false

Shortest Paths:
{b: [b], h: [h], c: [c], e: [e], g: [c, g]}

Edge exists: a->b
true

Strongly connected components:
[{h}, {b}, {g}, {c}, {e}, {a}, {l}, {k, i, f, d}]

Strongly connected components, sorted:
[{h}, {b}, {g}, {c}, {e}, {a}, {l}, {d, f, i, k}]

Strongly connected components, sorted, inverse:
[{l}, {g}, {e}, {k, i, f, d}, {h}, {c}, {b}, {a}]

Quasi-Topological Ordering:
{d, a, e, g}

Quasi-Topological Ordering, sorted:
{a, d, e, g}

Reverse-Quasi-Topological Ordering, sorted:
{g, e, a, d}

Weighted Directed Graphs

The example below shows how to construct an object of type WeightedDirectedGraph. Initial graph edges are specified in the form of map of type Map<T, Map<T, W>>. The vertex type T extends Object and therefore must be a non-nullable. The type associated with the edge weight W extends Comparable to enable sorting of vertices by their edge weight.

The constructor takes an optional comparator function as parameter. Vertices may be sorted if a comparator function is provided or if T implements Comparator.

import 'package:directed_graph/directed_graph.dart';

void main(List<String> args) {
  int comparator(String s1, String s2) {
    return s1.compareTo(s2);
  }

  final a = 'a';
  final b = 'b';
  final c = 'c';
  final d = 'd';
  final e = 'e';
  final f = 'f';
  final g = 'g';
  final h = 'h';
  final i = 'i';
  final k = 'k';
  final l = 'l';

  int sum(int left, int right) => left + right;

  var graph = WeightedDirectedGraph<String, int>(
    {
      a: {b: 1, h: 7, c: 2, e: 40, g: 7},
      b: {h: 6},
      c: {h: 5, g: 4},
      d: {e: 1, f: 2},
      e: {g: 2},
      f: {i: 3},
      i: {l: 3, k: 2},
      k: {g: 4, f: 5},
      l: {l: 0},
    },
    summation: sum,
    zero: 0,
    comparator: comparator,
  );

  print('Weighted Graph:');
  print(graph);

  print('\nNeighbouring vertices sorted by weight:');
  print(graph..sortEdgesByWeight());

  final lightestPath = graph.lightestPath(a, g);
  print('\nLightest path a -> g');
  print('$lightestPath weight: ${graph.weightAlong(lightestPath)}');

  final heaviestPath = graph.heaviestPath(a, g);
  print('\nHeaviest path a -> g');
  print('$heaviestPath weigth: ${graph.weightAlong(heaviestPath)}');

  final shortestPath = graph.shortestPath(a, g);
  print('\nShortest path a -> g');
  print('$shortestPath weight: ${graph.weightAlong(shortestPath)}');

  print('\nTransitive Closure');
  print(WeightedDirectedGraph.transitiveClosure(graph));

  print('\nTransitive Weighted Edges:');
  print(graph.transitiveWeightedEdges);

  print('\nVertices reachable from d:');
  print(graph.reachableVertices(d));

  print('\nUpdate weight of edge (a,b) with value 101:');
  graph.updateEdgeWeight(vertex: a, connectedVertex: b, weight: 101);
  print('graph.weightedEdges(a): ${graph.weightedEdges(a)}');
}
Click to show the console output.
$ dart example/bin/weighted_graph_example.dart
Weighted Graph:
{
 'a': {'b': 1, 'h': 7, 'c': 2, 'e': 40, 'g': 7},
 'b': {'h': 6},
 'c': {'h': 5, 'g': 4},
 'd': {'e': 1, 'f': 2},
 'e': {'g': 2},
 'f': {'i': 3},
 'g': {},
 'h': {},
 'i': {'l': 3, 'k': 2},
 'k': {'g': 4, 'f': 5},
 'l': {'l': 0},
}

Neighbouring vertices sorted by weight:
{
 'a': {'b': 1, 'c': 2, 'h': 7, 'g': 7, 'e': 40},
 'b': {'h': 6},
 'c': {'g': 4, 'h': 5},
 'd': {'e': 1, 'f': 2},
 'e': {'g': 2},
 'f': {'i': 3},
 'g': {},
 'h': {},
 'i': {'k': 2, 'l': 3},
 'k': {'g': 4, 'f': 5},
 'l': {'l': 0},
}

Lightest path a -> g
[a, c, g] weight: 6

Heaviest path a -> g
[a, e, g] weigth: 42

Shortest path a -> g
[a, g] weight: 7

Transitive Closure
{
 'a': {'b': 1, 'c': 2, 'h': 7, 'g': 6, 'e': 40},
 'b': {'h': 6},
 'c': {'g': 4, 'h': 5},
 'd': {'e': 1, 'f': 2, 'g': 3, 'i': 5, 'k': 7, 'l': 8},
 'e': {'g': 2},
 'f': {'i': 3, 'k': 5, 'l': 6, 'g': 9, 'f': 10},
 'g': {},
 'h': {},
 'i': {'k': 2, 'l': 3, 'g': 6, 'f': 7, 'i': 10},
 'k': {'g': 4, 'f': 5, 'i': 8, 'k': 10, 'l': 11},
 'l': {'l': 0},
}

Transitive Weighted Edges:
{a: {b: 1, c: 2, h: 7, g: 6, e: 40}, b: {h: 6}, c: {g: 4, h: 5}, d: {e: 1, f: 2, g: 3, i: 5, k: 7, l: 8}, e: {g: 2}, f: {i: 3, k: 5, l: 6, g: 9, f: 10}, g: {}, h: {}, i: {k: 2, l: 3, g: 6, f: 7, i: 10}, k: {g: 4, f: 5, i: 8, k: 10, l: 11}, l: {l: 0}}

Vertices reachable from d:
{e, g, f, i, k, l}

Update weight of edge (a,b) with value 101:
graph.weightedEdges(a): {b: 101, c: 2, h: 7, g: 7, e: 40}

Examples

For further information on how to generate a topological sorting of vertices see example.

Features and bugs

Please file feature requests and bugs at the issue tracker.

카테고리

의존성

의존성 목록 5 개

  • collection^1.19.1
  • exception_templates^1.1.0
  • lazy_memo^2.0.0
  • meta^1.18.2
  • quote_buffer^1.1.0

소스 저장소

이 패키지를 사용하는 프로젝트