在R中对向量进行递归排序

时间:2019-06-04 14:51:05

标签: r sorting

我有类似的任务

n <- 5
set.seed(11)
X <- rnorm(n)

X.sort <- {}
for(i in 1:n){
  X.sort <- sort.int(c(X.sort, X[i]), decreasing = TRUE)
  print(X.sort) # actually, other computations with X.sort
}

产生类似的输出

[1] -0.5910311
[1]  0.02659437 -0.59103110
[1]  0.02659437 -0.59103110 -1.51655310
[1]  0.02659437 -0.59103110 -1.36265335 -1.51655310
[1]  1.17848916  0.02659437 -0.59103110 -1.36265335 -1.51655310

当矢量已被排序(除了要插入的新条目X.sort除外)时,在循环的每一轮中对X[i]进行“重新排序”的效率都不高。

我尝试过“告诉” R在何处插入元素

library(R.utils)

X.sort <- {}
for(i in 1:n){
  pos <- match(F, X.sort>X[i])
  if(is.na(pos)){
    X.sort <- c(X.sort,X[i])
  } else {
    X.sort <- insert(X.sort, pos, X[i])                      
  }
  print(X.sort)
}

但是在基准测试时不会产生任何收益。

是否有明显的改进,还是R已经有效地利用了X.sort被排序的知识?

编辑:

基准测试表明[但还请考虑以下问题]接受的答案最快(至少在n接近1000时),而且似乎也适用于大型n,并且成为最简单的一个。

library(R.utils)
library(microbenchmark)
n <- 600
set.seed(11)
X <- rnorm(n)

sorted_insert <- function(x, y) { 
  c(x[x >= y], y, x[x < y]) 
}

recursive_fun <- function(ans=list(NULL), vec, i=1) { 
  if (i > length(vec)) {
    tail(ans, -1)
  } else {
    ans <- c(ans, list(sorted_insert(ans[[i]], vec[i]))) 
    recursive_fun(ans=ans, vec, i=i+1) 
  }
}

microbenchmark(
  {
    X.sort <- {}
    for(i in 1:n){
      X.sort <- sort.int(c(X.sort, X[i]), decreasing = TRUE)
    }
  },{
    X.sort <- {}
    for(i in 1:n){
      pos <- match(F, X.sort>X[i])
      if(is.na(pos)){
        X.sort <- c(X.sort,X[i])
      } else {
        X.sort <- insert(X.sort, pos, X[i])                      
      }
    }
    },{
    X.sort <- {X[1]}
    for(i in 2:n){
      X.sort <- append(X.sort, X[i], after = sum(X.sort > X[i]))
    }
  },{
    lapply(seq_along(X), function(a) {sort(X[seq_len(a)], decreasing = T)})
  },{
    lapply(1:length(X), function(i) sort(X[1:i], decreasing = T))
  },
  {
    recursive_fun(vec=X)
  },
  times=50
)

结果:

       min        lq      mean    median        uq       max neval
 21.308012 22.264314 24.065012 22.798643 26.381362 34.629395    50
 19.554413 20.334643 21.875769 20.617807 24.085896 30.625841    50
  4.497919  4.804550  5.380192  4.912923  5.114310 13.522485    50
 23.540616 24.105807 25.311692 24.335780 24.985024 30.348792    50
 23.251905 24.067122 25.722031 24.745380 27.986197 30.010018    50
  3.928746  4.096568  4.358911  4.258701  4.390684  9.106202    50

3 个答案:

答案 0 :(得分:2)

代码中的瓶颈实际上是print语句。

这是另一种速度快大约5倍的方法(如果不需要打印):

n <- 10000
set.seed(11)
X <- rnorm(n)

X.sort <- {X[1]}
for(i in 2:n){
  X.sort <- append(X.sort, X[i], after = sum(X.sort > X[i]))
}

答案 1 :(得分:1)

您可以尝试这种递归方法

working 函数是sorted_insert,它将new元素插入元素>= new-element,vec[vec >= y]和{{ 1}}新元素<。假定向量总是被排序的(在这种情况下是真的)。​​

vec[vec < y]

sorted_insert <- function(x, y) { c(x[x >= y], y, x[x < y]) } sorted_function递归调用。如果计数器recursive_funi向量的长度(即未遍历整个未分类输入向量),则它将使用先前的分类答案{ {1}}作为输入向量,<=作为要插入的新元素。也就是说,每次迭代中的排序向量都是使用前一次迭代中的排序向量和未排序输入向量中的新元素建立的。抱歉,请尽我所能解释。

sorted_function

使用给定的示例

ans[[i]]

一个更大的例子

vec[i]

如果您的unsorted-input-vector很大,就会出现问题

recursive_fun <- function(ans=list(NULL), vec, i=1) { 
    if (i > length(vec)) {
        tail(ans, -1)
    } else {
        ans <- c(ans, list(sorted_insert(ans[[i]], vec[i]))) 
        recursive_fun(ans=ans, vec, i=i+1) 
    }
}

请注意,如果您不想要在每次迭代中收集结果,则可以使用n <- 5 set.seed(11) X <- rnorm(n) recursive_fun(vec=X) ,它应该很快。

答案 2 :(得分:0)

这里是lapply的一种方式-

n <- 5
set.seed(11)
x <- rnorm(n)

lapply(seq_along(x), function(a) {
  sort(x[seq_len(a)], decreasing = T)
})

[[1]]
[1] -0.5910311

[[2]]
[1]  0.02659437 -0.59103110

[[3]]
[1]  0.02659437 -0.59103110 -1.51655310

[[4]]
[1]  0.02659437 -0.59103110 -1.36265335 -1.51655310

[[5]]
[1]  1.17848916  0.02659437 -0.59103110 -1.36265335 -1.51655310

为了提高性能,您应该考虑将{cy1b}与Reduce一起使用。请参阅下面的基准-

n <- 600
set.seed(11)
x <- rnorm(n)

r_sort <- function(x, y) {
  append(x, y, after = sum(x > y))
}


microbenchmark(
  lapply = lapply(seq_along(x), function(a) {
  sort(x[seq_len(a)], decreasing = T)
  }),
  forloop = {
    x.sort <- x[1]
    for(i in 2:n){
      x.sort <- append(x.sort, x[i], after = sum(x.sort > x[i]))
    }
  },
  Reduce = Reduce(r_sort, as.list(x), accumulate = T), # only if you want intermediate results
  Reduce2 = Reduce(r_sort, as.list(x)),
  times = 50
)

Unit: milliseconds
    expr       min        lq      mean    median        uq       max neval
  lapply 35.069533 36.318154 45.302362 37.870738 41.959249 203.45682    50
 forloop  8.366112  8.743501 11.196852  9.128391 11.800904  30.76272    50
  Reduce  4.574459  4.861448  7.418195  5.332593 11.076522  22.40293    50
 Reduce2  4.556300  4.754075  6.918486  5.161860  9.563809  14.41776    50