Bagaimana cara meringkas elemen-elemen dari vektor C ++?

240

Apa cara yang baik untuk menemukan jumlah semua elemen dalam std::vector?

Misalkan saya memiliki vektor std::vector<int> vectordengan beberapa elemen di dalamnya. Sekarang saya ingin mencari jumlah semua elemen. Apa cara yang berbeda untuk hal yang sama?

Prasoon Saurav
sumber
1
"Berapa banyak"? Betulkah? Itu sepertinya pertanyaan yang terlalu samar. : p Mungkin lebih bermanfaat untuk meminta cara yang baik untuk melakukannya.
jalf
3
Apa maksud Anda ketika Anda mengatakan "fungsinya mirip?" Apakah Anda mencari pengganti std::accumulatedi Boost? (Jika demikian, mengapa?) Apakah Anda mencari fungsi yang melakukan sesuatu yang mirip std::accumulate? (Jika ya, apa?)
James McNellis
4
Jika Anda menginginkan sesuatu yang mirip std::accumulate, mungkin Anda juga ingin itu berbeda dalam beberapa hal (jika tidak, Anda bisa menggunakan std::accumulate); apa perbedaan std::accumulateyang Anda cari?
CB Bailey

Jawaban:

429

Sebenarnya ada beberapa metode.

int sum_of_elems = 0;

C ++ 03

  1. Klasik untuk loop:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. Menggunakan algoritma standar:

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

    Catatan Penting: Jenis argumen terakhir digunakan tidak hanya untuk nilai awal, tetapi untuk jenis hasil juga. Jika Anda meletakkan int di sana, int akan mengakumulasikan bahkan jika vektor telah mengambang. Jika Anda menjumlahkan angka floating-point, ubah 0ke 0.0atau 0.0f(terima kasih kepada nneonneo). Lihat juga solusi C ++ 11 di bawah ini.

C ++ 11 dan lebih tinggi

  1. b. Secara otomatis melacak jenis vektor bahkan jika terjadi perubahan di masa depan:

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

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Menggunakan rentang berbasis untuk loop (terima kasih kepada Roger Pate):

    for (auto& n : vector)
        sum_of_elems += n;
Prasoon Saurav
sumber
6
Tentu saja dalam C ++ 03 yang dapat Anda gunakan std::for_eachdengan functor, hanya dibutuhkan lebih banyak baris kode untuk didefinisikan daripada lambda C ++ 0x.
Ben Voigt
8
Mengapa contoh lambda Anda digunakan for_each? accumulateakan lebih ringkas (bahkan jika itu tidak membutuhkan lambda)
jalf
4
@jalf: maksud Anda benar, saya seharusnya menggunakannya accumulatedi dalam for_eachtetapi bukankah contoh ini berguna (untuk tujuan pembelajaran) karena ini menunjukkan bahwa kita juga dapat memiliki lambdas :-)
Prasoon Saurav
58
Hati-hati dengan accumulate. Jenis argumen terakhir digunakan tidak hanya untuk nilai awal, tetapi untuk jenis hasilnya. Jika Anda meletakkan di intsana, itu akan menumpuk intbahkan jika vektor memiliki float. Hasilnya bisa sedikit salah, dan kompiler akan mengembalikan hasilnya ke float tanpa memberi tahu Anda.
nneonneo
3
Mengapa Anda gunakan for_eachjika Anda miliki accumulate?
juanchopanza
46

Cara termudah adalah dengan menggunakan std:accumuatedari vector<int> A:

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

Prasoon telah menawarkan sejumlah cara yang berbeda (dan bagus) untuk melakukan ini, tidak ada yang perlu diulang di sini. Saya ingin menyarankan pendekatan alternatif untuk kecepatan.

Jika Anda akan melakukan ini sedikit, Anda mungkin ingin mempertimbangkan "sub-classing" vektor Anda sehingga sejumlah elemen dipertahankan secara terpisah ( sebenarnya bukan sub-classing vector yang rapuh karena kurangnya destructor virtual - Saya berbicara lebih banyak tentang kelas yang berisi jumlah dan vektor di dalamnya, has-adaripada is-a, dan menyediakan metode seperti vektor).

Untuk vektor kosong, jumlahnya diatur ke nol. Pada setiap penyisipan ke vektor, tambahkan elemen yang dimasukkan ke jumlah. Pada setiap penghapusan, kurangi. Pada dasarnya, apa pun yang dapat mengubah vektor yang mendasarinya disadap untuk memastikan jumlah dijaga konsisten.

