¿Cómo resumir elementos de un vector C ++?

240

¿Cuáles son las buenas maneras de encontrar la suma de todos los elementos en a std::vector?

Supongamos que tengo un vector std::vector<int> vectorcon algunos elementos. Ahora quiero encontrar la suma de todos los elementos. ¿Cuáles son las diferentes formas de lo mismo?

Prasoon Saurav
fuente
1
"Cuántos"? De Verdad? Esa parece una pregunta demasiado vaga. : p Podría ser más útil pedir una buena forma de hacerlo.
jalf
3
¿Qué quieres decir cuando dices "función similar a"? ¿Estás buscando un reemplazo std::accumulateen Boost? (Si es así, ¿por qué?) ¿Está buscando funciones que hagan algo similar std::accumulate? (Si es así, ¿qué?)
James McNellis
44
Si quiere algo similar std::accumulate, presumiblemente también quiere que sea diferente en algún aspecto (de lo contrario, podría usarlo std::accumulate); ¿Qué diferencia (s) std::accumulateestás buscando?
CB Bailey

Respuestas:

429

En realidad, hay bastantes métodos.

int sum_of_elems = 0;

C ++ 03

  1. Clásico para loop:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. 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 usa no solo para el valor inicial, sino también para el tipo del resultado . Si coloca un int allí, acumulará entradas incluso si el vector tiene flotación. Si está sumando números de coma flotante, cambie 0a 0.0o 0.0f(gracias a nneonneo). Vea también la solución C ++ 11 a continuación.

C ++ 11 y superior

  1. si. 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));
  2. Utilizando std::for_each:

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Uso de un bucle for basado en rango (gracias a Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
fuente
66
Por supuesto, en C ++ 03 se puede usar std::for_eachcon un functor, solo se necesitan más líneas de código para definir que el lambda C ++ 0x.
Ben Voigt
8
¿Por qué usan tus ejemplos lambda for_each? accumulatesería más conciso (incluso si no necesita la lambda)
jalf
44
@jalf: su punto es correcto, debería haberlo usado accumulatedentro, for_eachpero este ejemplo no es útil (con fines de aprendizaje), ya que muestra que también podemos tener lambdas anidadas :-)
Prasoon Saurav
58
Tenga cuidado con el accumulate. El último tipo de argumento se usa no solo para el valor inicial, sino también para el tipo del resultado. Si pones un intallí, se acumulará ints incluso si el vector tiene float. El resultado puede ser sutilmente incorrecto, y el compilador volverá a generar el resultado en un flotante sin decírselo.
nneonneo
3
¿Por qué usarías for_eachsi tienes accumulate?
juanchopanza
46

La forma más fácil es usar std:accumuateun vector<int> A:

#include <numeric>
cout << accumulate(A.begin(), A.end(), 0);
Beahacker
fuente
34

Prasoon ya ha ofrecido una gran cantidad 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 un poco, es posible que desee considerar "subclasificar" su vector para que una suma de elementos se mantenga por separado (en realidad no es un subclasificación de vectores que 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, en has-alugar de is-a, y proporciona los métodos de tipo vector).

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, restarlo. Básicamente, cualquier cosa que pueda cambiar el vector subyacente se intercepta para garantizar que la suma se mantenga constante.

De esa manera, tiene un método O (1) muy eficiente para "calcular" la suma en cualquier momento (solo devuelva la suma calculada actualmente). La inserción y la eliminación tardarán un poco más a medida que ajuste el total y debe tener en cuenta este éxito de rendimiento.

Los vectores en los que la suma se necesita con más frecuencia que el vector que se cambia 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 necesita 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, ese es un seudocódigo y es posible que desee tener un poco más de funcionalidad, pero muestra el concepto básico.

paxdiablo
fuente
77
interesante, pero tenga cuidado ya std::vectorque no está destinado a subclasificar.
Evan Teran
77
Lo siento, debería haber sido más claro: podría crear su propia clase con los mismos métodos que vector que mantiene un has-avector dentro de ella, en lugar de ser una subclase adecuada ( is-a).
paxdiablo
1
Esto es problemático a menos que desactive los accesos en los datos, incluidos, entre otros operator[](int), los iteradores no constantes ...
David Rodríguez - dribeas
1
@paxdiablo Creo que David quiere decir si los datos almacenados en el vector se manipulan mediante el uso del operador [] o indirectamente a través de un iterador no constante. El valor en la posición manipulada ahora será diferente, lo que hará que la suma sea incorrecta. No hay forma de asegurar que la suma sea correcta si el código del cliente puede contener una referencia mutable a cualquier elemento dentro del vector "subclasificado".
Bret Kuhns
2
Este enfoque causa una penalización de rendimiento para operaciones básicas de vectores.
Basilevs
23

¿Por qué realizar la suma hacia adelante cuando puedes hacerlo al revés ? Dado:

std::vector<int> v;     // vector to be summed
int sum_of_elements(0); // result of the summation

Podemos usar suscripciones, contando hacia atrás:

for (int i(v.size()); i > 0; --i)
    sum_of_elements += v[i-1];

Podemos usar "suscripciones" controladas por rango, 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 ciclo 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 accumulatecon iteradores inversos:

sum_of_elems = std::accumulate(v.rbegin(), v.rend(), 0);

Podemos usar for_eachcon una expresión lambda usando iteradores inversos:

std::for_each(v.rbegin(), v.rend(), [&](int n) { sum_of_elements += n; });

Por lo tanto, 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 emocionantes y ofrecen muchas más oportunidades para errores fuera de uno.

