图分析:识别循环路径

时间:2015-06-24 18:55:47

标签: python graph igraph

我有一个无向图,如:

import igraph as ig
G = ig.Graph()
G.add_vertices(9)
G.add_edges([(0,1), (1,2),(2,3),(3,0),(0,4),(4,5),(5,6),(6,7),(6,8)])

从节点0到1,2,3有一个“循环路径”回到0(对不起,在pic中节点标签以1而不是0开始)

Graph

对于赋值,我需要识别“循环路径”连接到图的其余部分的节点,即0,最重要的是,“循环路径”本身,即{{1 }和/或[0,1,2,3,0]

我正在努力,但真的没有任何线索。如果我使用here中的[0,3,2,1,0]函数,我当然只会find_all_paths(G,0,0)

4 个答案:

答案 0 :(得分:2)

由于问题也是用networkx标记的,我用它来举例说明代码。

在图论中,“循环路径”通常称为循环。

我看到的最简单(可能不是最快)的想法是找到周期和一组关节点(或切割椎体,即增加连接组件数量的点),然后它们的交点就是解决方案。 / p>

以同样的方式开始:

import networkx as nx
G.add_nodes_from([9])
G.add_edges_from([(0,1), (1,2),(2,3),(3,0),(0,4),(4,5),(5,6),(6,7),(6,8)])

现在问题的解决方案:

cycles = nx.cycle_basis(G) # list of cycles
cuts = list(nx.articulation_points(G)) # list of cut verteces
nodes_needed = set() # the set of nodes we are looking for
for cycle in cycles:
    for node in cycle:
        if node in cuts:
            nodes_needed.add(node)

答案 1 :(得分:1)

以下是使用广度优先搜索查找循环的示例。我想知道是否存在更有效的方法。如果是中等大图或更长的最大循环长度,这可能会持续很长时间。深度优先搜索也可以做同样的事情。首先,我相信您使用R发布了问题,因此请在下面找到R版本。 python版本由于同样的原因而不是完全pythonic,因为我从R快速翻译。 有关说明,请参阅代码中的注释。

import igraph

# creating a toy graph
g = igraph.Graph.Erdos_Renyi(n = 100, p = 0.04)

# breadth first search of paths and unique loops
def get_loops(adj, paths, maxlen):
    # tracking the actual path length:
    maxlen -= 1
    nxt_paths = []
    # iterating over all paths:
    for path in paths['paths']:
        # iterating neighbors of the last vertex in the path:
        for nxt in adj[path[-1]]:
            # attaching the next vertex to the path:
            nxt_path = path + [nxt]
            if path[0] == nxt and min(path) == nxt:
                # the next vertex is the starting vertex, we found a loop
                # we keep the loop only if the starting vertex has the 
                # lowest vertex id, to avoid having the same loops 
                # more than once
                paths['loops'].append(nxt_path)
                # if you don't need the starting vertex 
                # included at the end:
                # paths$loops <- c(paths$loops, list(path))
            elif nxt not in path:
                # keep the path only if we don't create 
                # an internal loop in the path
                nxt_paths.append(nxt_path)
    # paths grown by one step:
    paths['paths'] = nxt_paths
    if maxlen == 0:
        # the final return when maximum search length reached
        return paths
    else:
        # recursive return, to grow paths further
        return get_loops(adj, paths, maxlen)

adj = []
loops = []
# the maximum length to limit computation time on large graphs
# maximum could be vcount(graph), but that might take for ages
maxlen = 4

# creating an adjacency list
# for directed graphs use the 'mode' argument of neighbors() 
# according to your needs ('in', 'out' or 'all')
adj = [[n.index for n in v.neighbors()] for v in g.vs]

# recursive search of loops 
# for each vertex as candidate starting point
for start in xrange(g.vcount()):
    loops += get_loops(adj, 
        {'paths': [[start]], 'loops': []}, maxlen)['loops']

R

相同
require(igraph)

# creating a toy graph
g <- erdos.renyi.game(n = 100, p.or.m = 0.04)

# breadth first search of paths and unique loops
get_loops <- function(adj, paths, maxlen){
    # tracking the actual path length:
    maxlen <- maxlen - 1
    nxt_paths <- list()
    # iterating over all paths:
    for(path in paths$paths){
        # iterating neighbors of the last vertex in the path:
        for(nxt in adj[[path[length(path)]]]){
            # attaching the next vertex to the path:
            nxt_path <- c(path, nxt)
            if(path[1] == nxt & min(path) == nxt){
                # the next vertex is the starting vertex, we found a loop
                # we keep the loop only if the starting vertex has the 
                # lowest vertex id, to avoid having the same loops 
                # more than once
                paths$loops <- c(paths$loops, list(nxt_path))
                # if you don't need the starting vertex included 
                # at the end:
                # paths$loops <- c(paths$loops, list(path))
            }else if(!(nxt %in% path)){
                # keep the path only if we don't create 
                # an internal loop in the path
                nxt_paths <- c(nxt_paths, list(nxt_path))
            }
        }
    }
    # paths grown by one step:
    paths$paths <- nxt_paths
    if(maxlen == 0){
        # the final return when maximum search length reached
        return(paths)
    }else{
        # recursive return, to grow paths further
        return(get_loops(adj, paths, maxlen))
    }
}

adj <- list()
loops <- list()
# the maximum length to limit computation time on large graphs
# maximum could be vcount(graph), but that might take for ages
maxlen <- 4

# creating an adjacency list
for(v in V(g)){
    # for directed graphs use the 'mode' argument of neighbors() 
    # according to your needs ('in', 'out' or 'all')
    adj[[as.numeric(v)]] <- neighbors(g, v)
}

# recursive search of loops 
# for each vertex as candidate starting point
for(start in seq(length(adj))){
    loops <- c(loops, get_loops(adj, list(paths = list(c(start)), 
        loops = list()), maxlen)$loops)
}

答案 2 :(得分:1)

好的,这是我自己问题答案的第一部分:

感谢Max Li'sdeeenes'的帮助,我想改写networkx cycle_basis function以便在python_igraph中工作:

SELECT  pi.desc,
    pa.nameAddress,
    pi.ref,
    pi.descItem,
    pi.quantity,
    pi.totalDF,
    pi.code,
    pi.codeBL,
    cl.dateShip,    po.dtValidated,     po.supervisorDate,  DATEDIFF(po.supervisorDate, po.dtValidated) AS 'diffValidSupervisor',   
    DATEDIFF(cl.dtlivr, po.supervisorDate) AS 'diffExpeValid',  year(cl.dtlivr),    month(cl.dtlivr) FROM
    new.proforma_item pi
        INNER JOIN
    old.cdestk_lig cl ON pi.codeCde = cl.codcde INNER JOIN new.proforma po ON po.idProforma = pi.idProforma 
    Inner JOIN new.proforma_address pa ON po.idProforma = pa.idProforma GROUP BY pi.desc, pi.ref, pi.descItem, pi.code, pi.codeBL, cl.dateShip, 
    po.dtValidated, po.supervisorDate, month(cl.dateShip), po.dateInvoice 
    HAVING (po.dateInvoice between CONCAT(YEAR(CURDATE()),'-01-01') AND   last_day(curdate()-interval 1 month))

答案 3 :(得分:0)

对于大图和有向图,@ deeenes的答案是正确的,并且是python版本 是O.K.,但R版本存在瓶颈,无法复制列表时间和时间 再次,我通过以下方式解决了性能问题:

# breadth first search of paths and unique loops
get_loops <- function(adj, paths, maxlen) {
  # tracking the actual path length:
  maxlen <- maxlen - 1
  nxt_paths <- list()
  # count of loops and paths in the next step, avoid copying lists that cause        performance bottleneck.
  if (is.null(paths$lc))
    paths$lc <- 0
  paths$pc <- 0
  # iterating over all paths:
  for (path in paths$paths) {
    # iterating neighbors of the last vertex in the path:
    for (nxt in adj[[path[length(path)]]]) {
      # attaching the next vertex to the path:
      nxt_path <- c(path, nxt)
      if (path[1] == nxt & min(path) == nxt) {
        # the next vertex is the starting vertex, we found a loop
        # we keep the loop only if the starting vertex has the
        # lowest vertex id, to avoid having the same loops
        # more than once
        paths$lc <- paths$lc + 1
        paths$loops[paths$lc] <- list(nxt_path)
        # if you don't need the starting vertex included
        # at the end:
        # paths$loops <- c(paths$loops, list(path))
        # cat(paste(paths$loops,collapse=","));cat("\n")
      } else if (!(nxt %in% path)) {
        # keep the path only if we don't create
        # an internal loop in the path
        paths$pc <- paths$pc + 1
        nxt_paths[paths$pc] <- list(nxt_path)
      }
    }
  }
  # paths grown by one step:
  paths$paths <- nxt_paths
  if (maxlen == 0) {
    # the final return when maximum search length reached
    return(paths)
  } else{
    # recursive return, to grow paths further
    return(get_loops(adj, paths, maxlen))
  }
}