保留其元素在同一列表中没有适当子集的向量(来自向量列表)(使用 RCPP)
Keeping vectors (from list of vectors) whose elements do not have a proper subset within that same list (using RCPP)
我之前已经问过这个问题 并且使用 purr
包得到了满意的答案。然而,这已被证明是我程序中的一个瓶颈,所以我想使用 RCPP
包重写该部分。
真子集:集合S的真子集S'是严格包含在S中的子集,因此排除了S本身(注意我也排除了空集).
假设列表中有以下向量:
a = c(1,2)
b = c(1,3)
c = c(2,4)
d = c(1,2,3,4)
e = c(2,4,5)
f = c(1,2,3)
我的目标是只保留列表中没有适当子集的向量,在这个例子中是 a、b 和 c。
上一个解决方案
library(purr)
possibilities <- list(a,b,c,d,e,f)
keep(possibilities,
map2_lgl(.x = possibilities,
.y = seq_along(possibilities),
~ !any(map_lgl(possibilities[-.y], function(z) all(z %in% .x)))))
您是否尝试过先在 base R 中优化解决方案?例如,以下重现您的预期输出并使用(更快的)基本 R 数组例程:
match_fun <- Vectorize(function(s1, s2) all(s1 %in% s2))
mat <- outer(possibilities, possibilities, match_fun)
possibilities[colSums(mat) == 1]
#[[1]]
#[1] 1 2
#
#[[2]]
#[1] 1 3
#
#[[3]]
#[1] 2 4
受 Onyambu 的高性能解决方案的启发,这是另一个使用递归函数的基本 R 选项
f_recursive <- function(x, i = 1) {
if (i > length(x)) return(x)
idx <- which(sapply(x[-i], function(el) all(x[[i]] %in% el))) + 1
if (length(idx) == 0) f_recursive(x, i + 1) else f_recursive(x[-idx], i + 1)
}
f(possibilities)
性能与 Onyambu 的解决方案相当。
poss <- rep(possibilities, 100)
microbenchmark::microbenchmark(
method1(poss),
onlySet(poss),
f_recursive(poss))
#Unit: milliseconds
# expr min lq mean median uq
# method1(poss) 682.558602 710.974831 750.325377 730.627996 765.040976
# onlySet(poss) 1.700646 1.782713 1.870972 1.819820 1.918669
# f_recursive(poss) 1.681120 1.737459 1.884685 1.806384 1.901582
# max neval
# 1200.562889 100
# 2.371646 100
# 3.217013 100
这里的概念是避免 O(N^3) 并使用较少的顺序。这里提供的另一个答案仍然会很慢,因为它大于 O(N^2)。这是一个小于 O(N^2) 的解决方案,当所有元素都是唯一时,最坏的情况是 O(N^2)。
onlySet <- function(x){
i <- 1
repeat{
y <- sapply(x[-1], function(el)!all(is.element(x[[1]], el)))
if(all(y)){
if(i==length(x)) break
else i <- i+1
}
x <- c(x[-1][y], x[1])
}
x
}
现在要显示时差,请查看以下内容:
match_fun <- Vectorize(function(s1, s2) all(s1 %in% s2))
method1 <- function(a){
mat <- outer(a, a, match_fun)
a[colSums(mat) == 1]
}
poss <- rep(possibilities, 100)
microbenchmark::microbenchmark(method1(poss), onlySet(poss))
Unit: milliseconds
expr min lq mean median uq max neval cld
method1(poss) 840.7919 880.12635 932.255030 889.36380 923.32555 1420.1077 100 b
onlySet(poss) 1.9845 2.07005 2.191647 2.15945 2.24245 3.3656 100 a
我之前已经问过这个问题 purr
包得到了满意的答案。然而,这已被证明是我程序中的一个瓶颈,所以我想使用 RCPP
包重写该部分。
真子集:集合S的真子集S'是严格包含在S中的子集,因此排除了S本身(注意我也排除了空集).
假设列表中有以下向量:
a = c(1,2)
b = c(1,3)
c = c(2,4)
d = c(1,2,3,4)
e = c(2,4,5)
f = c(1,2,3)
我的目标是只保留列表中没有适当子集的向量,在这个例子中是 a、b 和 c。
上一个解决方案
library(purr)
possibilities <- list(a,b,c,d,e,f)
keep(possibilities,
map2_lgl(.x = possibilities,
.y = seq_along(possibilities),
~ !any(map_lgl(possibilities[-.y], function(z) all(z %in% .x)))))
您是否尝试过先在 base R 中优化解决方案?例如,以下重现您的预期输出并使用(更快的)基本 R 数组例程:
match_fun <- Vectorize(function(s1, s2) all(s1 %in% s2))
mat <- outer(possibilities, possibilities, match_fun)
possibilities[colSums(mat) == 1]
#[[1]]
#[1] 1 2
#
#[[2]]
#[1] 1 3
#
#[[3]]
#[1] 2 4
受 Onyambu 的高性能解决方案的启发,这是另一个使用递归函数的基本 R 选项
f_recursive <- function(x, i = 1) {
if (i > length(x)) return(x)
idx <- which(sapply(x[-i], function(el) all(x[[i]] %in% el))) + 1
if (length(idx) == 0) f_recursive(x, i + 1) else f_recursive(x[-idx], i + 1)
}
f(possibilities)
性能与 Onyambu 的解决方案相当。
poss <- rep(possibilities, 100)
microbenchmark::microbenchmark(
method1(poss),
onlySet(poss),
f_recursive(poss))
#Unit: milliseconds
# expr min lq mean median uq
# method1(poss) 682.558602 710.974831 750.325377 730.627996 765.040976
# onlySet(poss) 1.700646 1.782713 1.870972 1.819820 1.918669
# f_recursive(poss) 1.681120 1.737459 1.884685 1.806384 1.901582
# max neval
# 1200.562889 100
# 2.371646 100
# 3.217013 100
这里的概念是避免 O(N^3) 并使用较少的顺序。这里提供的另一个答案仍然会很慢,因为它大于 O(N^2)。这是一个小于 O(N^2) 的解决方案,当所有元素都是唯一时,最坏的情况是 O(N^2)。
onlySet <- function(x){
i <- 1
repeat{
y <- sapply(x[-1], function(el)!all(is.element(x[[1]], el)))
if(all(y)){
if(i==length(x)) break
else i <- i+1
}
x <- c(x[-1][y], x[1])
}
x
}
现在要显示时差,请查看以下内容:
match_fun <- Vectorize(function(s1, s2) all(s1 %in% s2))
method1 <- function(a){
mat <- outer(a, a, match_fun)
a[colSums(mat) == 1]
}
poss <- rep(possibilities, 100)
microbenchmark::microbenchmark(method1(poss), onlySet(poss))
Unit: milliseconds
expr min lq mean median uq max neval cld
method1(poss) 840.7919 880.12635 932.255030 889.36380 923.32555 1420.1077 100 b
onlySet(poss) 1.9845 2.07005 2.191647 2.15945 2.24245 3.3656 100 a