LoginSignup
0
0

More than 3 years have passed since last update.

分岐・端点間のパスを列挙(グラフ問題)

Last updated at Posted at 2019-06-04

こんにちは。
分岐・端点間のパスを列挙するグラフ問題を解いてみました1 2gonum/graph を利用しました。

  1. まず、分岐・端点(degree != 2 の vertex)を列挙3(図例内で緑色)
  2. 次に、それら同士を繋ぐパスを列挙(図例内で vertex 7 と 9 との間を繋ぐパスが、line 8 + 9 というわけです)

test.jpg

$ go run connectingPaths.go
len(nodes, paths) = (7, 8)
0: nodes: (0 1)  lines: {0}
1: nodes: (6 5 2)  lines: {7 6}
2: nodes: (7 8 9)  lines: {8 9}
3: nodes: (1 2)  lines: {1}
4: nodes: (1 2)  lines: {2}
5: nodes: (1 3 4 2)  lines: {3 4 5}
6: nodes: (9 10)  lines: {10}
7: nodes: (9 9)  lines: {11}
connectingPaths.go
package main

import (
     "fmt"
    "gonum.org/v1/gonum/graph"
    "gonum.org/v1/gonum/graph/multi"
)

type Int64s map[int64]struct{}

func (set Int64s) Add(e int64) {
    set[e] = struct{}{}
}

func (set Int64s) Has(e int64) bool {
    _, ok := set[e]
    return ok
}

type myLine struct {
    line graph.Line
    direction bool
}

func Degree(g *multi.UndirectedGraph, nid int64) (deg int) {
    m := g.From(nid)
    fac := map[bool]int{true: 1, false: 2}  // {ordinary, self-loop}
    for m.Next() {
        mid := m.Node().ID()
        deg += g.Lines(nid, mid).Len() * fac[nid != mid]
    }
    return deg
}

func connectingPaths(g *multi.UndirectedGraph) (nodes Int64s, paths [][]myLine) {
// nodes with their degree not being 2
    nodes = make(Int64s)
    n := g.Nodes()
    for n.Next() {
        nid := n.Node().ID()
        if Degree(g, nid) != 2 {nodes.Add(nid)}
    }

// paths among the nodes
    linesVisited := make(Int64s)
    for uid, _ := range nodes {
        v := g.From(uid)
        for v.Next() {
            vid := v.Node().ID()
            luv := g.Lines(uid, vid)
            for luv.Next() {
                line := luv.Line()
                if linesVisited.Has(line.ID()) {continue}
                linesVisited.Add(line.ID())
                path := []myLine{myLine{line, uid==line.From().ID()}}
                xid := vid
                NEXT:
                for {
                    if nodes.Has(xid) {break}
                    w := g.From(xid)
                    for w.Next() {
                        lxw := g.Lines(xid, w.Node().ID())
                        for lxw.Next() {
                            line = lxw.Line()
                            if !linesVisited.Has(line.ID()) {
                                linesVisited.Add(line.ID())
                                path = append(path, myLine{line, xid==line.From().ID()})
                                xid = w.Node().ID()
                                goto NEXT
                            }
                        }
                    }
                }
                paths = append(paths, path)
            }
        }
    }
    return nodes, paths
}

func printPaths(paths [][]myLine) {
    for j, path := range paths {
        fmt.Printf("%d: nodes: (", j)
        for i, p := range path {
            f, t := p.line.From().ID(), p.line.To().ID()
            if !p.direction {f, t = t, f}
            if i == 0 {fmt.Printf("%d", f)}
            fmt.Printf(" %d", t)
        }
        fmt.Printf(")  lines: {")
        for i, p := range path {
            if i != 0 {fmt.Printf(" ")}
            fmt.Printf("%d", p.line.ID())
        }
        fmt.Printf("}\n")
    }
}

func generateGraph() *multi.UndirectedGraph {
    lines = []struct{ F, T int64 }{
        {0, 1}, {1, 2}, {1, 2}, {1, 3}, {3, 4}, {4, 2}, {2, 5}, {5, 6}, {7, 8}, {8, 9}, {9, 10}, {9, 9},
    }

    g := multi.NewUndirectedGraph()
    for i, l := range lines {
        g.SetLine(multi.Line{F: multi.Node(l.F), T: multi.Node(l.T), UID: int64(i)})
    }

    return g
}

func main() {
    g := generateGraph()
    nodes, paths := connectingPaths(g)
    fmt.Printf("len(nodes, paths) = (%d, %d)\n", len(nodes), len(paths))
    printPaths(paths)
    return
}

  1. 関連した話題として、"Removing degree-2 vertices from a graph" (StackExchange) を見つけました。 

  2. また関連した話題として、"In igraph in R: how can I remove non-branch-point vertices, while maintaining connectivity?" (stackoverflow) を見つけました。 

  3. 分岐を持たない閉路グラフの場合は処理されないことになります(全ての vertex が degree = 2 なので)。 

0
0
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
0
0