You can use nested outer :
set.seed(1) X = rnorm(10) Y = seq(11,20) Z = seq(21,30) F = array(0, dim = c( length(X),length(Y),length(Z) ) ) for (i in 1:length(X)) for (j in 1:length(Y)) for (k in 1:length(Z)) F[i,j,k] = X[i] * (Y[j] + Z[k]) F2 <- outer(X, outer(Y, Z, "+"), "*") > identical(F, F2) [1] TRUE
Microbenchmark, including expand.grid solution proposed by Nick K:
X = rnorm(100) Y = seq(1:100) Z = seq(101:200) forLoop <- function(X, Y, Z) { F = array(0, dim = c( length(X),length(Y),length(Z) ) ) for (i in 1:length(X)) for (j in 1:length(Y)) for (k in 1:length(Z)) F[i,j,k] = X[i] * (Y[j] + Z[k]) return(F) } nestedOuter <- function(X, Y, Z) { outer(X, outer(Y, Z, "+"), "*") } expandGrid <- function(X, Y, Z) { df <- expand.grid(X = X, Y = Y, Z = Z) G <- df$X * (df$Y + df$Z) dim(G) <- c(length(X), length(Y), length(Z)) return(G) } library(microbenchmark) mbm <- microbenchmark( forLoop = F1 <- forLoop(X, Y, Z), nestedOuter = F2 <- nestedOuter(X, Y, Z), expandGrid = F3 <- expandGrid(X, Y, Z), times = 50L) > mbm Unit: milliseconds expr min lq mean median uq max neval forLoop 3261.872552 3339.37383 3458.812265 3388.721159 3524.651971 4074.40422 50 nestedOuter 3.293461 3.36810 9.874336 3.541637 5.126789 54.24087 50 expandGrid 53.907789 57.15647 85.612048 88.286431 103.516819 235.45443 50