Reputation: 85
If I have some double summation like this, how do I completely vectorize it? (No loops)
A good starting point would be to get vector i and j. For example if n = 2:
i = 0:2
j = c(0,0,1,0,1,2)
Then how should we approach next?
Upvotes: 2
Views: 366
Reputation: 269596
1) row/col Use row
and col
to get matrices i
and j
and then just write out the formula multiplying by the inequality shown to zero out the cells above the diagonal.
n <- 2
i <- row(diag(n+1)) - 1
j <- col(i) - 1
sum( (i+1) * i * j * (j <= i) )
## [1] 20
2) row/call from 1 Since the zero row and column don't actually contribute to the sum we could alternately use:
n <- 2
i <- row(diag(n))
j <- col(i)
sum( (i+1) * i * j * (j <= i) )
# [1] 20
3) explicit polynomial Another approach which builds on the former approaches is that it is clear that the result is a polynomial in n.
We can deduce what this polynomial is by regressing the values against n up to sufficiently large n. From the regression coefficients we see that the coefficients above ^5 are effectively zero so
f <- function(n, i = row(diag(n)), j = col(i)) sum( (i+1) * i * j * (j <= i) )
DF <- data.frame(v = sapply(1:10, f), n = 1:10)
lm(v ~ poly(n, 9), DF) # note that coefs above n^5 are zero
so from the coefficients
coef(lm(v ~ poly(n, 5, raw = TRUE), DF))
write out the polynomial explicitly:
f2 <- function(n) n/15 + n^2/2 + n^3/1.2 + n^4/2 + n^5/10
# test
f2(2)
## [1] 20
# another test
f(20)
## [1] 406868
f2(20)
## [1] 406868
To double check this we can use list comprehensions to get a direct translation to code.
library(listcompr)
sum( gen.vector((i + 1) * i * j, j <= i, i = 0:n, j = 0:n) )
## [1] 20
Upvotes: 1
Reputation: 73295
i = 0
and j = 0
contribute nothing, so the summation can start from 1, not 0;
outer
can be used to evaluate j * i * (i + 1)
for all i = 1, 2, ..., n
and j = 1, 2, ..., n
, giving an n
-by-n
square matrix;
the summation is over j <= i
, only involving elements in the lower triangular part of this matrix.
n <- 2
i <- 1:n
j <- 1:n
x <- outer(i, j, function (i, j) j * i * (i + 1))
sum(x[lower.tri(x, diag = TRUE)])
#[1] 20
A less intuitive but more efficient way is to generate (i, j)
index for all the lower triangular elements directly.
n <- 2
i <- sequence(n:1, 1:n)
j <- rep(1:n, n:1)
sum(j * i * (i + 1))
#[1] 20
Actually, after some simple math, I realize that this simplifies to a single summation:
n <- 2
i <- 1:n
0.5 * c(crossprod(i * (i + 1)))
#[1] 20
Upvotes: 7