添加向量的连续元素直到值

时间:2011-12-16 21:06:54

标签: r

我想计算向量中连续元素的最小数量,这些元素在添加(连续)时将小于给定值。

例如,在以下向量中

ev<-c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 2.7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3.27, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 370.33, 1375.4, 
1394.03, 1423.8, 1360, 1269.77, 1378.8, 1350.37, 1425.97, 1423.6, 
1363.4, 1369.87, 1365.5, 1294.97, 1362.27, 1117.67, 1026.97, 
1077.4, 1356.83, 565.23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 356.83, 
973.5, 0, 240.43, 1232.07, 1440, 1329.67, 1096.87, 1331.37, 1305.03, 
1328.03, 1246.03, 1182.3, 1054.53, 723.03, 1171.53, 1263.17, 
1200.37, 1054.8, 971.4, 936.4, 968.57, 897.93, 1099.87, 876.43, 
1095.47, 1132, 774.4, 1075.13, 982.57, 947.33, 1096.97, 929.83, 
1246.9, 1398.2, 1063.83, 1223.73, 1174.37, 1248.5, 1171.63, 1280.57, 
1183.33, 1016.23, 1082.1, 795.37, 900.83, 1159.2, 992.5, 967.3, 
1440, 804.13, 418.17, 559.57, 563.87, 562.97, 1113.1, 954.87, 
883.8, 1207.1, 1046.83, 995.77, 803.93, 1036.63, 946.9, 887.33, 
727.97, 733.93, 979.2, 1176.8, 1241.3, 1435.6)

连续添加的最小元素数量(如向量中的顺序)总和为20000

为了更清楚,我需要以下内容: 从ev [1]开始,连续添加最多20000.记录你必须添加的元素数量,以r00 [1]的形式获得20000。然后从ev [2]开始并添加到20000,依此类推。记录你必须添加到20000的元素数量为r [2]。在ev的整个长度上这样做。然后返回min(r)

例如

j<-c(1, 2, 3, 5, 7, 9, 2)

我希望连续添加的最小元素数量可以让我们说&gt; 20。这应该是3(5 + 7 + 9)

非常感谢

4 个答案:

答案 0 :(得分:5)

好吧,我会试一试:这个会找到最小数字序列的长度 加起来高于max。它没有声称速度快,但它有O(2n)时间复杂度: - )

我让它返回起始索引和长度。

f <- function(x, max=10) {
  s <- 0
  len <- Inf
  start <- 1
  j <- 1
  for (i in seq_along(x)) {
     s <- s + x[i]
     while (s >= max) {
       if (i-j+1 < len) {
         len <- i-j+1
         start <- j
       }
       s <- s - x[j]
       j <- j + 1
     }
  }

  list(start=start, length=len)
  # uncomment the line below if you don't need the start index...
  #len
}

r <- f(ev, 20000) # list(start=245, length=15)
sum(ev[seq(r$start, len=r$length)]) # 20275.42

# Test speed:
x <- sin(1:1e6)

system.time( r <- f(x, 1.9) ) # 1.54 secs

# Compile the function makes it 9x faster...
g <- compiler::cmpfun(f)
system.time( r <- g(x, 1.9) ) # 0.17 secs

答案 1 :(得分:2)

library(zoo) # Needed for rollapply
N <- 20000 # The desired sum we want to achieve
j <- 0
for(i in 1:length(ev)){
    k <- rollapply(ev, i, sum)
    j[i] <- max(k)
    if(j[i] >= N){
        break
    }
}
i # contains how many consecutive elements you need to sum (15)
j[i] # contains the corresponding sum(20275.42)

目前,这并没有告诉您在向量中出现特定子集的位置,但另一种使用rollapply可以获取该信息。

还有其他方法可以做到这一点,但如果你有一个非常长的向量,这将打破循环,所以你不会计算超过你需要的。基本思想是使用rollapply创建长度为k的连续和的向量,然后找到其最大值。如果这小于我们想要的长度k + 1的总和那么做同样的事情。重复,直到找到大于所需阈值的总和。

编辑:

这似乎快了大约100倍。我没有将它与Tommy的答案进行比较(这可能比这更快,但与原始方法相比,这将提供显着的加速。

编辑2:移动[-n]并删除抑制警告会加快这一点。

myfun <- function(ev, N){
    i <- 1
    n <- length(ev)
    j <- ev
    repeat{
        j <- (j[-n] + ev[-c(1:i)])
        i <- i+1
        n <- n-1
        if(max(j) >= N | i > length(ev)){
            break;
        }
    }
    return(i)
}

myfun(ev, 20000)

# And stealing the idea from Tommy gives a nice speedup as well
myfuncomp <- compiler:cmpfun(myfun)
myfuncomp(ev, 20000)
myfunc3 <- compiler:cmpfun(myfun, options = list(optimize = 3))
myfunc3(ev, 20000)

library(rbenchmark) # For testing
# If you have Tommy's functions loaded as f and g you can compare
benchmark(f(ev, 20000), g(ev, 20000), myfun(ev, 20000), myfuncomp(ev, 20000), myfunc3(ev, 20000))

答案 2 :(得分:0)

你的意思是这样的吗?

> sum(ifelse(cumsum(ev)<=200000, 1, 0))
[1] 364

答案 3 :(得分:0)

我认为这可能是一个伪装的旅行推销员问题,除非你加入一些限制。你不一定从最大值开始向任何一个方向走,因为它可能是一个局部非密集的最大值

x=1:length(ev)
 plot(x,ev)
 lxy <- loess(ev~x )
 lines(predict(lxy, x=1:length(y)))
 title(main="loess() fit of ev")

enter image description here

但是在最密集值的区域,值非常平坦。

 x=1:length(y); y=c(356.83, 
 973.5, 0, 240.43, 1232.07, 1440, 1329.67, 1096.87, 1331.37, 1305.03, 
 1328.03, 1246.03, 1182.3, 1054.53, 723.03, 1171.53, 1263.17, 
 1200.37, 1054.8, 971.4, 936.4, 968.57, 897.93, 1099.87, 876.43, 
 1095.47, 1132, 774.4, 1075.13, 982.57, 947.33, 1096.97, 929.83, 
 1246.9, 1398.2, 1063.83, 1223.73, 1174.37, 1248.5, 1171.63, 1280.57, 
 1183.33, 1016.23, 1082.1, 795.37, 900.83, 1159.2, 992.5, 967.3, 
 1440, 804.13, 418.17, 559.57, 563.87, 562.97, 1113.1, 954.87, 
 883.8, 1207.1, 1046.83, 995.77, 803.93, 1036.63, 946.9, 887.33, 
 727.97, 733.93, 979.2, 1176.8, 1241.3, 1435.6)

 lxyhi <- loess(y~x)
 plot(x,y)
 lines(predict(lxyhi, x=1:length(y)))

enter image description here