-1

I want to creat an alogrithm in R using while and for such that, given a vector V and an integer M, sums the elements of the vector V until that sum exceeds M, and I want it to inform the value that exceeded M as well as how many repetitions it took in order to exceed such number (i.e., how many elements of the vector did it summed).

Gaspar
  • 49
  • 1
  • 8
  • It's easier to help you if you provide a [reproducible example](https://stackoverflow.com/questions/5963269/how-to-make-a-great-r-reproducible-example) with sample input and desired output that can be used to test and verify possible solutions. – MrFlick Aug 25 '22 at 20:36
  • You're right. Say: V = [1 2 3 4 5]; X = 7; V[1] + V[2] = 1 + 2 = 3 <= 7; V[1] + V[2] + V[3] = 1 + 2 +3 = 6 <= 7; V[1] + V[2] + V[3] + V[4] = 1 + 2 + 3 + 4 = 10 > 7; So I want the algorithm to stop at 4, inform the value of 10, and tell me the number of iterations it took (again, in this case, 4). Hope it's clear! – Gaspar Aug 25 '22 at 20:37
  • I used X instead of M here, but it's the same. It's the value it has to exceed. – Gaspar Aug 25 '22 at 20:44

2 Answers2

2

Here are three functions.

  1. fun uses vectorised cumsum to sum the vector;
  2. The other two have self-descriptive names.

They all return a named vector with the iterations count and the first sum exceeding m.

fun <- function(v, m) {
  i <- which(cumsum(v) < m)
  if(length(i) + 1L < length(v))
    i <- c(i, length(i) + 1L)
  c(Iter = length(i), Sum = sum(v[i]))
}
fun_while <- function(v, m) {
  Sum <- 0L
  Iter <- 0L
  while(Iter < length(v)) {
    Iter <- Iter + 1L
    Sum <- Sum + v[Iter]
    if(Sum > m) break
  }
  c(Iter = Iter, Sum = Sum)
}
fun_for <- function(v, m) {
  Sum <- 0L
  for(i in seq_along(v)) {
    Sum <- Sum + v[i]
    if(Sum > m) break
  }
  if(is.null(i))   i <- 0L
  c(Iter = i, Sum = Sum)
}

V <- 1:5
M <- 7
fun(V, M)
#> Iter  Sum 
#>    4   10
fun_while(V, M)
#> Iter  Sum 
#>    4   10
fun_for(V, M)
#> Iter  Sum 
#>    4   10

V <- 1:5
M <- 20
fun(V, M)
#> Iter  Sum 
#>    5   15
fun_while(V, M)
#> Iter  Sum 
#>    5   15
fun_for(V, M)
#> Iter  Sum 
#>    5   15

V <- 1:10
M <- 42
fun(V, M)
#> Iter  Sum 
#>    9   45
fun_while(V, M)
#> Iter  Sum 
#>    9   45
fun_for(V, M)
#> Iter  Sum 
#>    9   45

V <- NULL
M <- 7
fun(V, M)
#> Iter  Sum 
#>    0    0
fun_while(V, M)
#> Iter  Sum 
#>    0    0
fun_for(V, M)
#> Iter  Sum 
#>    0    0

Created on 2022-08-25 by the reprex package (v2.0.1)

Rui Barradas
  • 70,273
  • 8
  • 34
  • 66
  • I was comparing your fun_for vs my f.loop, no clue why, but the max of your function is high: Unit: microseconds expr min lq mean median uq max neval cld f.loop(v, val) 77.6 82.50 96.6310 85.20 88.00 440.5 1000 a fun_for(v, val) 70.7 75.20 93.0540 77.50 80.65 5389.6 1000 a (ie 440 vs 5389) – Will Aug 25 '22 at 21:35
  • Thank you very much, truly. But in reality I am looking for something much, much simpler. Using just ```for``` and ```while```. If somebody comes up with an idea for that, I'd be truly appreciated. – Gaspar Aug 25 '22 at 22:04
  • @Gaspar I don't understand this last comment, one of the functions uses only `while` and another uses only `for`. – Rui Barradas Aug 26 '22 at 04:42
1

both looping and vectorial way. benchmark added for curiosity.

library(data.table)
library(microbenchmark)
library(ggplot2)

val <- 2e6
v <- seq(1, 1e6, 2)

f.vec <- function(v, val) {
  dt <- as.data.table(v)
  dt[, cs := cumsum(v)]
  idx <- dt[cs <= val, .N]
  valnext <- dt[idx+1, .(v)]
  list(rep = idx, valnext = valnext)
}

f.loop <- function(v, val) {
  idx <- 1
  total <- 0
  for (el in v) {
    total <- total + el
    if (total >= val) {
      break
    } 
    idx <- idx + 1
  }
  list(rep = idx-1, valnext = v[idx])
}

f.vec(v, val)
#> $rep
#> [1] 1414
#> 
#> $valnext
#>       v
#> 1: 2829
f.loop(v, val)
#> $rep
#> [1] 1414
#> 
#> $valnext
#> [1] 2829

tm <- microbenchmark(f.vec(v, val), f.loop(v, val), times=1000L)
autoplot(tm)
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

tm
#> Unit: microseconds
#>            expr    min     lq      mean median     uq     max neval cld
#>   f.vec(v, val) 4848.5 5163.9 7856.0267 5643.6 9146.1 66502.9  1000   b
#>  f.loop(v, val)   72.7   75.8   81.1795   79.1   81.2   255.2  1000  a

Created on 2022-08-25 with reprex v2.0.2

Will
  • 910
  • 7
  • 17
  • Thank you very much for your answer, but I need to do it using the while function! – Gaspar Aug 25 '22 at 20:52
  • @Gaspar Why do you need to do it using `while`, is it homework? If so, please edit the question with that information, not even in the question title you say that `while` is *needed*. – Rui Barradas Aug 25 '22 at 20:56
  • It's the syntax I use. And the title does say the name of the functions "while" and "for". But will make it clearer anyways. – Gaspar Aug 25 '22 at 21:00
  • 2
    Gaspar> you're right, I ignored your requirements because I assumed it was a beginner mistake. R is a vectorial language. it does not make sense to do it with a loop. and the performance will kill you. but added with a loop to answer your question. – Will Aug 25 '22 at 21:07