Dengan begitu, Anda memiliki metode O (1) yang sangat efisien untuk "menghitung" jumlah pada setiap saat (hanya mengembalikan jumlah yang saat ini dihitung). Penyisipan dan penghapusan akan memakan waktu sedikit lebih lama saat Anda menyesuaikan total dan Anda harus mempertimbangkan kinerja ini.

Vektor di mana jumlah dibutuhkan lebih sering daripada vektor diubah adalah yang kemungkinan mendapat manfaat dari skema ini, karena biaya perhitungan jumlah diamortisasi atas semua akses. Jelas, jika Anda hanya membutuhkan jumlah setiap jam dan vektor berubah tiga ribu kali per detik, itu tidak akan cocok.

Sesuatu seperti ini sudah cukup:

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

Jelas, itu pseudo-code dan Anda mungkin ingin memiliki lebih banyak fungsi, tetapi itu menunjukkan konsep dasar.

paxdiablo
sumber
7
menarik, tapi hati-hati karena std::vectortidak dimaksudkan untuk subkelas.
Evan Teran
7
Maaf, saya seharusnya lebih jelas - Anda dapat membuat kelas Anda sendiri dengan metode yang sama seperti vektor yang mempertahankan has-avektor di dalamnya, daripada menjadi subkelas yang tepat ( is-a).
paxdiablo
1
Ini bermasalah, kecuali jika Anda menonaktifkan aksesor ke dalam data, termasuk tetapi tidak terbatas pada operator[](int), non-const iterators ...
David Rodríguez - dribeas
1
@paxdiablo Saya percaya David berarti jika data yang disimpan dalam vektor dimanipulasi melalui penggunaan operator [] atau tidak langsung melalui iterator non-const. Nilai pada posisi yang dimanipulasi sekarang akan berbeda yang akan membuat jumlah salah. Tidak ada cara untuk memastikan jumlahnya benar jika kode klien dapat menyimpan referensi yang dapat diubah ke elemen apa pun di dalam vektor "subkelas".
Bret Kuhns
2
Pendekatan ini menyebabkan penalti kinerja untuk operasi vektor dasar.
Basilev
23

Mengapa melakukan penjumlahan ke depan ketika Anda bisa melakukannya mundur ? Diberikan:

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

Kita bisa menggunakan subskrip, menghitung mundur:

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

Kita dapat menggunakan "subskrip," yang dicentang jangkauan, menghitung mundur (untuk berjaga-jaga):

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

Kita dapat menggunakan iterator terbalik dalam for loop:

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

Kita bisa menggunakan forward iterator, iterasi mundur, dalam for loop (oooh, tricky!):

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

Kita dapat menggunakan accumulatedengan iterator terbalik:

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

Kita dapat menggunakan for_eachdengan ekspresi lambda menggunakan iterator terbalik:

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

Jadi, seperti yang Anda lihat, ada banyak cara untuk menjumlahkan vektor ke belakang sama halnya dengan menjumlahkan vektor ke depan, dan beberapa di antaranya jauh lebih menarik dan menawarkan peluang yang jauh lebih besar untuk kesalahan satu per satu.

James McNellis
sumber
36
Dan mengapa tidak juga menggilir melalui vektor dengan menambahkan bilangan prima dengan operator modulus untuk wraparound? :-)
paxdiablo
3
@ paxdiablo Anda hanya benar-benar harus relatif prima v.size().
clstrfsck
1
-1: vector :: size () mengembalikan nilai yang tidak ditandatangani, membuat ekspresi seperti (v.size () - 1) menghasilkan peringatan, atau ladang ranjau dalam kasus terburuk.
Julien Guertault
1
Mengapa jawaban ini ada? Apakah ada keuntungan untuk menjumlahkan mundur, atau Anda hanya trolling?
Lynn
4
@ Lynn: Jika ujung vektor adalah cache panas (dari loop sebelumnya yang maju), maka ya, looping mundur dapat diukur lebih cepat pada CPU Intel x86 saat ini. Juga, menghitung loop-counter turun ke nol dapat menyimpan kompiler instruksi dalam asm, yang bisa signifikan jika tidak membuka gulungannya. Prefetching kadang-kadang bekerja sedikit lebih baik saat looping ke depan, jadi secara umum tidak selalu lebih baik loop ke belakang.
Peter Cordes
15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);
rafak
sumber
Terima kasih atas jawabannya. BTW apa perbedaan antara std :: akumulasi dan boost :: akumulasi dalam kompleksitas waktu?
Prasoon Saurav
1
Kompleksitas waktu adalah sama untuk std dan boost yang menumpuk - linear. Dalam hal ini, boost :: akumulasi hanya lebih mudah untuk diketik daripada mengirim awal dan akhir secara manual. Tidak ada perbedaan nyata.
logam
7
boost::accumulatehanyalah pembungkus di sekitar std::accumulate.
rafak
2
Cara non-boost tidak jauh lebih sulit: #include <numeric>dan std::accumulate(v.begin(), v.end(), (int64_t)0);. Perhatikan bahwa jenis nilai akumulator awal digunakan sebagai jenis akumulator, jadi jika Anda ingin menjumlahkan elemen 8-bit menjadi hasil 64-bit, itulah cara Anda melakukannya.
Peter Cordes
6

