Como resumir elementos de um vetor C ++?

240

Quais são as boas maneiras de encontrar a soma de todos os elementos em a std::vector?

Suponha que eu tenha um vetor std::vector<int> vectorcom alguns elementos. Agora eu quero encontrar a soma de todos os elementos. Quais são as diferentes maneiras para o mesmo?

Prasoon Saurav
fonte
1
"Quantos"? Realmente? Essa parece uma pergunta excessivamente vaga. : p Pode ser mais útil pedir uma boa maneira de fazê-lo.
jalf
3
O que você quer dizer quando diz "função semelhante a"? Você está procurando um substituto std::accumulateno Boost? (Se sim, por quê?) Você está procurando funções que façam algo semelhante std::accumulate? (Se sim, qual?)
James McNellis
4
Se você deseja algo semelhante std::accumulate, presumivelmente também deseja que seja diferente em algum aspecto (caso contrário, você pode simplesmente usar std::accumulate); que diferença (s) std::accumulatevocê está procurando?
CB Bailey

Respostas:

429

Na verdade, existem alguns métodos.

int sum_of_elems = 0;

C ++ 03

  1. Clássico para loop:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. Usando um algoritmo padrão:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(), 0);

    Nota importante: O tipo do último argumento é usado não apenas para o valor inicial, mas também para o tipo do resultado . Se você colocar um int lá, ele acumulará ints, mesmo que o vetor tenha flutuado. Se você estiver somando números de ponto flutuante, altere 0para 0.0ou 0.0f(graças a nneonneo). Veja também a solução C ++ 11 abaixo.

C ++ 11 e superior

  1. b. Acompanhar automaticamente o tipo de vetor, mesmo em caso de alterações futuras:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                   decltype(vector)::value_type(0));
  2. Usando std::for_each:

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Usando um loop for baseado em intervalo (graças a Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
fonte
6
É claro que no C ++ 03 você pode usar std::for_eachcom um functor, basta levar mais linhas de código para definir do que o lambda C ++ 0x.
Ben Voigt
8
Por que seus exemplos lambda usam for_each? accumulateseria mais conciso (mesmo que não precisa do lambda)
jalf
4
@jalf: seu ponto é correto, eu deveria ter usado accumulatedentro for_each, mas não é este exemplo útil (para a aprendizagem de propósito), pois mostra que também pode ter lambdas aninhados :-)
Prasoon Saurav
58
Tenha cuidado com accumulate. O tipo do último argumento é usado não apenas para o valor inicial, mas para o tipo do resultado. Se você colocar um intlá, ele acumulará ints, mesmo que o vetor tenha float. O resultado pode estar sutilmente errado, e o compilador converterá o resultado em um float sem avisar.
Nneonneo
3
Por que você usaria for_eachse tiver accumulate?
Juanchopanza
46

A maneira mais fácil é usar std:accumuateum vector<int> A:

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

Prasoon já ofereceu várias maneiras diferentes (e boas) de fazer isso, nenhuma das quais precisa ser repetida aqui. Eu gostaria de sugerir uma abordagem alternativa para a velocidade, no entanto.

Se você estiver fazendo isso um pouco, considere "subclassificar" seu vetor para que uma soma de elementos seja mantida separadamente ( na verdade, não um vetor de subclasse que é duvidoso devido à falta de um destruidor virtual - estou falando mais de uma classe que contém a soma e um vetor dentro dela, has-ae não is-ae fornece os métodos semelhantes a vetores).

Para um vetor vazio, a soma é definida como zero. Em todas as inserções no vetor, adicione o elemento que está sendo inserido na soma. Em cada exclusão, subtraia-o. Basicamente, qualquer coisa que possa alterar o vetor subjacente é interceptada para garantir que a soma seja mantida consistente.

Dessa forma, você tem um método O (1) muito eficiente para "calcular" a soma a qualquer momento (basta retornar a soma calculada atualmente). A inserção e exclusão levarão um pouco mais de tempo enquanto você ajusta o total e você deve levar em consideração esse desempenho atingido.

Os vetores em que a soma é necessária com mais frequência do que o vetor é alterado são os que mais se beneficiam com esse esquema, uma vez que o custo do cálculo da soma é amortizado em todos os acessos. Obviamente, se você precisar da soma a cada hora e o vetor mudar três mil vezes por segundo, isso não será adequado.

Algo assim seria 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, isso é pseudo-código e você pode querer ter um pouco mais de funcionalidade, mas mostra o conceito básico.

paxdiablo
fonte
7
interessante, mas tenha cuidado, pois std::vectornão se destina a subclasses.
Evan Teran
7
Desculpe, eu deveria ter sido mais claro - você pode criar sua própria classe com os mesmos métodos que o vetor, que mantém um has-avetor dentro dela, em vez de ser uma subclasse adequada ( is-a).
21410
1
Isso é problemático a menos que você desativar os acessores para os dados, incluindo mas não limitado a operator[](int), iterators não-const ...
David Rodríguez - dribeas
1
@paxdiablo Creio que David quer dizer que se os dados armazenados no vetor forem manipulados através do uso do operador [] ou indiretamente através de um iterador não constante. O valor na posição manipulada agora será diferente, o que tornará a soma incorreta. Não há como garantir que a soma esteja correta se o código do cliente conseguir manter uma referência mutável a qualquer elemento dentro do vetor "subclasse".
Bret Kuhns
2
Essa abordagem causa penalidades de desempenho para operações vetoriais básicas.
Basilevs
23

Por que executar a soma para a frente quando você pode fazer isso para trás ? Dado:

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

Podemos usar a assinatura, contando ao contrário:

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

Podemos usar a "assinatura" com intervalo verificado, contando para trás (apenas no caso):

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

