Arreglos 2

En este capítulo se resuelven algunos problemas que involucran vectores. Para ese fin se emplean las estructuras for y for-of, así como los métodos iterativos disponibles para matrices estudiados en el capítulo anterior. En general, los problemas que involucran arreglos, se resuelven de forma más sencilla (aunque no necesariamente más eficiente) empleando los métodos disponibles para el trabajo con arrays.

Por ejemplo, para calcular el valor de la siguiente expresión:

\[ \sum_{i=1}^{n}{v_i} \]

Es decir, calcular la sumatoria de los elementos de un vector "v", como el siguiente:

var v = [1, 2, 4, 5, 6, 8, 12, 23, 25, 42, 2, 6, 7, 3, 112];

La forma más simple consiste en llamar al método sum:

v.sum()

Igualmente (sobre todo si no se trabaja en la calculadora), se puede recurrir al método reduce:

v.reduce((s,e)=>s+e)

Es posible, también, emplear el método forEach:

var s = 0;
v.forEach(e => s+=e);
s

En este caso, como forEach simplemente ejecuta su función para cada uno de los elementos del array (v), el acumulador "s" debe ser iniciado antes del método y mostrado al final del mismo.

Se puede emplear también la estructura for-of, que es una variante de la estructura for y que tiene la siguiente sintaxis:

for (e of array) instrucciones;

Donde las instrucciones son ejecutadas para cada uno de los elementos e del array. Estando el elemento e disponible en las instrucciones del ciclo.

Empleando esta estructura, la solución es similar a la del método forEach:

var s = 0;
for (let e of v) s+=e;
s

Como for-of no recibe una función, sino directamente las instrucciones, se escribe directamente la instrucción que suma los elementos "e" del vector.

Por supuesto, también se puede emplear la recursividad para resolver este tipo de problemas. Así, con una función flecha inmediatamente invocada (la cual cuenta con una función recursiva anidada: "sum"), el problema se resuelve con:

(v=>{sum=i=>i>=0?sum=sum(i-1)+v[i]:0;return sum(v.length-1)})(v)
s

Como ya se vio previamente, una sumatoria (o productoria) con límites definidos, puede ser traducida directamente en un ciclo for, siendo los límites del ciclo los límites de la sumatoria (o productoria) y la variable de la sumatoria (o productoria) el contador del ciclo. El acumulador (la variable que almacena el resultado) debe iniciar en 0 si es una sumatoria y en 1 si es una productoria.

Así, para calcular la sumatoria, con la estructura for, se escribe:

var s = 0;
for (let i=0; i<v.length; i++) s+=v[i];
s

Es importante recordar que, cuando se trabaja con vectores (y objetos en general), las variables son punteros, es decir variables que sólo guardan la dirección de memoria del lugar donde están almacenados los datos, pero no los datos en sí. Por esa razón, para efectivamente copiar un vector, se debe emplear el método slice (o si se trabaja en la calculadora, se puede emplear también el método copy).

Ejemplos

Promedio de un vector de números

En este ejemplo se programa un método para calcular el promedio de los valores almacenados en un vector "x", programando la expresión:

\[ \bar{x} = \dfrac{\displaystyle\sum_{i=0}^{n-1}{x_i}}{n} \]

Al ser un problema sencillo, puede ser codificado directamente (sin necesidad de elaborar un algoritmo): el contador del ciclo "i", debe comenzar en 0 e ir hasta "n-1", incrementando en 1 en cada repetición del ciclo. El acumulador que se emplee, por ejemplo "s", debe comenzar en cero y en cada repetición del ciclo se le debe añadir el valor de x[i].

