¿Cómo resumir elementos de un vector de C++?
¿ Cuáles son las buenas formas de encontrar la suma de todos los elementos en a std::vector
?
Supongamos que tengo un vector std::vector<int> vector
con algunos elementos. Ahora quiero encontrar la suma de todos los elementos. ¿Cuáles son las diferentes formas de lograr lo mismo?
En realidad existen bastantes métodos.
int sum_of_elems = 0;
C++03
Clásico para bucle:
for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it) sum_of_elems += *it;
Usando un algoritmo estándar:
#include <numeric> sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);
Nota importante: el tipo del último argumento se utiliza no sólo para el valor inicial, sino también para el tipo del resultado . Si coloca un int allí, acumulará int incluso si el vector tiene flotación. Si está sumando números de punto flotante, cambie
0
a0.0
o0.0f
( gracias a nneonneo ). Consulte también la solución C++11 a continuación.
C++11 y superior
b. Realizar un seguimiento automático del tipo de vector incluso en caso de cambios futuros:
#include <numeric> sum_of_elems = std::accumulate(vector.begin(), vector.end(), decltype(vector)::value_type(0));
Usando
std::for_each
:std::for_each(vector.begin(), vector.end(), [&] (int n) { sum_of_elems += n; });
Usando un bucle for basado en rango ( gracias a Roger Pate ):
for (auto& n : vector) sum_of_elems += n;
C ++ 17 y superior
El uso
std::reduce
de which también se ocupa del tipo de resultado, por ejemplo, si tienestd::vector<int>
, obtendráint
el resultado. Si lo tienesstd::vector<float>
, obtienesfloat
. O si lo tienestd::vector<std::string>
, obtienestd::string
(todas las cadenas concatenadas). Interesante, ¿no?auto result = std::reduce(v.begin(), v.end());
Hay otras sobrecargas de esta función que puedes ejecutar incluso en paralelo, en caso de que tengas una colección grande y quieras obtener el resultado rápidamente.
La forma más sencilla es utilizar std:accumulate
un vector<int> A
:
#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);
Prasoon ya ha ofrecido una serie de formas diferentes (y buenas) de hacer esto, ninguna de las cuales necesita repetirse aquí. Sin embargo, me gustaría sugerir un enfoque alternativo para la velocidad.
Si va a hacer esto con bastante frecuencia, es posible que desee considerar "subclasificar" su vector para que una suma de elementos se mantenga por separado (en realidad no subclasificar el vector, lo cual es dudoso debido a la falta de un destructor virtual: estoy hablando más de una clase que contiene la suma y un vector dentro de ella, has-a
en lugar de is-a
y proporciona métodos similares a vectores).
Para un vector vacío, la suma se establece en cero. En cada inserción en el vector, agregue el elemento que se inserta a la suma. En cada eliminación, réstalo. Básicamente, cualquier cosa que pueda cambiar el vector subyacente se intercepta para garantizar que la suma se mantenga constante.
De esa manera, tendrá un método O(1) muy eficiente para "calcular" la suma en cualquier momento (simplemente devuelva la suma calculada actualmente). La inserción y eliminación tardarán un poco más a medida que ajuste el total y debe tener en cuenta este impacto en el rendimiento.
Los vectores en los que se necesita la suma con más frecuencia de la que se cambia el vector son los que probablemente se beneficiarán de este esquema, ya que el costo de calcular la suma se amortiza en todos los accesos. Obviamente, si solo necesitas la suma cada hora y el vector cambia tres mil veces por segundo, no será adecuado.
Algo como esto sería suficiente:
class UberVector:
private Vector<int> vec
private int sum
public UberVector():
vec = new Vector<int>()
sum = 0
public getSum():
return sum
public add (int val):
rc = vec.add (val)
if rc == OK:
sum = sum + val
return rc
public delindex (int idx):
val = 0
if idx >= 0 and idx < vec.size:
val = vec[idx]
rc = vec.delindex (idx)
if rc == OK:
sum = sum - val
return rc
Obviamente, eso es pseudocódigo y es posible que desees tener un poco más de funcionalidad, pero muestra el concepto básico.
¿Por qué realizar la suma hacia adelante cuando puedes hacerlo hacia atrás ? Dado:
std::vector<int> v; // vector to be summed
int sum_of_elements(0); // result of the summation
Podemos usar subíndices, contando hacia atrás:
for (int i(v.size()); i > 0; --i)
sum_of_elements += v[i-1];
Podemos utilizar "subíndices" de rango comprobado, contando hacia atrás (por si acaso):
for (int i(v.size()); i > 0; --i)
sum_of_elements += v.at(i-1);
Podemos usar iteradores inversos en un bucle for:
for(std::vector<int>::const_reverse_iterator i(v.rbegin()); i != v.rend(); ++i)
sum_of_elements += *i;
Podemos usar iteradores hacia adelante, iterando hacia atrás, en un bucle for (¡oooh, complicado!):
for(std::vector<int>::const_iterator i(v.end()); i != v.begin(); --i)
sum_of_elements += *(i - 1);
Podemos usar accumulate
con iteradores inversos:
sum_of_elems = std::accumulate(v.rbegin(), v.rend(), 0);
Podemos usar for_each
una expresión lambda usando iteradores inversos:
std::for_each(v.rbegin(), v.rend(), [&](int n) { sum_of_elements += n; });
Entonces, como puede ver, hay tantas formas de sumar el vector hacia atrás como de sumar el vector hacia adelante, y algunas de ellas son mucho más interesantes y ofrecen muchas más oportunidades de cometer errores uno por uno.
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);