Podemos usar iteradores reversos em um loop for:

for(std::vector<int>::const_reverse_iterator i(v.rbegin()); i != v.rend(); ++i)
    sum_of_elements += *i;

Podemos usar iteradores para frente, iterando para trás, em um loop for (oooh, complicado!):

for(std::vector<int>::const_iterator i(v.end()); i != v.begin(); --i)
    sum_of_elements += *(i - 1);

Podemos usar accumulatecom iteradores reversos:

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

Podemos usar for_eachcom uma expressão lambda usando iteradores reversos:

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

Portanto, como você pode ver, existem tantas maneiras de somar o vetor para trás quanto de somar o vetor para frente, e algumas delas são muito mais empolgantes e oferecem uma oportunidade muito maior para erros isolados.

James McNellis
fonte
36
E por que não também percorrer o vetor adicionando um número primo ao operador de módulo para envolvê-lo? :-)
paxdiablo
3
@ paxdiablo Você realmente só precisa ser relativamente privilegiado v.size().
Clstrfsck
1
-1: vector :: size () retorna um valor não assinado, fazendo expressões como (v.size () - 1) gerar avisos ou um campo minado nos piores casos.
Julien Guertault
1
Por que essa resposta existe? Existe uma vantagem em resumir ao contrário, ou você está apenas trollando?
Lynn
4
@ Lynn: Se o final do vetor estiver quente no cache (de um loop anterior que avançou), sim, o loop inverso pode ser mensurável mais rapidamente nas CPUs Intel x86 atuais. Além disso, contar um contador de loop até zero pode salvar o compilador de uma instrução no asm, que pode ser significativa se não desenrolar o loop. Às vezes, a pré-busca funciona um pouco melhor ao fazer o loop para a frente; portanto, em geral, não é melhor sempre fazer um loop para trás.
Peter Cordes
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
rafak
fonte
Obrigado pela resposta. BTW, qual é a diferença entre std :: acumulate e boost :: acumulate na complexidade do tempo?
Prasoon Saurav
1
A complexidade do tempo é a mesma para o std e o boost de acumular - linear. Nesse caso, boost :: acumulate é apenas mais fácil de digitar do que enviar o início e o final manualmente. Não há diferença real.
metal
7
boost::accumulateé apenas um invólucro std::accumulate.
Rafak
2
O caminho sem impulso não é muito mais difícil: #include <numeric>e std::accumulate(v.begin(), v.end(), (int64_t)0);. Observe que o tipo do valor inicial do acumulador é usado como o tipo de acumulador; portanto, se você deseja somar elementos de 8 bits em um resultado de 64 bits, é assim que você faz.
Peter Cordes
6

Pode-se também usar std::valarray<T>assim

#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;
}

Alguns podem não achar esse caminho eficiente, pois o tamanho das valarraynecessidades precisa ser tão grande quanto o tamanho do vetor e a inicialização valarraytambém levará tempo.

Nesse caso, não o use e tome-o como mais uma maneira de resumir a sequência.

Estrêla de Neutróns
fonte
5

Apenas C ++ 0x:

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

Isso é semelhante ao BOOST_FOREACH mencionado em outro lugar e tem o mesmo benefício de clareza em situações mais complexas, em comparação com os funcores com estado usados ​​com acumular ou for_each.


fonte
3
Se você mudar for (int n : v) sum += n;para for (auto n : v) sum += n;ele, funcionará com qualquer modelo de vetor. I conhecida referes op para vector <int>, mas desta forma é ligeiramente mais geral :-)
Jonas
5

Eu sou um usuário Perl, um jogo que temos é encontrar todas as maneiras diferentes de incrementar uma variável ... isso não é realmente diferente aqui. A resposta para quantas maneiras de encontrar a soma dos elementos de um vetor em C ++ é provavelmente an infinity...

Meus 2 centavos:

Usando BOOST_FOREACH, para se livrar da sintaxe feia do iterador:

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

iterando nos índices (realmente fáceis de ler).

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

Este outro é destrutivo, acessando o vetor como uma pilha:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}
kriss
fonte
Por que você diz que iterar nos índices é ineficiente? Qual é a sua base para dizer isso?
precisa saber é o seguinte
@obobobo: bem, ineficiente é provavelmente excessivo. Você precisa calcular a posição efetiva dos dados a partir do vetor e do contador de incremento, mas uma dessas duas operações deve ser suficiente, mas o custo de desreferenciar os iteradores pode ser ainda pior. Por isso, removerei a palavra.
kriss
Um compilador otimizador pode otimizar a variável de índice e usar apenas um incremento de ponteiro, se desejar. (Pode fazer com que a condição de saída do loop seja uma comparação de ponteiro start + length). Os iteradores reais também devem otimizar completamente. Lembre-se, não é perl; é totalmente compilado para asm, não interpretado.
Peter Cordes
-1

Encontrei a maneira mais fácil de encontrar a soma de todos os elementos de um vetor

#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;

}

Neste programa, eu tenho um vetor do tamanho 10 e sou inicializado por 1. Calculei a soma por um loop simples, como na matriz.

Ravi Kumar Yadav
fonte
1
Usar intpara indexar a std::vectornão é seguro em geral. v.size()pode ser maior que o valor mais alto que pode ser armazenado int(observe que em algumas plataformas e compiladores de destino size_of(int) < size_of(size_t)). Nesse caso, seu código irá estourar o índice. std :: vector <T> :: size_type deve ser preferido.
Vincent Saulue-Laborde
É um exemplo @ VincentSaulue-Laborde Você pode usar o tipo e o tamanho dos dados de acordo com sua necessidade.
Ravi Kumar Yadav
-5

Isso é fácil. O C ++ 11 fornece uma maneira fácil de resumir elementos de um vetor.

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