Sólo para que la lógica resulte aún más clara, se elabora el diagrama de flujo respectivo:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Start","text":"promedio","key":-1,"loc":"172 -460"},{"category":"Input","text":"vector de números: x","key":-3,"loc":"171.99999999999994 -410.4666427612303"},{"category":"Conditional","text":"n==0 ","key":-5,"loc":"171.99999999999994 -301.6332496643064"},{"category":"Output","text":"lanzar error:\n \"vector vacío\"","key":-4,"loc":"319.0000000000004 -142.99999999999994"},{"category":"Process","text":"s = 0","key":-2,"loc":"38.85404968261713 -254.33321380615212"},{"category":"Process","text":"i = i+1","key":-7,"loc":"-44.00000000000014 -145.44982967376689"},{"category":"Start","text":"fin","key":-9,"loc":"171.99999999999986 41.65581652323428"},{"category":"Output","text":"s/n","key":-10,"loc":"38.85404968261713 -11.22199312845845"},{"category":"Process","text":"n = num. de elementos en x","key":-11,"loc":"171.99999999999994 -358.9332855224607"},{"category":"For","text":"i<n","key":-6,"loc":"38.85404968261713 -145.44982967376689"},{"category":"Process","text":"i = 0","key":-12,"loc":"38.85404968261713 -199.7998565673826"},{"category":"Process","text":"s = s+x[i]","key":-13,"loc":"38.85404968261713 -76.09980278015117"}],"linkDataArray":[{"from":-5,"to":-4,"fromPort":"R","toPort":"T","visible":true,"points":[208.15933227539057,-301.6332496643064,218.15933227539057,-301.6332496643064,319.0000000000004,-301.6332496643064,319.0000000000004,-243.42775586446112,319.0000000000004,-185.22226206461585,319.0000000000004,-175.22226206461585],"text":"Si"},{"from":-5,"to":-2,"fromPort":"L","toPort":"T","visible":true,"points":[135.84066772460932,-301.6332496643064,125.84066772460932,-301.6332496643064,38.85404968261713,-301.6332496643064,38.85404968261713,-291.61657104492167,38.85404968261713,-281.5998924255369,38.85404968261713,-271.5998924255369],"text":"No"},{"from":-4,"to":-9,"fromPort":"B","toPort":"R","points":[319.0000000000004,-119.79997131347653,319.0000000000004,-109.79997131347653,319.0000000000004,-108,319.0000000000004,-108,319.0000000000004,41.655816523234265,205.55952284071176,41.655816523234265,195.55952284071176,41.655816523234265]},{"from":-10,"to":-9,"fromPort":"B","toPort":"L","points":[38.85404968261713,1.4580212148032716,38.85404968261713,11.45802121480327,38.85404968261713,12,38.85404968261713,12,38.85404968261713,41.655816523234265,138.44047715928815,41.655816523234265,148.44047715928815,41.655816523234265]},{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[171.99999999999994,-444.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-434.73332138061505,171.99999999999994,-424.73332138061505]},{"from":-3,"to":-11,"fromPort":"B","toPort":"T","points":[171.99999999999994,-396.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-386.1999641418455,171.99999999999994,-376.1999641418455]},{"from":-11,"to":-5,"fromPort":"B","toPort":"T","points":[171.99999999999994,-341.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-331.66660690307594,171.99999999999994,-321.66660690307594]},{"from":-2,"to":-12,"fromPort":"B","toPort":"T","points":[38.85404968261713,-237.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-227.06653518676737,38.85404968261713,-217.06653518676737]},{"from":-12,"to":-6,"fromPort":"B","toPort":"T","points":[38.85404968261713,-182.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-172.53317794799784,38.85404968261713,-162.53317794799784]},{"from":-6,"to":-13,"fromPort":"B","toPort":"T","visible":true,"points":[38.85404968261713,-128.36648139953593,38.85404968261713,-118.36648139953593,38.85404968261713,-110.86648139953593,38.85404968261713,-110.86648139953593,38.85404968261713,-103.36648139953593,38.85404968261713,-93.36648139953593],"text":"Si"},{"from":-13,"to":-7,"fromPort":"L","toPort":"B","points":[2.690673828124943,-76.09980278015117,-7.309326171875057,-76.09980278015117,-44.00000000000014,-76.09980278015117,-44.00000000000014,-97.14147691726666,-44.00000000000014,-118.18315105438214,-44.00000000000014,-128.18315105438214]},{"from":-7,"to":-6,"fromPort":"R","toPort":"L","points":[-16.1634902954103,-145.44982967376689,-6.163490295410298,-145.44982967376689,-1.506393432617287,-145.44982967376689,-1.506393432617287,-145.44982967376689,3.1507034301757244,-145.44982967376689,13.150703430175724,-145.44982967376689]},{"from":-6,"to":-10,"fromPort":"R","toPort":"T","visible":true,"points":[64.55739593505854,-145.44982967376689,74.55739593505854,-144.72491483688344,106,-144.72491483688344,106,-47,38.85404968261713,-47,38.85404968261713,-38.833124160766396,38.85404968261713,-28.833124160766396],"text":"No"}]} })

