30
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rust でグラフ(petgraph)

Last updated at Posted at 2020-05-10

AtCoderのRust実行環境で使えるグラフライブラリ petgraph 0.5.0 についての覚書

1. グラフの作り方

最も簡潔な方法は from_edges というエッジ一覧から作る方法:

// 三すくみの無向グラフ
let g = UnGraph::<(),()>::from_edges(&[(0,1), (1,2), (2,0)]);
// エッジに整数値の重みがある場合
let g = UnGraph::<(),i32>::from_edges(&[(0,1,100), (1,2,50), (2,0,200)]);
// 例: 全重みの合計
assert_eq!(350, g.edge_indices().map(|ei| g[ei]).sum::<i32>());

普通の方法は extend_with_edges で、ノードを作ってからエッジを作る。ノードには文字列を エッジには整数値を重みとしてもつ有向グラフを作る例:

let mut g = DiGraph::<&str,i32>::new();
let a = g.add_node("a");
let b = g.add_node("b");
let c = g.add_node("c");
g.extend_with_edges(&[(a,b,100), (b,c,50), (c,a,200)]);
assert_eq!(350,  g.edge_indices().map(|ei| g[ei]).sum::<i32>());

2. グラフの巡り方

petgraph::visit にあるように、DFS, 帰りがけDFS, BFS, トポロジカル順など、様々な順で巡るイテレータが用意されている。しかし多くの場合単なるイテレータでは機能不足で、枝刈りや大域脱出が必要になる。DFSの場合は depth_first_search がそれを反映した汎用的な枠組みだが、他の巡り方に対しては、同様のAPIは存在しない。自分で書くときはGraphが実装しているメソッド(例えば隣接ノードを求めるneighbors()など)を使って記述するか、次章で紹介する汎用アルゴリズムを使う。

Dfsビジタを使って全てのノードの重み(i32整数値)をインクリメントする例:

let mut dfs = Dfs::new(&graph, a);
while let Some(nx) = dfs.next(&graph) {
   graph[nx] += 1;
}

depth_first_search を使って DFSの最中で 打ち切る(Break)/枝刈りをする(Prune)という典型的な制御フローを組み込んでパスを求める例が公式のExampleに記載がある。抜粋:

depth_first_search(&gr, Some(start), |event| {
    if let DfsEvent::TreeEdge(u, v) = event {
        predecessor[v.index()] = u;
        if v == goal {
            return Control::Break(v);
        }
    }
    Control::Continue
});

3. 有名なアルゴリズム

有名なアルゴリズム が実装されているので、頻出の問題には関数を呼び出すだけでよい。パス全探索・最短経路・ダイクストラ法・スパニングツリー・強連結分解等々。UnionFind もあるが、Graphのアルゴリズムの場所とは別の箇所で提供されている。

4. スニペット

use petgraph::{Undirected, Directed};
use petgraph::graph::Graph;

fn main() {
    // エッジ一覧から有向グラフをつくる例(少し煩雑だが汎用的)
    let data = [(0,1),(1,2),(2,3),(3,1)];
    let mut g = Graph::<(),()>::new(); // デフォルトでは有向グラフ
    let nodes: Vec<_> = (0..6).map(|_| g.add_node(())).collect(); // ノードの数は6個としよう
    g.extend_with_edges(data.iter().map(|&x| (nodes[x.0], nodes[x.1])));
    assert_eq!((6, 4), (g.node_count(), g.edge_count()));
    // エッジ一覧から有向グラフをつくる例(簡潔だが注意が必要)
    let g = Graph::<(), (), Directed, usize>::from_edges(&data);
    assert_eq!((4, 4), (g.node_count(), g.edge_count())); // エッジ一覧にないノードは存在しない
    
    // エッジ一覧から無向グラフをつくる例(少し煩雑だが汎用的)
    let data = [(0,1),(1,2),(2,3),(3,1)];
    let mut g = Graph::<(),(),Undirected>::new_undirected();
    let nodes: Vec<_> = (0..6).map(|_| g.add_node(())).collect(); // ノードの数は6個としよう
    g.extend_with_edges(data.iter().map(|&(i,j)| (nodes[i], nodes[j])));
    assert_eq!((6, 4), (g.node_count(), g.edge_count()));
    // エッジ一覧から無向グラフをつくる(簡潔だが注意が必要)例
    let g = Graph::<(), (), Undirected, usize>::from_edges(&data);
    assert_eq!((4, 4), (g.node_count(), g.edge_count())); // エッジ一覧にないノードは存在しない

    // 重みのある有向グラフ
    let data = [(0,1,10),(1,2,40),(2,3,100),(3,1,300)];
    let mut g = Graph::<usize,i32>::new();
    let nodes: Vec<_> = (0..6).map(|i| g.add_node(i)).collect(); // ノードの重みは添え字にしてみる
    g.extend_with_edges(data.iter().map(|&(i,j,k)| (nodes[i], nodes[j], k)));
    assert_eq!(450, g.edge_indices().map(|ei| g[ei]).sum::<i32>());
    assert_eq!((6, 4), (g.node_count(), g.edge_count()));
    // 簡便な方法
    let g = Graph::<usize, i32, Directed, usize>::from_edges(&data);
    assert_eq!((4, 4), (g.node_count(), g.edge_count())); // エッジ一覧にないノードは存在しない
    assert_eq!(450, g.edge_indices().map(|ei| g[ei]).sum::<i32>());
    assert!(g.node_indices().all(|ni| g[ni] == 0)); // ノードの重みには型のデフォルト値がはいる
    
    // 重みのある無向グラフ
    let data = [(0,1,10),(1,2,40),(2,3,100),(3,1,300)];
    let mut g = Graph::<usize, i32, Undirected>::new_undirected();
    let nodes: Vec<_> = (0..6).map(|i| g.add_node(i)).collect(); // ノードの重みは添え字にしてみる
    g.extend_with_edges(data.iter().map(|&(i,j,k)| (nodes[i], nodes[j], k)));
    assert_eq!(450, g.edge_indices().map(|ei| g[ei]).sum::<i32>());
    assert_eq!((6, 4), (g.node_count(), g.edge_count()));
    // 簡便な方法
    let g = Graph::<usize, i32, Undirected, usize>::from_edges(&data);
    assert_eq!(450, g.edge_indices().map(|ei| g[ei]).sum::<i32>());
    assert_eq!((4, 4), (g.node_count(), g.edge_count())); // エッジ一覧にないノードは存在しない
}

最後に

コメント歓迎です

30
19
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
30
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?