Sum the odds numbers of a "number"

Viewed 125

I am trying to sum the odds numbers of a specific number (but excluding itself), for example: N = 5 then 1+3 = 4

a<-5
sum<-function(x){
  k<-0
  for (n in x) {
    if(n %% 2 == 1)
      k<-k+1      
  }
         return(k)
}

sum(a)

# [1] 1

But the function is not working, because it counts the odds numbers instead of summing them.

5 Answers

We may use vectorized approach

a1 <- head(seq_len(a), -1)
sum(a1[a1%%2 == 1])
[1] 4

If we want a loop, perhaps

f1 <- function(x) {
  s <- 0
  k <- 1
  while(k < x) {
    if(k %% 2 == 1) {
      s <- s + k
    }
     k <- k + 1
     
     }
s

}
f1(5)

The issue in OP's code is

for(n in x)

where x is just a single value and thus n will be looped once - i.e. if our input is 5, then it will be only looped once and 'n' will be 5. Instead, it would be seq_len(x -1). The correct loop would be something like

f2<-function(x){
  k<- 0
  for (n in seq_len(x-1)) {
    if(n %% 2 == 1) {
     k <- k + n 
    }
    
  }
    k    
}


f2(5)

NOTE: sum is a base R function. So, it is better to name the custom function with a different name

Mathematically, we can try the following code to calculate the sum (N could be odd or even)

(ceiling((N - 1) / 2))^2

It's simple and it does what it says:

sum(seq(1, length.out = floor(N/2), by = 2))

The multiplication solution is probably gonna be quicker, though.

NB - an earlier version of this answer was

sum(seq(1, N - 1, 2))

which as @tjebo points out, silently gives the wrong answer for N = 1.

We could use logical statement to access the values:

a <- 5
a1 <- head(seq_len(a), -1)

sum(a1[c(TRUE, FALSE)])

output:

[1] 4

Fun benchmarking. Does it surprise that Thomas' simple formula is by far the fastest solution...?

count_odds_thomas <- function(x){
  (ceiling((x - 1) / 2))^2
}

count_odds_akrun <- function(x){
  a1 <- head(seq_len(x), -1)
  sum(a1[a1%%2 == 1])
}

count_odds_dash2 <- function(x){
  sum(seq(1, x - 1, 2))
}

m <- microbenchmark::microbenchmark(
  akrun = count_odds_akrun(10^6),
  dash2 = count_odds_dash2(10^6),
  thomas = count_odds_thomas(10^6)
)
m
#> Unit: nanoseconds
#>    expr      min         lq        mean   median       uq      max neval
#>   akrun 22117564 26299922.0 30052362.16 28653712 31891621 70721894   100
#>   dash2  4016254  4384944.0  7159095.88  4767401  8202516 52423322   100
#>  thomas      439      935.5    27599.34     6223     8482  2205286   100

ggplot2::autoplot(m)
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

Moreover, Thomas solution works on really big numbers (also no surprise)... on my machine, count_odds_akrun stuffs the memory at a “mere” 10^10, but Thomas works fine till Infinity…

count_odds_thomas(10^10)
#> [1] 2.5e+19

count_odds_akrun(10^10)
#> Error: vector memory exhausted (limit reached?)
Related