La condición del ciclo es "i<n", porque las iteraciones deben repetirse mientras el contador "i" es menor a "n", no menor o igual a "n", porque el primer índice en javascript es 0, no 1.

El código respectivo, es:

var promedio = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
let s = 0;
for (let i=0; i<n; i++) s += x[i];
return s/n;
};

Llamando al método con algunos valores de prueba, se obtiene:

var a = [1, 9, 12, 3, 5, 7, 12];
promedio(a)
randseed = 1;
var b = [].rand(20, 1, 30).round(); print(b);
promedio(b)

En el segundo ejemplo primero se genera (con rand) y muestra (con print) un vector con 20 números enteros aleatorios comprendidos entre 1 y 30. Luego, se llama a la función promedio para calcular el promedio de los elementos del vector.

Observe también que, antes de generar el vector con números aleatorios, se ha iniciado la semilla (randseed) en 1, para que siempre se genere la misma secuencia de números pseudoaleatorios, de manera que el ejemplo pueda ser replicado siempre (recuerde que, para volver a generar números realmente aleatorios, se debe asignar null a randseed ).

Otros ejemplos similares, pero generando los vectores con los métodos range y linspace, son:

var c = [].range(17, 51, 2); print(c); promedio(c)
var d = [].linspace(5, 10, 21); print(d);
promedio(d)

El problema resuelto con la estructura for-of, es:

var promedioa = function(x) {
const n=x.length;
if (n===0) throw "vector vacío";
let s = 0;
for (let e of x) s+=e;
return s/n;
};
promedioa([].range(17, 51, 2))

El problema resuelto con el método forEach, es:

var promediob = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
let s = 0;
x.forEach(e=>s+=e);
return s/n;
};
promediob([].range(17, 51, 2))

Como se vio en el anterior capítulo, además del valor del elemento ("e"), la función de forEach (en este caso la función flecha: e=>s+=e) puede recibir el índice y el array, sin embargo, en este ejemplo, no se reciben esos datos, porque no son empleados dentro de la función.

El problema resuelto con el método reduce, es:

var promedioc = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
return x.reduce((s,e)=>s+e)/n;
};
promedioc([].range(17, 51, 2))

En este caso, reduce devuelve la sumatoria, la cual se divide entre el número de elementos para obtener el promedio.

Al igual que la función de forEach y como ocurre con la mayoría de las funciones de los métodos de la clase Array, reduce, puede recibir, además del acumulador, el valor actual del elemento, el índice y el array en sí, pero, en este caso no son recibidos, porque no se requieren en la función.

Cono ya se vio en el anterior capítulo y aunque no es necesario en este caso, se puede asignar también un valor inicial al acumulador "s" de reduce (que, como se tratar de una sumatoria debe ser igual a 0):

Array.prototype.promedioc = function() {
const x = this;
const n = x.length;
if (n===0) throw "vector vacío";
return x.reduce((s,e)=>s+e, 0)/n;
};
[].range(17, 51, 2).promedioc()

El problema resuelto con el método sum, es:

var promediod = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
return x.sum()/n;
};
promediod([].range(17, 51, 2))

Igualmente, el problema puede ser resuelto con el método map:

var promedioe = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
s = 0;
x.map(e=>s+=e);
return s/n;
};
promedioe([].range(17, 51, 2))

Desde el punto de vista de la eficiencia, esta no es la mejor opción, porque map devuelve un nuevo array con el mismo número de elementos que el array original (con los valores intermedios de la sumatoria), sin embargo, en este ejemplo, ese array no es empleado y por esa razón esta solución es menos eficiente (se pierde el tiempo en generar un array que, finalmente, no es empleado).

También se puede emplear el método every:

var promediof = function() {
const n = x.length;
if (n===0) throw "vector vacío";
s = 0;
x.every(e=>(s+=e,true));
return s/n;
};
promedio([].range(17, 51, 2))