James McNellis
fuente
36
¿Y por qué no también recorrer el vector agregando un número primo con el operador de módulo para wraparound? :-)
paxdiablo
3
@paxdiablo Solo necesitas ser relativamente primo v.size().
clstrfsck
1
-1: vector :: size () devuelve un valor sin signo, haciendo que expresiones como (v.size () - 1) generen advertencias o un campo minado en el peor de los casos.
Julien Guertault
1
¿Por qué existe esta respuesta? ¿Hay una ventaja en sumar al revés, o solo estás trolleando?
Lynn
44
@ Lynn: Si el final del vector está caliente en la memoria caché (desde un bucle anterior que fue hacia adelante), entonces sí, el bucle hacia atrás puede ser mucho más rápido en las CPU Intel x86 actuales. Además, contar un contador de bucles hasta cero puede ahorrarle al compilador una instrucción en el asm, que puede ser importante si no desenrolla el bucle. Sin embargo, la captación previa a veces funciona un poco mejor cuando se realiza un bucle hacia adelante, por lo que, en general, no es mejor hacerlo siempre hacia atrás.
Peter Cordes
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
rafak
fuente
Gracias por la respuesta. Por cierto, ¿cuál es la diferencia entre std :: acumular y aumentar :: acumular en la complejidad del tiempo?
Prasoon Saurav
1
La complejidad del tiempo es la misma para la acumulación de std y boost: lineal. En este caso, boost :: acumular es más fácil de escribir que enviar en el principio y el final de forma manual. No hay diferencia real.
metal
77
boost::accumulatees solo una envoltura std::accumulate.
rafak
2
La forma sin impulso no es mucho más difícil: #include <numeric>y std::accumulate(v.begin(), v.end(), (int64_t)0);. Observe que el tipo del valor del acumulador inicial se usa como el tipo de acumulador, por lo que si desea sumar elementos de 8 bits en un resultado de 64 bits, así es como lo hace.
Peter Cordes
6

También se puede usar std::valarray<T>así

#include<iostream>
#include<vector>
#include<valarray>

int main()
{
    std::vector<int> seq{ 1,2,3,4,5,6,7,8,9,10 };
    std::valarray<int> seq_add{ seq.data(), seq.size() };
    std::cout << "sum = " << seq_add.sum() << "\n";

    return 0;
}

Algunos pueden no encontrar esta manera eficiente ya que el tamaño de las valarraynecesidades debe ser tan grande como el tamaño del vector y la inicialización valarraytambién llevará tiempo.

En ese caso, no lo use y tómelo como otra forma de resumir la secuencia.

Estrella neutrón
fuente
5

Solo C ++ 0x:

vector<int> v; // and fill with data
int sum {}; // or = 0 ... :)
for (int n : v) sum += n;

Esto es similar al BOOST_FOREACH mencionado en otra parte y tiene el mismo beneficio de claridad en situaciones más complejas, en comparación con los functores con estado utilizados con acumular o para cada uno.


fuente
3
Si te cambias for (int n : v) sum += n;a for (auto n : v) sum += n;él, funcionará con cualquier plantilla de vector. Sabía que OP se refiere al vector <int>, pero de esta manera es un poco más general :-)
Jonas
5

Soy un usuario de Perl, un juego que tenemos es encontrar diferentes formas de incrementar una variable ... eso no es realmente diferente aquí. La respuesta a cuántas formas de encontrar la suma de los elementos de un vector en C ++ es probablemente an infinity...

Mis 2 centavos:

Usando BOOST_FOREACH, para liberarse de la sintaxis del iterador feo:

sum = 0;
BOOST_FOREACH(int & x, myvector){
  sum += x;
}

iterando sobre índices (realmente fácil de leer).

int i, sum = 0;
for (i=0; i<myvector.size(); i++){
  sum += myvector[i];
}

Este otro es destructivo, accediendo al vector como una pila:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}
kriss
fuente
¿Por qué dice que iterar en índices es ineficiente? ¿Cuál es tu base para decir eso?
bobobobo
@bobobobo: bueno, ineficiente es probablemente excesivo. Tiene que calcular la posición efectiva de los datos desde el vector y el contador de incrementos, pero una de estas dos operaciones debería ser suficiente, pero el costo de desreferenciar iteradores puede ser incluso peor. Por lo tanto, eliminaré la palabra.
kriss
Un compilador de optimización puede optimizar la variable de índice y simplemente usar un incremento de puntero si así lo desea. (Puede hacer que la condición de salida de bucle sea una comparación de puntero contra start + length). Los iteradores reales también deberían optimizarse por completo. Recuerda, no es perl; está completamente compilado a asm, no interpretado.
Peter Cordes
-1

Encontré la forma más fácil de encontrar la suma de todos los elementos de un vector

#include <iostream>
#include<vector>
using namespace std;

int main()
{
    vector<int>v(10,1);
    int sum=0;
    for(int i=0;i<v.size();i++)
    {
        sum+=v[i];
    }
    cout<<sum<<endl;

}

En este programa, tengo un vector de tamaño 10 y se inicializan en 1. He calculado la suma mediante un bucle simple como en una matriz.

Ravi Kumar Yadav
fuente
1
Usar intpara indexar a std::vectorno es seguro en general. v.size()puede ser mayor que el valor más alto que se puede almacenar int(tenga en cuenta que con algunas plataformas y compiladores de destino size_of(int) < size_of(size_t)). En este caso, su código desbordará el índice. Se debe preferir std :: vector <T> :: size_type .
Vincent Saulue-Laborde
Es un ejemplo @ VincentSaulue-Laborde Puede tomar el tipo de datos y su tamaño según sus requisitos.
Ravi Kumar Yadav
-5

Es fácil. C ++ 11 proporciona una manera fácil de resumir elementos de un vector.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Haresh karnan
fuente