r igraph找到所有周期

问题描述 投票:0回答:1

我已指示igraph并想要获取所有周期。周长函数有效,但只返回最小的周期。在R中有一种方法可以获取长度大于3的图形中的所有循环(没有顶点指向自身和循环)

r igraph cycle girth
1个回答
3
投票

它不是igraph中的直接功能,但当然你可以编码。要查找循环,请从某个节点开始,转到某个相邻节点,然后找到返回原始节点的简单路径。由于您没有提供任何样本数据,我将用一个简单的例子来说明。

样本数据

## Sample graph
library(igraph)
set.seed(1234)
g = erdos.renyi.game(7, 0.29, directed=TRUE)
plot(g, edge.arrow.size=0.5)

Sample Graph

寻找周期

让我从一个节点和一个邻居开始。节点2连接到节点4.所以一些周期可能看起来像2 - > 4 - >(除了2或4之外的节点) - > 2.让我们得到所有这样的路径。

v1 = 2
v2 = 4
lapply(all_simple_paths(g, v2,v1, mode="out"), function(p) c(v1,p))
[[1]]
[1] 2 4 2
[[2]]
[1] 2 4 3 5 7 6 2
[[3]]
[1] 2 4 7 6 2

我们看到有三个循环从2开始,4作为第二个节点。 (我知道你说长度大于3.我会回到那个。)

现在我们只需要为每个节点v1和v1的每个邻居v2执行此操作。

Cycles = NULL
for(v1 in V(g)) {
    for(v2 in neighbors(g, v1, mode="out")) {
        Cycles = c(Cycles, 
            lapply(all_simple_paths(g, v2,v1, mode="out"), function(p) c(v1,p)))
    }
}

这在整个图中给出了17个循环。虽然您可能需要查看两个问题,具体取决于您希望如何使用它。首先,你说你想要长度大于3的循环,所以我假设你不希望看起来像2 - > 4 - > 2的循环。这些很容易摆脱。

LongCycles = Cycles[which(sapply(Cycles, length) > 3)]

LongCycles有13个循环消除了4个短周期

2 -> 4 -> 2
4 -> 2 -> 4
6 -> 7 -> 6
7 -> 6 -> 7

但该清单指出了另一个问题。还有一些你循环,你可能会认为是重复。例如:

2 -> 7 -> 6 -> 2
7 -> 6 -> 2 -> 7
6 -> 2 -> 7 -> 6

你可能想要把它们除掉。要获得每个循环的一个副本,您始终可以选择以最小顶点数开头的顶点序列。从而,

LongCycles[sapply(LongCycles, min) == sapply(LongCycles, `[`, 1)]
[[1]]
[1] 2 4 3 5 7 6 2
[[2]]
[1] 2 4 7 6 2
[[3]]
[1] 2 7 6 2

这给出了不同的周期。


关于效率和可扩展性的补充

我提供了一个更有效的代码,我最初提供的代码。然而,它主要是为了争论,除了非常简单的图形,你将无法产生所有周期。

这是一些更有效的代码。它消除了检查许多无法产生循环或将作为冗余循环消除的情况。为了便于运行我想要的测试,我把它变成了一个函数。

## More efficient version
FindCycles = function(g) {
    Cycles = NULL
    for(v1 in V(g)) {
        if(degree(g, v1, mode="in") == 0) { next }
        GoodNeighbors = neighbors(g, v1, mode="out")
        GoodNeighbors = GoodNeighbors[GoodNeighbors > v1]
        for(v2 in GoodNeighbors) {
            TempCyc = lapply(all_simple_paths(g, v2,v1, mode="out"), function(p) c(v1,p))
            TempCyc = TempCyc[which(sapply(TempCyc, length) > 3)]
          TempCyc = TempCyc[sapply(TempCyc, min) == sapply(TempCyc, `[`, 1)]
          Cycles  = c(Cycles, TempCyc)
        }
    }
    Cycles
}

然而,除了非常简单的图形之外,存在可能路径的组合爆炸,因此找到所有可能的周期是完全不切实际的,我将使用比您在评论中提到的图形小得多的图来说明这一点。

首先,我将从一些小图开始,其中边的数量大约是顶点数的两倍。生成我的示例的代码如下,但我想关注循环次数,所以我将从结果开始。

## ecount ~ 2 * vcount
Nodes   Edges   Cycles
10   21    15
20   41    18
30   65    34
40   87   424
50  108  3433
55  117 22956

但是,您报告的数据边缘大约是顶点的5倍。让我们来看看这样的例子。

## ecount ~ 5 * vcount
Nodes  Edges    Cycles
10      48        3511
12      61       10513
14      71      145745

随着循环次数的增加,使用具有50K边缘的10K节点似乎是不可能的。顺便说一下,用14个顶点和71个边来计算这个例子花了几分钟。

为了重现性,以下是我生成上述数据的方法。

set.seed(1234)
g10 = erdos.renyi.game(10, 0.2, directed=TRUE)
ecount(g10)
length(FindCycles(g10))

set.seed(1234)
g20 = erdos.renyi.game(20, 0.095 , directed=TRUE)
ecount(g20)
length(FindCycles(g20))

set.seed(1234)
g30 = erdos.renyi.game(30, 0.056 , directed=TRUE)
ecount(g30)
length(FindCycles(g30))

set.seed(1234)
g40 = erdos.renyi.game(40, 0.042 , directed=TRUE)
ecount(g40)
length(FindCycles(g40))

set.seed(1234)
g50 = erdos.renyi.game(50, 0.038 , directed=TRUE)
ecount(g50)
length(FindCycles(g50))

set.seed(1234)
g55 = erdos.renyi.game(55, 0.035 , directed=TRUE)
ecount(g55)
length(FindCycles(g55))

##########
set.seed(1234)
h10 = erdos.renyi.game(10, 0.55, directed=TRUE)
ecount(h10)
length(FindCycles(h10))

set.seed(1234)
h12 = erdos.renyi.game(12, 0.46, directed=TRUE)
ecount(h12)
length(FindCycles(h12))

set.seed(1234)
h14 = erdos.renyi.game(14, 0.39, directed=TRUE)
ecount(h14)
length(FindCycles(h14))
© www.soinside.com 2019 - 2024. All rights reserved.