Como el método every termina de iterar cuando un resultado es falso y en este caso es necesario que se itere para todos los elementos del array, la función debe devolver, siempre, un valor verdadero (true, 1 o cualquier valor equivalente a verdadero), porque de no ser así, un resultado equivalente a falso (como 0, cuando el primer elemento del array es 0) haría que las iteraciones terminen y el método devuelva el resultado erróneo 0.

Note que, en este ejemplo, para no emplear el comando return, las dos instrucciones de la función flecha (s+=e y true) han sido encerradas entre paréntesis y separadas con una coma (en la forma estándar se escribiría: e=>{s+=e; return true;}). Cuando dos o más instrucciones se escriben entre paréntesis y se separan con comas, son tratadas como si se fueran una sola instrucción Javascript. Las instrucciones dentro de los paréntesis son evaluadas una tras otra, pero, externamente, sólo se toma en cuenta el resultado devuelto por la última instrucción. Así, en este ejemplo, como la última instrucción es el valor booleano true ese es el valor final de las instrucciones escritas entre paréntesis.

Igualmente, se puede emplear el método some (que es el complemento de every):

var promediog = function(x) {
const n = x.length;
if (n===0) throw "vector vacío";
s = 0;
x.some(e=>(s+=e,false));
return s/n;
};
promediog([].range(17, 51, 2))

En este caso, como some termina cuando el resultado es verdadero y se quiere iterar para todos los elementos del array, el resultado de la función debe ser siempre falso (false, 0 o cualquier valor equivalente a falso).

De forma similar se pueden emplear los métodos find, findIndex, findLast, findLastIndex y filter. Sin embargo, es necesario dejar en claro que ninguno de estos métodos es el más adecuado para resolver este tipo de problemas, porque su propósito es otro: Verificar si todos los elementos cumplen una determinada condición (every), si alguno de los elementos cumple una determinada condición (some), verificar si un valor dado es uno de los elementos del array (find y findLast), encontrar la posición de un determinado elemento en el array (findIndex y findLastIndex) y obtener un array con los elementos que cumplen una determinada condición (filter).

En la asignatura se emplean estos métodos, para resolver este tipo de problemas, únicamente para comprender mejor la forma en que operan (su lógica), pero no deben ser empleados, en este tipo de problemas, en una aplicación real.

Vector de números aleatorios

En este ejemplo se elabora un método que genera un vector con "n" números aleatorios comprendidos entre 1 y 1000.

El problema puede ser resuelto fácilmente con el método rand: simplemente se generan "n" números aleatorios comprendidos entre 1 y 1000 y se redondea el vector resultante, tal como se muestra en la siguiente función:

function rand1000(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; return [].rand(n, 1, 1000).round(); };

Llamando a la función con algunos valores de prueba, se obtiene:

randseed = 10;
rand1000(5)
rand1000(10)
rand1000(20)
rand1000(25)
rand1000(-10)
rand1000(5.7)

Si embargo, si no se tiene acceso al método rand, porque no se está trabajando en la calculadora o no se ha importado la librería array.js, el problema puede ser resuelto empleando la estructura for y la función estándar random.

Para transformar el número aleatorio comprendido entre 0 y 1, que es el resultado devuelto por random, en un número comprendido entre "li" y "ls", se aplica la siguiente expresión:

\[ \begin{aligned} \text{Nú}&\text{mero aleatorio comprendido entre li y ls}=\\ &(\text{Número aleatorio comprendido entre 0 y 1})\times (\text{ls}-\text{li})+\text{li} \end{aligned} \]

Entonces, en cada repetición del ciclo for, que va desde 0 hasta "n-1", se genera y guarda un número aleatorio, como se muestra en el siguiente diagrama de flujo:

