【问题标题】:Finding the cumulative sum of the value of nodes in a DAG查找 DAG 中节点值的累积和
【发布时间】:2021-08-04 07:06:03
【问题描述】:

假设我有以下有向无环图 (DAG),每个节点的权重为 1。

我有兴趣根据其祖先的值计算每个节点的累积总和。假设我之前说的每个节点的权重都是1,那么这就是我期望得到的

这是我尝试做的:

 library(tidygraph, quietly = TRUE) 
 library(tidyverse)
 library(ggraph)

 # create adjacencies
 grafo_df <- tribble(
  ~from, ~to,
  "C", "A",
  "C", "B",
  "A", "D",
  "B", "D")
 
 # create the graph
 grafo <- as_tbl_graph(grafo_df)
 
 
 # calculate accumulated sum
 grafo %>% 
  arrange(node_topo_order()) %>% 
  mutate(
   
   revenue = 1,
   
   cum_weight = map_dfs(1, .f = function(node, path, ...) {
    
    sum(.N()$revenue[c(node, path$node)])
    
   })) %>% 
  as_tibble() %>% 
  unnest("cum_weight")
 
#> # A tibble: 4 x 3
#>   name  revenue cum_weight
#>   <chr>   <dbl>      <dbl>
#> 1 C           1          1
#> 2 A           1          2
#> 3 B           1          2
#> 4 D           1          3

由reprex package (v2.0.0) 于 2021-05-13 创建

如您所见,D的累加和结果是3而不是4,因为D的值应该是A和B的累加值之和。我不明白为什么D不加4

我试图理解here 给出的解决方案,但很难理解它

如何获得累计金额?

更新 #1

我(暂时)不关心算法的复杂性,也就是说,如果算法在 O(V + E) 中完成它是不相关的。

this问题中提到的重要一点是关于两次计数的问题,即A的值的偏和等于C(1) + A(1) = 2,偏B 的值的总和等于 C(1) + B (1) = 2,因此可以说 D 的值不等于 A (2) + B(2) 的部分总和,因为C 会重复我认为它不适用于这种情况,原因如下:

假设这 4 个节点(A、B、C 和 D)中的每一个都是互联网节点,每个节点产生 1 美元的收入,因此 4 个节点的总累计收入为 4 美元。如果 D 是其余节点的收敛节点,那么在 D 停止工作的情况下,其余节点和 D 的收益将不再可能,因此,它的价值是 4 美元。

更新 #2

如果我添加一条从 C 到 D 的新路径,那么 D 的值应该始终为 4,因为依赖节点的数量保持不变,也就是说,重要的是累加和中依赖节点的数量。例如,在@ThomasIsCoding 提出的解决方案中,如果我添加这个新路径,D 的值现在是 5,我认为部分原因是他们的算法使用度数作为参数来计算累积和,但是,如果我添加一个附加节点,则计算正确。

更新 #3

我放置的示例很简单,目的是易于理解目标,但是,我没有指定它应该对于具有许多具有三种不同拓扑结构的节点的图是可推广的。最外层是树,中间层是环,最内层是全网格。

【问题讨论】:

  • 如何让节点 A 的总和为 2?不应该是1吗?
  • 据我了解 D 应该是 5,而不是 4,4 只是没有节点值的祖先的总和。
  • @Onyambu。因为是累加和,所以通过map_dfs应用sum函数时,A的值加上它的祖先的值。一开始都值1。当我们开始迭代计算累加时,A 的值将是 A 当前拥有的值加上其祖先的值,在本例中为 C,因此 1 + 1 = 2。
  • @Rocco。它应该值 4 的场景或上下文如下:假设这 4 个节点是创收站点(在这种情况下,假设它们产生 1 美元)。如您所见,节点 C、B 和 A 依赖于 D 的工作,因为所有“流量”都经过 D。如果 D 崩溃或停止工作会发生什么?好吧,我们将损失 4 美元的收入,这将产生 C、A、B 和 D。在此假设下,D 的值或累计总和应为 4。
  • 您对@Rocco 问题的回答没有意义。您通过将 C 自己的值 (1) 添加到其祖先的总和 (也是 1) 来计算 C 的总和,那么为什么不对 D 做同样的事情呢?如果您始终如一地应用此规则(即,如果您对 D 和对 C 做同样的事情),它确实会导致 D 的总和为 2+2+1=5。

标签: r algorithm igraph graph-theory tidygraph


【解决方案1】:

这是一个 igraph 选项,使用 distance 和参数 mode = "in"

  • 如果您的节点未加权,即所有节点均使用 revenue=1
g <- graph_from_data_frame(grafo_df)

data.frame(name = names(V(g))) %>%
  mutate(revenue = 1) %>%
  mutate(cum_weight = rowSums((!is.infinite(distances(g, mode = "in"))) %*% diag(revenue)))

给你

  name revenue cum_weight
1    C       1          1
2    A       1          3
3    B       1          2
4    F       1          1
5    D       1          5
  • 如果您的节点是加权的,例如,
data.frame(name = names(V(g))) %>%
  mutate(revenue = 1:n()) %>%
  mutate(cum_weight = rowSums((!is.infinite(distances(g, mode = "in"))) %*% diag(revenue)))

给你

  name revenue cum_weight
1    C       1          1
2    A       2          7
3    B       3          4
4    F       4          4
5    D       5         15

数据

grafo_df <- tribble(
  ~from, ~to,
  "C", "A",
  "C", "B",
  "A", "D",
  "C", "D",
  "B", "D",
  "F", "A"
)

plot(g) 的 DAG 为

【讨论】:

  • 添加一个额外的节点效果很好!但是,如果我添加一条新路径,例如从 C 到 D,它会将 D 的值更新为 5,这是不正确的。同样,关注您的代码可能会帮助我想办法修复它,但是,我不熟悉 igraph
  • @William 感谢您的反馈。我想我错误地理解了你的问题,并把它弄得太复杂了。我更新了我的解决方案,希望它现在适用于您。
  • 它有效,但正如我在更新#3 中提出的,我似乎未能解释解决方案必须是可推广的,以便计算具有不同拓扑的图的累积和。例如,如果我在 tibble 中添加 F -> A,则 D 的最终值现在应该是 6,但它会给出 5。
  • @William 我不明白为什么 D 是 6。D 有 4 个祖先。你能解释一下吗。我根据您的 update3 更新了我的解决方案。
  • @William 我认为新方法应该也可以,但效率更高。
【解决方案2】:

现在问题很清楚了,所以我提出了一个算法,我无法编写代码,因为我不知道您使用的语言。

对于图中的每个节点 Ni,我们将计算祖先 Ai 的集合,那么每个节点的累加和将是 |Ai| + 1。

  1. 用空祖先集 Ai = {}
  2. 初始化所有节点
  3. 从包含所有没有传入边的节点的集合 S0 开始
  4. 初始化下一组 Sn+1
  5. 对每个节点 N 迭代 Sn:
  6. 对于具有来自 N 的传入边的所有节点 D:
    1. 将 D 的祖先集与 N 的祖先集加上 N 本身合并
    2. 去掉egde N->D
  7. 如果 D 没有其他传入边,则将其添加到 Sn+1
  8. 如果 Sn+1 不为空,则将 pass 增加到 n+1 并从 2 开始重复。

这个解决方案最大的限制是复杂性,我稍后会尝试找到一些优化的解决方案。

【讨论】:

  • 我会尽量把你的算法翻译成代码
猜你喜欢
  • 1970-01-01
  • 2017-12-15
  • 1970-01-01
  • 2022-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-07
  • 1970-01-01
相关资源
最近更新 更多