r - 在保留度数的同时重新连接图中的边
问题描述
假设我有一个包含 5 个节点的图。每个节点都有一定数量的边(从节点到自身没有边),这些边称为该节点的度数。我想创建一组新的边,以便每个节点都具有与以前相同的度数。
我的第一次尝试是创建一个向量,其中每个节点 v 都有 degree(v) 条目,对该向量进行采样(以获得该向量的排列),将该向量分成两个长度相等的向量,并检查第 n 个条目是否在两个向量是不同的(将此条件称为 1)。如果是这种情况,这两个向量将形成一个从节点到自身没有边的边列表。
对于 5 个节点,这可以正常工作,但对于 1000+ 个节点和一些度数(超过 100)的节点,需要大量重复,直到满足条件 1。
我的第二次尝试是sample()
,创建两个向量并从那些不满足条件 1的节点对再次采样,然后添加拆分结果并将它们添加到剩余的两个向量中并重复几次,直到条件 1是满足或违反条件 1的节点集不能正确匹配以形成正确的边(即那些不违反条件 1的边)。
显式计算所有可能的向量(节点标签),删除无效的向量,然后随机选择一个向量对于大型图来说不是一个好主意。这将占用太多内存,并且仅计算所有这些可能也需要大量时间。
我在寻找什么
给定一个节点向量(只是整数标签,偶数长度),返回一个随机选择的(因此它需要使用类似的东西sample()
或基于伪随机数的其他函数)节点对集(最好是两个向量,它们形成一个边列表),以便每条边连接两个不同的节点,并且节点的度数保持不变。
编码示例
一个使用 5 个节点的小编码示例:
E<-c(1,1,1,1,2,2,2,3,3,4,4,4,5,5)
一个有效的解决方案:
V1<-c(1,1,1,1,2,2,4)
V2<-c(2,3,4,5,3,4,5)
另一个有效的解决方案(这有一个重复的边缘,这是允许的):
V1<-c(1,1,1,1,2,2,3)
V2<-c(2,3,4,5,4,4,5)
不是一个有效的解决方案(这有一个自我边缘,这是不允许的):
V1<-c(1,2,1,1,2,2,4)
V2<-c(1,3,4,5,3,4,5)
使用(外来的)R 库很好,如果他们设法加快速度,他们会受到特别欢迎。
附加信息
可以假设还提供了原始图中的实际边,而不是仅仅具有节点的向量(重复次数与它们在边中出现的次数一样多)。
解决方案
我认为解决这个问题的另一种方法是使用增长模型,例如Erdos-Renyi 模型,它有助于生成随机图。
你说,“我想创建一组新的边,这样每个节点的度数都和以前一样。” 如果我是你,我会修改 Erdos-Renyi 模型以生成新图。
首先,您需要创建一个图表。下面的代码(确切地说是函数 randomGraph)可以为您完成。
#install.packages("network")
library(network)
increaseDegree <- function(key, degreeMap) {
keyChar = as.character(key)
if(is.null(degreeMap[[keyChar]])) {
degreeMap[[keyChar]] = 0
}
degreeMap[[keyChar]] = degreeMap[[keyChar]] + 1
return(degreeMap)
}
getDegree <- function(key, degreeMap) {
keyChar = as.character(key)
if(is.null(degreeMap[[keyChar]])) {
return (0)
} else {
return (degreeMap[[keyChar]])
}
}
randomGraph <- function(numNodes) {
degreeMap <- new.env(hash=T, parent=emptyenv())
initialNumNodes = 2;
g <- network.initialize(numNodes, directed = FALSE);
add.edge(g,1,2);
increaseDegree(1, degreeMap)
increaseDegree(2, degreeMap)
i = 3;
while(i <= numNodes) {
sourceNode <- i;
destNode <- sample(i-1, 1);
add.edge(g,sourceNode,destNode);
increaseDegree(sourceNode, degreeMap)
increaseDegree(destNode, degreeMap)
i = i + 1;
}
return (list(graph = g, degreeMap = degreeMap))
}
如您所见,我使用哈希映射来存储每个节点的度数,并在需要时快速获取(参见 degreeMap 变量和函数 increaseDegree 和 getDegree)。
之后,您将获得第一个图表。但是,您需要第二个图,其中每个节点的度数与以前相同。为此,您可以修改 randomGraph 函数并使用第一个图形来创建新图形。因此,修改后的函数将类似于:
newGraphPresevingDegree <- function(oldGraphObject) {
oldGraph = oldGraphObject$graph
oldDegreeMap = oldGraphObject$degreeMap
numNodes = network.size(oldGraph)
newGraph <- network.initialize(numNodes, directed = FALSE);
newDegreeMap <- new.env(hash=T, parent=emptyenv())
i = 1;
while(i <= numNodes) {
sourceNode <- i;
sourceDesiredDegree = getDegree(sourceNode, oldDegreeMap);
sourceCurrentDegree = getDegree(sourceNode, newDegreeMap);
while(sourceCurrentDegree < sourceDesiredDegree) {
destNode <- sample(i:numNodes, 1);
destDesiredDegree = getDegree(destNode, oldDegreeMap);
destCurrentDegree = getDegree(destNode, newDegreeMap);
if(sourceNode != destNode && sourceCurrentDegree < sourceDesiredDegree
&& destCurrentDegree < destDesiredDegree && is.adjacent(newGraph,sourceNode,destNode) == FALSE) {
add.edge(newGraph, sourceNode, destNode);
increaseDegree(sourceNode, newDegreeMap)
increaseDegree(destNode, newDegreeMap)
sourceCurrentDegree = getDegree(sourceNode, newDegreeMap);
}
}
i = i + 1;
}
return (list(graph = newGraph, degreeMap = newDegreeMap))
}
最后,您通过以下方式执行所有操作:
# creating a random graph
numNodes = 3000;
oldGraphObject = randomGraph(numNodes)
oldGraph = oldGraphObject$graph
oldDegreeMap = oldGraphObject$degreeMap
#creating the new graph
newGraphObject = newGraphPresevingDegree(oldGraphObject)
newGraph = newGraphObject$graph
newDegreeMap = newGraphObject$degreeMap
并检查度数是否保持不变:
i = 1
while(i <= numNodes) {
oldDegree = length(get.neighborhood(oldGraph, i))
currentDegree = length(get.neighborhood(newGraph, i))
if(oldDegree != currentDegree) {
print(paste("old neighborhood of node", i))
print(get.neighborhood(oldGraph, i))
print(paste("new neighborhood of node", i))
print(get.neighborhood(newGraph, i))
print("------------")
}
i = i + 1
}
推荐阅读
- regex - 过滤掉具有 3 个或更多子文件夹的行
- android - 如何将两个不同的 Firebase 帐户用户带到不同的活动中?
- java - apache tomcat 服务器总是运行一个程序的问题,它显示端口 8080 已经被使用..?
- java - Maven surefire 插件默认策略来运行测试
- c++ - Cmake确实找到了`A_library`,但是当`make`它告诉`library not found for A_library`?
- javascript - 如何在反应中显示损坏的 HTML?
- vue-router - nuxt 链接正在更新路线,但不改变内容
- java - 摄取附件需要更多权限
- hadoop - Hive 查询以检查活动连接数
- regex - Nginx 配置捕获 uuid 路由