gojsGraph({divi, modelo: {"class":"go.GraphLinksModel","linkFromPortIdProperty":"fromPort","linkToPortIdProperty":"toPort","nodeDataArray":[{"category":"Start","text":"rand1000","key":-1,"loc":"172 -460"},{"category":"Input","text":"número de elementos a generar: n","key":-3,"loc":"171.99999999999994 -410.4666427612303"},{"category":"Process","text":"v = []","key":-2,"loc":"171.99999999999997 -359.3332138061521"},{"category":"Process","text":"i = i+1","key":-7,"loc":"57.99999999999987 -250.44982967376689"},{"category":"Start","text":"fin","key":-9,"loc":"172.00000000000009 -63.34418347676573"},{"category":"Output","text":"v","key":-10,"loc":"171.99999999999997 -116.22199312845845"},{"category":"For","text":"i<n","key":-6,"loc":"171.99999999999997 -250.44982967376686"},{"category":"Process","text":"i = 0","key":-12,"loc":"171.99999999999997 -304.79985656738245"},{"category":"Process","text":"v[i] = random()*999+1","key":-13,"loc":"171.99999999999997 -181.09980278015115"}],"linkDataArray":[{"from":-10,"to":-9,"fromPort":"B","toPort":"T","points":[171.99999999999997,-103.5419787851967,171.99999999999997,-93.5419787851967,171.99999999999997,-91.07642044067359,171.99999999999997,-91.07642044067359,171.99999999999997,-88.61086209615047,171.99999999999997,-78.61086209615047]},{"from":-1,"to":-3,"fromPort":"B","toPort":"T","points":[172.00000000000006,-444.73332138061534,172.00000000000006,-434.73332138061534,172.00000000000006,-434.73332138061517,171.99999999999997,-434.73332138061517,171.99999999999997,-434.73332138061505,171.99999999999997,-424.73332138061505]},{"from":-2,"to":-12,"fromPort":"B","toPort":"T","points":[171.99999999999997,-342.06653518676734,171.99999999999997,-332.06653518676734,171.99999999999997,-332.06653518676734,171.99999999999997,-332.0665351867674,171.99999999999997,-332.0665351867674,171.99999999999997,-322.0665351867674]},{"from":-12,"to":-6,"fromPort":"B","toPort":"T","points":[171.99999999999997,-287.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-277.53317794799784,171.99999999999997,-267.53317794799784]},{"from":-6,"to":-13,"fromPort":"B","toPort":"T","visible":true,"points":[171.99999999999997,-233.36648139953593,171.99999999999997,-223.36648139953593,171.99999999999997,-215.86648139953593,171.99999999999997,-215.86648139953593,171.99999999999997,-208.36648139953593,171.99999999999997,-198.36648139953593],"text":"Si"},{"from":-13,"to":-7,"fromPort":"L","toPort":"B","points":[93.007453918457,-181.09980278015115,83.007453918457,-181.09980278015115,57.99999999999986,-181.09980278015115,57.99999999999986,-202.14147691726663,57.99999999999986,-223.18315105438214,57.99999999999986,-233.18315105438214]},{"from":-7,"to":-6,"fromPort":"R","toPort":"L","points":[85.8365097045897,-250.44982967376689,95.8365097045897,-250.44982967376689,116.06658172607413,-250.44982967376689,116.06658172607413,-250.44982967376689,136.29665374755857,-250.44982967376689,146.29665374755857,-250.44982967376689]},{"from":-6,"to":-10,"fromPort":"R","toPort":"T","visible":true,"points":[197.70334625244138,-250.44982967376689,207.70334625244138,-249.72491483688344,270,-249.72491483688344,270,-152,171.99999999999997,-152,171.99999999999997,-143.83312416076637,171.99999999999997,-133.83312416076637],"text":"No"},{"from":-3,"to":-2,"fromPort":"B","toPort":"T","points":[171.99999999999997,-396.1999641418455,171.99999999999997,-386.1999641418455,171.99999999999997,-386.1999641418455,171.99999999999997,-386.5998924255369,171.99999999999997,-386.5998924255369,171.99999999999997,-376.5998924255369]}]} })

Siendo el código respectivo:

var rand1000 = function(n) {
if (n%1!==0 || n<0) throw "El número debe ser entero positivo";
const v= [];
for (let i=0; i<n; i++) v[i] = (Math.random()*999+1).round();
return v;
};
randseed = 10;
rand1000(5)
rand1000(10)
rand1000(20)
rand1000(25)

La solución, implementada empleando el método push (en lugar de una asignación directa), es:

var rand1000_ = function(n) {
if (n%1!==0 || n<0) throw "El número debe ser entero positivo";
const v = [];
for (let i=0; i<n; i++) v.push((Math.random()*999+1).round());
return v;
};
randseed = 10;
rand1000_(5)
rand1000_(10)
rand1000_(20)
rand1000_(25)

El problema puede ser resuelto, igualmente, con el método estático Array.from:

var rand1000__ = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; return Array.from({length:n}, ()=>(Math.random()*999+1).round()); };
randseed = 10;
rand1000__(5)
rand1000__(10)
rand1000__(20)
rand1000__(25)

El problema, resuelto con la estructura for-of, es:

var rand1000a = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; const v = []; for (const _ of [].zeros(n)) v.push((Math.random()*999+1).round()); return v; };
randseed = 10;
rand1000a(5)

Como el método for-of itera para cada uno de los elementos de un array y en este caso, ese array no existe, es necesario crear uno con ese número de elementos (en este ejemplo se crea con zeros). Luego se itera para cada uno de los elementos de ese array, pero no se emplea ninguno de esos elementos y por ello son recibidos en el parámetro "_" (que como se explicó, suele emplearse para identificar parámetros/variables, que son recibidos/declarados, pero que no son utilizados). Debido a que for-of, requiere la creación de un array adicional (cuyos elementos no se utilizan), no es la estructura más adecuada para resolver este problema, porque el crear ese array consume tiempo y memoria adicional.

Los "n" elementos del array pueden ser generados con cualquiera de los métodos disponibles: const, range, rand, Array.from, new Array, etc., sin que ello afecte ni la lógica ni el resultado obtenido (porque únicamente es importante que el array tenga el número correcto de elementos).

El problema puede ser resuelto también con el método forEach:

var rand1000b = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; const v = [].zeros(n); v.forEach((_,i)=>v[i]=(Math.random()*999+1).round()); return v;}
randseed = 10;
rand1000b(5)

Al igual que en la solución anterior se genera un array con "n" elementos (que tampoco son utilizados). Luego, con forEach, se itera para cada uno de esos elementos, pero ahora se recibe el índice "i" de cada elemento, para reemplazar ese elemento (v[i]) con el número aleatorio generado. Esta solución es un poco más eficiente que la anterior, porque solo se genera un array, pero, al igual que en el ejemplo anterior, los elementos del array origeneradoginal no son empleados.

El problema resuelto con el método reduce y zeros (para generar el array), es:

var rand1000c = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; return [].zeros(n).reduce(v=>(v.push((Math.random()*999+1).round()),v),[]); };
randseed = 10;
rand1000c(5)

Como no se emplean los elementos del array, en la función solo se recibe el primer parámetro (el acumulador "v") que se convertirá en el array con los números aleatorios. Por esa razón, el acumulador "v" inicia como un vector vacío ([]), luego, en cada iteración, se le añada un número aleatorio y se devuelve array con el nuevo elemento añadido. De esa manera, en la última iteración, reduce devuelve el array con todos los números aleatorios generados.

El problema puede ser resuelto, también, con el método some, aunque, como se explicó previamente, no es la opción correcta para este tipo de problemas:

var rand1000d = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; const v = []; [].zeros(n).some(()=>(v.push((Math.random()*999+1).round()),false)); return v;};
randseed = 10;
rand1000d(5)

Como ocurre con for=of, no se emplean los elementos del array generado (zeros), por lo que la función no recibe nada y para garantizar que el método itere para todos los elementos, la función devuelve siempre un valor false.

De la misma forma, se puede emplear el método every:

var rand1000e = function(n) { if (n%1!==0 || n<0) throw "El número debe ser entero positivo"; const v = []; [].range(n).every(()=>(v.push((Math.random()*999+1).round()),true)); return v; };
randseed = 10;
rand1000e(5)

Que es prácticamente la misma solución que con some, sólo que ahora la función devuelve siempre true y el array se genera con range.

El problema puede ser resuelto, igualmente, con el método map.

var rand1000f = function(n) {
if (n%1!==0 || n<0) throw "El número debe ser entero positivo";
return [].zeros(n).map(()=>(Math.random()*999+1).round());
};
randseed = 10;
rand1000f(5)

Esta solución es similar a la lograda con reduce, en el sentido de que si bien no se emplean los elementos del array generado (el array de ceros), sí se emplea el array para reemplazar esos ceros con los valores aleatorios generados en la función.

Como en casi todos los casos, el problema puede ser resuelto también con find, findIndex, findLast, findLastIndex y filter, aunque, como también se dijo, no son los métodos más adecuados para resolver este tipo de problemas.