Anda juga bisa menggunakan std::valarray<T>seperti ini

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

Beberapa mungkin tidak menemukan cara ini efisien karena ukuran valarraykebutuhan harus sebesar ukuran vektor dan inisialisasi valarrayjuga akan memakan waktu.

Dalam hal itu, jangan gunakan dan anggap sebagai cara lain untuk menyimpulkan urutannya.

Bintang neutron
sumber
5

C ++ 0x saja:

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

Ini mirip dengan BOOST_FOREACH yang disebutkan di tempat lain dan memiliki manfaat kejelasan yang sama dalam situasi yang lebih kompleks, dibandingkan dengan functor stateful yang digunakan dengan akumulasi atau for_each.


sumber
3
Jika Anda mengubah for (int n : v) sum += n;ke for (auto n : v) sum += n;itu akan bekerja dengan vektor Template. Saya tahu OP merujuk ke vektor <int>, tetapi cara ini sedikit lebih umum :-)
Jonas
5

Saya pengguna Perl, permainan yang kami miliki adalah menemukan setiap cara berbeda untuk meningkatkan variabel ... itu tidak terlalu berbeda di sini. Jawaban untuk berapa banyak cara untuk menemukan jumlah elemen vektor di C ++ mungkin an infinity...

2 sen saya:

Menggunakan BOOST_FOREACH, untuk mendapatkan sintaks iterator jelek:

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

iterasi pada indeks (sangat mudah dibaca).

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

Yang lain ini destruktif, mengakses vektor seperti tumpukan:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}
Kriss
sumber
Mengapa Anda mengatakan iterasi pada indeks tidak efisien? Apa dasar Anda mengatakan itu?
bobobobo
@obobobo: yah, tidak efisien mungkin berlebihan. Anda berdua harus menghitung posisi data yang efektif dari penghitung vektor dan kenaikan tetapi salah satu dari dua operasi ini sudah cukup, tetapi biaya iterator dereferencing bahkan mungkin lebih buruk. Karenanya saya akan menghapus kata.
Kriss
Kompilator pengoptimalisasi dapat mengoptimalkan variabel indeks dan hanya menggunakan peningkatan penunjuk jika diinginkan. (Ini dapat membuat kondisi loop-exit menjadi perbandingan-pointer terhadap start + length). Iterator yang sebenarnya juga harus mengoptimalkan sepenuhnya. Ingat, ini bukan perl; itu sepenuhnya dikompilasi ke asm, tidak diartikan.
Peter Cordes
-1

Saya menemukan cara termudah untuk menemukan jumlah semua elemen vektor

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

}

Dalam program ini, saya memiliki vektor ukuran 10 dan diinisialisasi dengan 1. Saya telah menghitung jumlah dengan loop sederhana seperti dalam array.

Ravi Kumar Yadav
sumber
1
Menggunakan intindeks a std::vectorsecara umum tidak aman. v.size()mungkin lebih besar dari nilai tertinggi yang dapat disimpan int(perhatikan bahwa dengan beberapa platform & kompiler target, size_of(int) < size_of(size_t)). Dalam hal ini, kode Anda akan meluap indeks. std :: vector <T> :: size_type harus dipilih.
Vincent Saulue-Laborde
Ini adalah contoh @ VincentSaulue-Laborde Anda dapat mengambil tipe data dan ukurannya sesuai dengan kebutuhan Anda.
Ravi Kumar Yadav
-5

Ini mudah. C ++ 11 menyediakan cara mudah untuk meringkas elemen vektor.

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