r igraph find all cycles
我已指导 igraph 并想获取所有周期。周长函数有效,但只返回最小的周期。 R中有没有办法在长度大于3的图中获取所有循环(没有指向自身和循环的顶点)
它不是igraph中的直接函数,但你当然可以编码。要找到一个循环,您从某个节点开始,到某个相邻节点,然后找到返回原始节点的简单路径。由于您没有提供任何示例数据,我将通过一个简单的示例进行说明。
样本数据
1
2 3 4 5 |
## Sample graph
library(igraph) set.seed(1234) g = erdos.renyi.game(7, 0.29, directed=TRUE) plot(g, edge.arrow.size=0.5) |
寻找循环
让我从一个节点和一个邻居开始。节点 2 连接到节点 4。所以一些循环可能看起来像 2 -> 4 ->(2 或 4 以外的节点)-> 2。让我们得到所有这样的路径。
1
2 3 4 5 6 7 8 9 |
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 执行此操作。
1
2 3 4 5 6 7 |
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 的循环。这些很容易摆脱。
1
|
LongCycles = Cycles[which(sapply(Cycles, length) > 3)]
|
LongCycles 有 13 个周期,消除了 4 个短周期
1
2 3 4 |
2 -> 4 -> 2
4 -> 2 -> 4 6 -> 7 -> 6 7 -> 6 -> 7 |
但该列表指出了另一个问题。还有一些你可能认为是重复的循环。例如:
1
2 3 |
2 -> 7 -> 6 -> 2
7 -> 6 -> 2 -> 7 6 -> 2 -> 7 -> 6 |
您可能想清除这些。要获得每个循环的一个副本,您始终可以选择以最小顶点编号开始的顶点序列。因此,
1
2 3 4 5 6 7 |
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 |
这只是给出了不同的循环。
关于效率和可扩展性的补充
我提供了一个更高效的代码版本
最初提供。但是,它主要是为了
争辩说,除了非常简单的图表,你将无法
产生所有循环。
这里有一些更高效的代码。它消除了许多检查
不能产生循环或将被消除的情况
作为一个冗余循环。为了便于运行测试
我想要的,我把它变成了一个函数。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
## 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 } |
但是,除了非常简单的图形外,还有一个组合
可能路径的爆炸,因此找到所有可能的循环是
完全不切实际我将用小得多的图表来说明这一点
比你在评论中提到的那个。
首先,我将从一些小图开始,其中边的数量
大约是顶点数的两倍。生成我的代码
示例如下,但我想关注周期数,所以我
将从结果开始。
1
2 3 4 5 6 7 8 |
## 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 倍
许多边作为顶点。让我们看一些这样的例子。
1
2 3 4 5 |
## ecount ~ 5 * vcount
Nodes Edges Cycles 10 48 3511 12 61 10513 14 71 145745 |
以此为周期数的增长,使用10K节点
50K 边缘似乎是不可能的。顺便说一句,花了几个
分钟计算具有 14 个顶点和 71 个边的示例。
为了重现性,这是我生成上述数据的方式。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
set.seed(1234)
g10 = erdos.renyi.game(10, 0.2, directed=TRUE) ecount(g10) length(FindCycles(g10)) set.seed(1234) set.seed(1234) set.seed(1234) set.seed(1234) set.seed(1234) ########## set.seed(1234) set.seed(1234) |
- 此代码适用于小图,但随着图的邻居越来越多,代码会崩溃 R。有没有办法简化它
- 当然,如果图表很大,检查所有路径将需要循环和内存。上面的代码确实有一些效率低下的地方。我会在答案中添加一点。但如果你连一步都做不了,那也无济于事。你能搜索一对节点吗?答案的一部分说: all_simple_paths(g, v2,v1, mode=”out”) ?需要一些时间来测试和编写更有效的方法来做到这一点。我将在今天晚些时候添加它。
- 该代码能够帮助我,但性能使其无法使用。很高兴看到您对答案的更新
- @Ankit所以我的目标是正确的大小,你的图有多少个顶点和边?
- 我有 10,000 个向量和 50k 个边
- 这比以前更好,但仍然放弃了 900 个节点的图形。我将其标记为答案,因为这接近我想要的。谢谢
- 是的,我很抱歉,但我认为即使对于 100 个节点和 500 个边的图,这也行不通。将有太多的周期来计算它们。
- iGraph 库有一个函数 girth 来确定最小的循环,它也适用于大图。如果此代码在库中并用 C 构建,性能会提高吗?
- 它可能表现得更好一些,但我认为对于大型示例没有办法解决这个问题。除了我的答案之外,数字示例的要点是,周期数似乎增长得非常快。对于 1K 节点和 5K 边,我预计周期数会如此之大,以至于没有计算机能够掌握答案。
- 这对我很有帮助,解释得很好,节奏也很好,非常感谢
来源:https://www.codenong.com/55091438/