Воспроизводимый пример ниже можно использовать в качестве тестового примера. Я ищу решение base-R, потому что
Фон согласно вводу @Gregor:
Первоначальная проблема связана с проблемами набора номера. Таким образом, возникает вопрос «а лучше ли объединить два тура?» миллиарды раз. В приведенном ниже примере такая же структура (На каком расстоянии находится квартира с двумя местами? В каком месте лучше всего остановиться? Каков результат, если мы объединим две экскурсии?).
Мы знаем о замечательном ответ как ускорить R-код, но в нашем случае у нас есть дополнительная «бизнес-логика», которая проявляется в различных операторах if-else. Таким образом, нам кажется невозможным получить векторизованный код при наличии операторов if-else - но, возможно, мы что-то упускаем.
Воспроизводимый пример
generate_random_sequence <- function(nrows=1000) {
x <- c(0, runif (nrows, min = 0, max = 100), 0)
y <- c(0, runif (nrows, min = 0, max = 100), 0)
loc <- c("Start", floor(runif (nrows, 1000, 9999)), "Start")
return(data.table::data.table(x = x, y = y, loc = loc, stringsAsFactors = FALSE))
}
dist <- function(x1, y1, x2, y2) {
return(sqrt((x1-x2)^2 + (y1-y2)^2))
}
get_best_loc <- function(cur_loc, stop1, stop2) {
d1 <- dist(cur_loc$x, cur_loc$y, stop1$x, stop1$y)
d2 <- dist(cur_loc$x, cur_loc$y, stop2$x, stop2$y)
if (d1 <= d2) {
best_stop <- stop1
stop_id <- 1L
} else {
best_stop <- stop2
stop_id <- 2L
}
return(list(best_stop = best_stop, stop_id = stop_id))
}
combine_sequence <- function(t1, t2) {
t_combined <- c(1, rep(NA, nrow(t1) + nrow(t2) - 4), 1)
ind_max <- c(nrow(t1) - 1, nrow(t2) - 1)
last_stop <- t1[1, ]
ind_next_stop <- c(2, 2)
ind_t_combined <- 1
while (all(ind_next_stop <= ind_max)) {
r <- get_best_loc(last_stop,
t1[ind_next_stop[1],], t2[ind_next_stop[2],])
best_stop <- r$best_stop; stop_id <- r$stop_id; rm(r)
if (stop_id == 1) {
ind_next_stop[1] <- ind_next_stop[1] + 1
} else {
ind_next_stop[2] <- ind_next_stop[2] + 1
}
ind_t_combined <- ind_t_combined + 1
t_combined[ind_t_combined] <- stop_id
last_stop <- best_stop
}
if (ind_next_stop[1] < ind_max[1]) {
t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <-
t1$loc[ind_next_stop[1]:(nrow(t1) - 1)]
} else {
t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <-
t2$loc[ind_next_stop[2]:(nrow(t2) - 1)]
}
return(t_combined)
}
n <- 1e2
t1 <- generate_random_sequence(n)
t2 <- generate_random_sequence(n)
microbenchmark::microbenchmark(combine_sequence(t1, t2),
times = 10L, unit = "s")
Ввод из комментариев:
Когда я использую профилирование, я получаю следующий результат. Мне непонятно, есть ли у меня рычаги, где я могу получить ускорение в 1000 раз. (Я бы просто был в темноте)
Код для профилирования:
profvis::profvis({
t_combined <- c(1, rep(NA, nrow(t1) + nrow(t2) - 4), 1)
ind_max <- c(nrow(t1) - 1, nrow(t2) - 1)
last_stop <- t1[1, ]
ind_next_stop <- c(2, 2)
ind_t_combined <- 1
while (all(ind_next_stop <= ind_max)) {
r <- get_best_loc(last_stop,
t1[ind_next_stop[1],], t2[ind_next_stop[2],])
best_stop <- r$best_stop; stop_id <- r$stop_id; rm(r)
if (stop_id == 1) {
ind_next_stop[1] <- ind_next_stop[1] + 1
} else {
ind_next_stop[2] <- ind_next_stop[2] + 1
}
ind_t_combined <- ind_t_combined + 1
t_combined[ind_t_combined] <- stop_id
last_stop <- best_stop
}
if (ind_next_stop[1] < ind_max[1]) {
t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <-
t1$loc[ind_next_stop[1]:(nrow(t1) - 1)]
} else {
t_combined[(ind_t_combined + 1):(length(t_combined) - 1)] <-
t2$loc[ind_next_stop[2]:(nrow(t2) - 1)]
}
})
Кроме того, как только вы определили узкие места в производительности, подумайте об использовании Rcpp или rJava для ускорения этих частей без необходимости переносить всю кодовую базу.
Одна из потенциальных проблем - rbind, ср. stackoverflow.com/questions/50990237/….
Выглядит лучше. Я снова открою, но, пожалуйста, также опишите свой код словами. Комментариев нет, поэтому было бы неплохо иметь описание цели. Ваше профилирование показывает, что на get_best_loc тратится много времени. Но, глядя на код, мне совсем не ясно, какова цель combine_sequence или почему вам нужен цикл while, выполняемый по очереди, а не, скажем, группировать каждую 1000 вместе для некоторой векторизации.




