生成系列 1, 2,1, 3,2,1, 4,3,2,1, 5,4,3,2,1

Generate series 1, 2,1, 3,2,1, 4,3,2,1, 5,4,3,2,1

我正在尝试生成一个向量,其中包含长度递减的序列,例如 1, 2,1, 3,2,1, 4,3,2,1, 5,4,3,2,1,即

c(1, 2:1, 3:1, 4:1, 5:1)

我尝试为此使用循环,但我不知道如何堆叠或连接结果。

for (i in 1:11)
 {
 x = rev(seq(i:1))
 print(x) 
 }
[1] 1
[1] 2 1
[1] 3 2 1
[1] 4 3 2 1
[1] 5 4 3 2 1
[1] 6 5 4 3 2 1
[1] 7 6 5 4 3 2 1
[1] 8 7 6 5 4 3 2 1
[1] 9 8 7 6 5 4 3 2 1
[1] 10  9  8  7  6  5  4  3  2  1
[1] 11 10  9  8  7  6  5  4  3  2  1

我也一直在试验 reprevseq,这是我最喜欢的选项,但没有走多远。

我们可以用 lapply

unlist(lapply(1:11, function(x) rev(seq(x))))

或者如@zx8754在评论中提到的,代替rev(seq,可以使用:

unlist(lapply(1:11, function(x) x:1))

或者按照@BrodieG 的建议,我们可以通过删除匿名函数调用来使它更紧凑

unlist(lapply(1:11, ":", 1))

sequence:

rev(sequence(5:1))
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

从 R 4.0.0 sequence 接受参数 fromby:

sequence(1:5, from = 1:5, by = -1)
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

rev 的高尔夫极简主义相去甚远...但是,如果您有一天早上醒来并想要创建这样一个 n = 1000 的序列(就像 中那样),那么后者实际上更快(但我可以在 fortunes::fortune(98) 中听到 Brian Ripley)

n = 1000

microbenchmark(
  f_rev = rev(sequence(n:1)),
  f_seq4.0.0 = sequence(1:n, from = 1:n, by = -1))
# Unit: microseconds
#        expr   min     lq     mean  median     uq    max neval
#       f_rev 993.7 1040.3 1128.391 1076.95 1133.3 1904.7   100
#  f_seq4.0.0 136.4  141.5  153.778  148.25  150.1  304.7   100

为了好玩,使用矩阵(并忽略警告 ;))

m <- matrix(c(1:5,0), ncol = 5, nrow = 5, byrow = T)
m[ upper.tri(m, diag = T) ]
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

我们可以将upper.tri简化为它的组成部分

m[ row(m) <= col(m)]
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

如果你能处理更多的乐趣,那么一些基准测试怎么样:

library(microbenchmark)
maxValue <- 1000

vec2 <- maxValue:1
m2 <- matrix(c(1:maxValue,0), ncol = maxValue, nrow = maxValue, byrow = T)

microbenchmark(
  
  henrik = {
    rev(sequence(maxValue:1))
  },
  henrik_4.0.0 = {
    sequence(1:maxValue, from = 1:maxValue, by = -1)
  },
  akrun = {
    unlist(lapply(1:maxValue, function(x) x:1))
  },
  symbolix1 = {
    m <- matrix(c(1:maxValue,0), ncol = maxValue, nrow = maxValue, byrow = T)
    m[ row(m) <= col(m) ]
  },
  symbolix2 = {
    m2[ row(m2) <= col(m2) ]
  },
  lmo1 = {
    unlist(lapply(1:maxValue, tail, x=maxValue:1))
  },
  lmo2 = {
    vec <- maxValue:1
    unlist(lapply(rev(vec), tail, x=vec))
  },
  lmo3 = {
    unlist(lapply(rev(vec2), tail, x=vec2))
  }
)
# Unit: microseconds
#         expr    min      lq      mean  median      uq      max neval
#       henrik 1018.7 1068.20  1176.430 1103.65 1223.20   2348.4   100
# henrik_4.0.0  139.9  147.90   166.092  151.40  162.70    379.0   100
#        akrun 3420.1 3637.75  3825.336 3729.10 3897.00   4960.6   100
#    symbolix1 6999.5 7483.20  7807.747 7618.30 7810.70  12138.7   100
#    symbolix2 4791.2 5043.00  5677.742 5190.50 5393.65  29318.7   100
#         lmo1 7530.1 7967.05 10918.201 8161.10 8566.45 132324.1   100
#         lmo2 7385.7 8017.95 12271.158 8213.90 8500.70 143798.2   100
#         lmo3 7539.5 7959.05 14355.810 8177.85 8500.85 131154.2   100

在此示例中,henrik_4.0.0 获胜! (仅对于带有 pre-R 4.0.0 sequence 的 bm,请参阅之前的编辑)


但我知道你在想什么,'why end all the fun there!'

好吧,让我们编写自己的 C++ 函数,看看它的性能如何

library(Rcpp)

cppFunction('NumericVector reverseSequence(int maxValue, int vectorLength){
                        
                        NumericVector out(vectorLength);
                        int counter = 0;
                        
                        for(int i = 1; i <= maxValue; i++){
                            for(int j = i; j > 0; j--){
                                out[counter] = j;
                                counter++;
                            }
                        }
                        
                        return out;
                        }')

maxValue <- 5
reverseSequence(maxValue, sum(1:maxValue))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

library(microbenchmark)
maxValue <- 1000

microbenchmark(
    akrun = {
        unlist(sapply(1:maxValue, function(x) x:1))
    },
    symbolix3 = {
        reverseSequence(maxValue, sum(1:maxValue))
    }
)

# Unit: microseconds
#        expr      min        lq     mean    median       uq       max neval
#      akrun 1522.250 1631.6030 3148.922 1829.9370 3357.493 45576.148   100
#  symbolix3  338.626  495.3825 1293.720  950.6635 2169.656  3816.091   100

另一种方法是在 lapply 中使用 tail,以连续 select 从初始向量中保留的元素数:

unlist(lapply(1:5, tail, x=5:1))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

或者,先构造基向量然后调用它可能会更快:

vec <- 5:1
unlist(lapply(rev(vec), tail, x=vec))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1