R语言 计算最优路径,并对顶点停靠点进行成本惩罚

zy1mlcev  于 2023-10-13  发布在  其他
关注(0)|答案(2)|浏览(99)

我希望用R来计算最佳旅行路线。假设我有一个像下面这样的旅行时间表。
通过igraph和distance_table,我和我的同事已经找到了如何计算旅行路线,使旅行时间之和最小化,或者使路线中的边数最小化(我通过将所有旅行时间设置为1来实现)。
我还没有弄清楚如何做,是计算最小旅行时间,但为每个停止点添加时间旅行惩罚。这样,我就可以避免获得中途停留过多的最佳路线。最好的方法是什么?我想用R来做,但我愿意使用igraph以外的库(我不太熟悉)。
| 起源|目的地|旅行时间|
| --|--|--|
| 一|B| 10 |
| 一|C| 20 |
| 一|D| 10 |
| 一|E| 30 |
| B|一| 10 |
| B| D| 5 |
| C|一| 20 |
| D|一| 10 |
| D| B| 5 |
| E|一| 30 |

6l7fqoea

6l7fqoea1#

你当然可以在这里使用igraph来使你的工作更容易。
首先,将数据框转换为图形对象,这很简单:

library(igraph)

g <- graph_from_data_frame(df)

我们可以通过做一些

plot(g)

如果我们想要节点之间的最短距离,考虑旅行时间,我们可以简单地使用函数distances,使用旅行时间作为权重:

travel_times <- edge_attr(g, 'Travel Time')

times <- distances(g, weights = travel_times)
times
#>    A  B  C  D  E
#> A  0 10 20 10 30
#> B 10  0 30  5 40
#> C 20 30  0 30 50
#> D 10  5 30  0 40
#> E 30 40 50 40  0

如果我们想添加一个固定的停留时间,那么我们可以为每条边添加一个固定的权重。我们需要在距离矩阵中的所有非零项的末尾删除一个中途停留时间,因为如果遍历n条边,将有n-1个中途停留:

stopover_time <- 5
times_with_stopover <- travel_times + stopover_time

totals <- distances(g, weights = times_with_stopover)

totals[totals > 0] <- totals[totals > 0] - stopover_time
totals
#>    A  B  C  D  E
#> A  0 10 20 10 30
#> B 10  0 35  5 45
#> C 20 35  0 35 55
#> D 10  5 35  0 45
#> E 30 45 55 45  0

如果中途停留时间根据节点而变化,则可以根据输入 Dataframe 中的目的地节点来编码该信息。
创建于2023-10-03带有reprex v2.0.2

来自问题的 Dataframe ,格式可重现

df <- structure(list(Origin = c("A", "A", "A", "A", "B", "B", "C", 
"D", "D", "E"), Destination = c("B", "C", "D", "E", "A", "D", 
"A", "A", "B", "A"), `Travel Time` = c(10L, 20L, 10L, 30L, 10L, 
5L, 20L, 10L, 5L, 30L)), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10"))
qij5mzcb

qij5mzcb2#

这里有一个替代方法,它允许在不同的点有不同的停止时间。
其主要思想是将每个顶点分为两个,inout。所有指向原点的边都将指向 in 顶点,所有离开它的边都将从 out 顶点开始。这两者将通过从 inout 的单个边连接,其权重表示停止时间。

# vertex names
v <- unique(unlist(df[, c('Origin', 'Destination')]))
# change vertex names to e.g. 'A.in' and 'A.out'
df$Origin <- paste0(df$Origin, '.out')
df$Destination <- paste0(df$Destination, '.in')
# add stopping penalties
df <- rbind(df, list(paste0(v, '.in'), paste0(v, '.out'), rep(5, length(v))))

现在df看起来像这样:

#    Origin Destination Travel Time
# 1   A.out        B.in          10
# 2   A.out        C.in          20
# 3   A.out        D.in          10
# 4   A.out        E.in          30
# 5   B.out        A.in          10
# 6   B.out        D.in           5
# 7   C.out        A.in          20
# 8   D.out        A.in          10
# 9   D.out        B.in           5
# 10  E.out        A.in          30
# 11   A.in       A.out           5
# 21   B.in       B.out           5
# 31   C.in       C.out           5
# 41   D.in       D.out           5
# 51   E.in       E.out           5

最短路径可以计算:

g <- graph_from_data_frame(df)
g <- set_edge_attr(g, "weight", value=df$`Travel Time`)

# compute only distances from "out" to "in" vertices
d <- distances(g, mode='out', v=paste0(v, '.out'), to=paste0(v, '.in'))
diag(d) <- 0
rownames(d) <- colnames(d) <- v
d
#    A  B  C  D  E
# A  0 10 20 10 30
# B 10  0 35  5 45
# C 20 35  0 35 55
# D 10  5 35  0 45
# E 30 45 55 45  0

数据来自@Allan卡梅隆的回答:

df <- structure(list(Origin = c("A", "A", "A", "A", "B", "B", "C", 
"D", "D", "E"), Destination = c("B", "C", "D", "E", "A", "D", 
"A", "A", "B", "A"), `Travel Time` = c(10L, 20L, 10L, 30L, 10L, 
5L, 20L, 10L, 5L, 30L)), class = "data.frame", row.names = c("1", 
"2", "3", "4", "5", "6", "7", "8", "9", "10"))

相关问题