Calculations on individual digits in a string

Viewed 112

I have a string of concatenated integers:

A = "883745"

I wish to perform calculations on the individual digits. Here I extract each single number:

N1 = as.numeric(substr(A , 1, 1))
N2 = as.numeric(substr(A , 2, 2))
N3 = as.numeric(substr(A , 3, 3))
N4 = as.numeric(substr(A , 4, 4))
N5 = as.numeric(substr(A , 5, 5))
N6 = as.numeric(substr(A , 6, 6))

Then I need to calculate:

N1 *  nchar(A)    + 
N2 * (nchar(A) - 1) + 
N3 * (nchar(A) - 2) + 
N4 * (nchar(A) - 3) + 
N5 * (nchar(A) - 4) + 
N6 * (nchar(A) - 5)

How do I do this in a loop?

4 Answers

substring not substr is useful for these kind of vectorised substringing operations:

nc <- seq(nchar(A))
sum(as.numeric(substring(A, nc, nc)) * rev(nc))
#[1] 134

Your code can be considerably simpler and no loop is necessary. This may be what you are trying to accomplish:

A <- 883745
len <- nchar(A)
N <- as.numeric(unlist(strsplit(as.character(A), "")))
N
# [1] 8 8 3 7 4 5
N * (len - 0:5)
# [1] 48 40 12 21  8  5
sum(N * (len - 0:5))
# [1] 134

You are given a string:

A <- "883745"

You can break down your calculation as follows:

ans = 0
ans += as.numeric(substr(A , 1, 1)) * (nchar(A) - 0)
ans += as.numeric(substr(A , 2, 2)) * (nchar(A) - 1)
ans += as.numeric(substr(A , 3, 3)) * (nchar(A) - 2)
ans += as.numeric(substr(A , 4, 4)) * (nchar(A) - 3)
ans += as.numeric(substr(A , 5, 5)) * (nchar(A) - 4)
ans += as.numeric(substr(A , 6, 6)) * (nchar(A) - 5)

This can be summarized as:

ans = 0
for i = 1, 2, ..., 6
  ans += as.numeric(substr(A , i, i)) * (nchar(A) - i + 1)
end for

Here, i goes from 1 to 6, which is nchar(A), so you are to process all digits in "A".

Freshman

Loop as it is.

ans <- 0
for (i in 1:nchar(A)) {
  ans <- ans + as.numeric(substr(A , i, i)) * (nchar(A) - i + 1)
}
ans
#[1] 134

Sophomore

Pre-compute the result of substring and nchar, then index them in the loop.

nc <- nchar(A)
N <- as.numeric(substring(A, 1:nc, 1:nc))
L <- nc:1
ans <- 0
for (i in 1:nc) {
  ans <- ans + N[i] * L[i]
}
ans
#[1] 134

Junior

Replace the loop by sum.

nc <- nchar(A)
N <- as.numeric(substring(A, 1:nc, 1:nc))
L <- nc:1
ans <- sum(N * L)
ans
#[1] 134

Senior

Replace as.numeric + substring by utf8ToInt (@ThomasIsCoding). Replace sum by crossprod.

N <- utf8ToInt(A) - 48
L <- nchar(A):1
ans <- c(crossprod(N, L))
ans
#[1] 134

We can also pack it into one line:

c(crossprod(utf8ToInt(A) - 48, nchar(A):1))
#[1] 134

Let's try this using utf8ToInt

> sum((utf8ToInt(A) - 48) * nchar(A):1)
[1] 134

Benchmark

For those who may care about the speed

set.seed(1)
A <- paste0(sample(1e4), collapse = "")

thelatemail <- function() {
  nc <- seq(nchar(A))
  sum(as.numeric(substring(A, nc, nc)) * rev(nc))
}

ZheyuanLi <- function() {
  N <- utf8ToInt(A) - 48
  L <- nchar(A):1
  c(crossprod(N, L))
}

dcarlson <- function() {
  len <- nchar(A)
  N <- as.numeric(unlist(strsplit(as.character(A), "")))
  N * (len - 0:(len - 1))
  sum(N * (len - 0:(len - 1)))
}

TIC <- function() sum((utf8ToInt(A) - 48) * nchar(A):1)


microbenchmark(
  thelatemail(),
  ZheyuanLi(),
  dcarlson(),
  TIC(),
  check = "equivalent"
)

we can see

Unit: microseconds
          expr     min       lq      mean   median       uq     max neval
 thelatemail() 14615.1 14826.35 15297.588 14945.60 15218.65 24667.8   100
   ZheyuanLi()   856.1   937.30  1856.775   977.50  1017.15 69490.1   100
    dcarlson() 13328.3 14128.95 14815.929 14244.85 14575.45 22232.8   100
         TIC()   755.2   821.75   989.245   854.70   895.10  8863.7   100
Related