Bir C ++ vektörünün elemanlarını nasıl özetleyebilirim?


240

A'daki tüm öğelerin toplamını bulmanın iyi yolları nelerdir std::vector?

std::vector<int> vectorİçinde birkaç element bulunan bir vektörüm olduğunu varsayalım . Şimdi tüm elementlerin toplamını bulmak istiyorum. Aynı şeyin farklı yolları nelerdir?


1
"Kaç"? Gerçekten mi? Bu aşırı belirsiz bir soru gibi görünüyor. : p Bunu yapmanın iyi bir yolunu sormak daha yararlı olabilir.
jalf

3
"Benzer fonksiyon" derken ne demek istiyorsun? std::accumulateBoost için bir yedek mi arıyorsunuz ? (Öyleyse, neden?) Benzer bir şey yapan işlevler mi arıyorsunuz std::accumulate? (Öyleyse, ne?)
James McNellis

4
Benzer bir şey istiyorsanız std::accumulate, muhtemelen bir açıdan farklı olmasını da istersiniz (aksi takdirde sadece kullanabilirsiniz std::accumulate); hangi farkları std::accumulatearıyorsunuz?
CB Bailey

Yanıtlar:


429

Aslında birkaç yöntem var.

int sum_of_elems = 0;

C ++ 03

  1. Döngü için klasik:

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

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

    Önemli Not: Son bağımsız değişkenin türü yalnızca başlangıç ​​değeri için değil , sonucun türü için de kullanılır. Buraya bir int koyarsanız, vektör yüzer olsa bile ints biriktirir. Eğer kayan noktalı sayılar, değişim toplanmasıyla varsa 0etmek 0.0veya 0.0f(nneonneo sayesinde). Ayrıca aşağıdaki C ++ 11 çözümüne bakın.

C ++ 11 ve üstü

  1. b. Gelecekteki değişikliklerde bile vektör türünü otomatik olarak takip etme:

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

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Döngü için aralık tabanlı kullanma (Roger Pate sayesinde):

    for (auto& n : vector)
        sum_of_elems += n;

6
Tabii ki C ++ 03 std::for_eachbir functor ile kullanabilirsiniz , sadece tanımlamak için daha fazla kod satırı C ++ 0x lambda daha alır.
Ben Voigt

8
Lambda örnekleriniz neden kullanılıyor for_each? accumulatedaha fazla özlü olurdu (
lambdaya

4
@jalf: puanınızı Kendimi kullanılmış olmalıdır doğrudur accumulateiçini for_eachama :-) biz de iç içe lambdas olabileceğini gösterdiği gibi (amacını öğrenme için) yararlı bu örnek değil
Prasoon Saurav

58
Dikkatli olun ile accumulate. Son bağımsız değişkenin türü yalnızca başlangıç ​​değeri için değil, sonucun türü için de kullanılır. Eğer intoraya koyarsanız int, vektör olsa bile s biriktirecektir float. Sonuç oldukça yanlış olabilir ve derleyici sonucu size bir şamandıra çevirir.
nneonneo

3
for_eachVarsa neden kullanasın ki accumulate?
juanchopanza

46

En kolay yolu kullanmaktır std:accumuatea vector<int> A:

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

34

Prasoon zaten bunu yapmanın farklı (ve iyi) yollarını teklif etti, bunların hiçbirinin burada tekrarlanması gerekmez. Ancak hız için alternatif bir yaklaşım önermek istiyorum.

Bunu biraz yapacaksanız, vektörünüzü "alt sınıflandırma" olarak değerlendirmek isteyebilirsiniz, böylece bir dizi öğe ayrı ayrı korunur ( aslında bir eksiklik nedeniyle iffy olan alt sınıflandırma vektörü değil) sanal yıkıcı - Ben toplamını ve içindeki bir vektör içeren bir sınıfın daha bahsediyorum has-aziyadeis-a ) ve vektör benzeri yöntemlerle sağlar.

Boş bir vektör için, toplam sıfıra ayarlanır. Vektöre yapılan her eklemede, eklenen öğeyi toplama ekleyin. Her silme işleminde çıkartın. Temel olarak, toplam vektörü değiştirebilecek her şey , toplamın tutarlı kalmasını sağlamak için durdurulur.

Bu şekilde, toplamı herhangi bir zamanda "hesaplamak" için çok verimli bir O (1) yönteminiz olur (sadece hesaplanan toplamı döndürün). Ekleme ve silme, toplamı ayarladıkça biraz daha uzun sürer ve bu performans isabetini dikkate almanız gerekir.

Toplamın vektöre göre daha sık ihtiyaç duyulduğu vektörler, bu şemadan yararlanma olasılığı yüksek olanlardır, çünkü toplamı hesaplama maliyeti tüm erişimlerde amortismana tabi tutulur. Açıkçası, sadece saatte bir toplama ihtiyacınız varsa ve vektör saniyede üç bin kez değişiyorsa, uygun olmayacaktır.

Böyle bir şey yeterli olacaktır:

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

Açıkçası, bu sahte kod ve biraz daha fazla işlevselliğe sahip olmak isteyebilirsiniz, ancak temel konsepti gösterir.


7
ilginç, ancak std::vectoralt sınıflandırma için değil dikkatli olun .
Evan Teran

7
Üzgünüm, daha net olmalıydım - has-auygun bir alt sınıf ( is-a) olmak yerine, içinde bir vektörü muhafaza eden vektörle aynı yöntemlerle kendi sınıfınızı yaratabilirsiniz .
paxdiablo

1
operator[](int)Sabit olmayan yineleyiciler dahil ancak bunlarla sınırlı olmamak üzere verileri erişimde devre dışı bırakmadığınız sürece bu sorun yaratır ...
David Rodríguez - dribeas

1
@paxdiablo David'e, vektörde depolanan verilerin operatör [] kullanılarak veya sabit olmayan bir yineleyici aracılığıyla dolaylı olarak yönlendirilip yönlendirilmediğini ifade ettiğine inanıyorum. Değiştirilen konumdaki değer şimdi toplamı yanlış yapacak ve bu da toplamı yanlış yapacaktır. İstemci kodu "alt sınıflı" vektör içindeki herhangi bir öğeye değiştirilebilir bir referans tutabiliyorsa, toplamın doğru olduğundan emin olmanın bir yolu yoktur.
Bret Kuhns

2
Bu yaklaşım, temel vektör işlemleri için performans cezasına neden olur.
Basilevs

23

Geriye doğru yapabildiğinizde toplamı neden ileriye taşıyorsunuz ? Verilen:

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

Aboneliği geriye doğru sayarak kullanabiliriz:

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

Aralık kontrollü "abonelik" i geriye doğru sayarak kullanabiliriz (her ihtimale karşı):

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

For döngüsünde ters yineleyiciler kullanabiliriz:

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

İleri döngüleri geriye doğru yineleyerek bir for döngüsü (oooh, tricky!)

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

accumulateTers yineleyicilerle birlikte kullanabiliriz :

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

for_eachTers yineleyiciler kullanarak bir lambda ifadesi ile kullanabiliriz :

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

Gördüğünüz gibi, vektörü ileri doğru toplamak için vektörün geriye doğru toplanmasının birçok yolu vardır ve bunların bazıları çok daha heyecan vericidir ve birebir hatalar için çok daha fazla fırsat sunar.


36
Ve neden sarma için modül operatörü ile bir asal sayı ekleyerek vektörde dolaşmıyorsunuz? :-)
paxdiablo

3
@paxdiablo Gerçekten görece asal olmanız gerekiyor v.size().
clstrfsck

1
-1: vector :: size () işaretsiz bir değer döndürür ve (v.size () - 1) gibi uyarılar oluşturur veya en kötü durumlarda mayın tarlası yapar.
Julien Guertault

1
Bu cevap neden var? Geriye doğru toplamanın bir avantajı var mı, yoksa sadece trollüyor musunuz?
Lynn

4
@Lynn: Vektörün sonu önbellekte sıcaksa (ileri giden önceki bir döngüden), evet ise, geriye doğru döngü geçerli Intel x86 işlemcilerinde ölçülebilir şekilde daha hızlı olabilir. Ayrıca, bir döngü sayacını sıfıra kadar saymak, derleyiciye asm'deki bir komutu kaydedebilir, bu da döngüyü açmazsa önemli olabilir. Önceden getirme bazen ileriye doğru döngü yaparken biraz daha iyi çalışır, bu nedenle genel olarak her zaman geriye doğru döngü yapmak daha iyi değildir.
Peter Cordes

15
#include<boost/range/numeric.hpp>
int sum = boost::accumulate(vector, 0);

Cevap için teşekkürler. BTW std :: accumulate and boost :: accumulate arasındaki fark ne zaman karmaşıklığı?
Prasoon Saurav

1
Zaman karmaşıklığı std ve boost birikimleri için aynıdır - doğrusal. Bu durumda, boost :: accumulate yazmak, başlangıç ​​ve bitişte elle göndermekten daha kolaydır. Gerçek bir fark yok.
metal

7
boost::accumulatesadece bir sarıcı std::accumulate.
rafak

2
Desteklenmeyen yol çok daha zor değildir: #include <numeric>ve std::accumulate(v.begin(), v.end(), (int64_t)0);. İlk akümülatör değerinin türünün akümülatör türü olarak kullanıldığına dikkat edin, bu nedenle 8 bitlik öğeleri 64 bitlik bir sonuçta toplamak istiyorsanız, bunu böyle yaparsınız.
Peter Cordes

6

Bir de kullanabilir std::valarray<T>böyle

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

Bazıları bu şekilde verimli bulamayabilir, çünkü valarrayihtiyaç büyüklüğü vektörün büyüklüğü kadar büyük olmalıdır ve başlatma valarrayda zaman alacaktır.

Bu durumda, kullanmayın ve diziyi özetlemenin başka bir yolu olarak almayın.


5

Yalnızca C ++ 0x:

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

Bu, başka bir yerde bahsedilen BOOST_FOREACH ile benzerdir ve birikim veya for_each ile kullanılan durumsal işlevlere kıyasla daha karmaşık durumlarda aynı netlik avantajına sahiptir.


3
Eğer değiştirirseniz for (int n : v) sum += n;içine for (auto n : v) sum += n;herhangi vektör şablonu ile çalışacaktır. OP'nin <int> vektörüne atıfta bulunduğunu biliyorum, ama bu yol biraz daha genel :-)
Jonas

5

Ben bir Perl kullanıcısıyım, sahip olduğumuz bir oyun, bir değişkeni artırmak için her farklı yolu bulmak ... burada gerçekten farklı değil. C ++ 'da bir vektörün elemanlarının toplamını bulmanın kaç yolunun cevabı muhtemelenan infinity ...

2 sentim:

Çirkin yineleyici sözdiziminden kurtulmak için BOOST_FOREACH kullanma:

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

endeksler üzerinde yineleme (okumak gerçekten kolay).

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

Bu diğeri yıkıcıdır, vektöre yığın gibi erişir:

while (!myvector.empty()){
   sum+=myvector.back();
   myvector.pop_back();
}

Neden endeksleri yinelemenin verimsiz olduğunu söylüyorsunuz? Bunu söylemenin temeli nedir?
bobobobo

@bobobobo: verimsiz muhtemelen çok fazla. Hem vektör hem de artış sayacından etkili veri konumu hesaplamanız gerekir, ancak bu iki işlemden biri yeterli olmalıdır, ancak kayıt silme yineleyicilerinin maliyeti daha da kötü olabilir. Bu yüzden kelimeyi kaldıracağım.
kriss

Optimize edici bir derleyici, dizin değişkenini optimize edebilir ve yalnızca işaretçi artışını kullanabilir. (Döngü-çıkış koşulunun işaretçi karşılaştırması olmasını sağlayabilir start + length). Gerçek yineleyiciler de tamamen optimize edilmelidir. Unutmayın, bu perl değil; yorumlanmak için tamamen derlenmiştir.
Peter Cordes

-1

Bir vektörün tüm öğelerinin toplamını bulmanın en kolay yolunu buldum

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

}

Bu programda, 10 büyüklüğünde bir vektör var ve 1 ile başlatılır. Toplamı dizi gibi basit bir döngü ile hesapladım.


1
A intindeksini kullanmak std::vectorgenel olarak güvenli değildir. v.size()içinde depolanabilecek en yüksek değerden daha büyük olabilir int(bazı hedef platformlar ve derleyicilerle birlikte size_of(int) < size_of(size_t)). Bu durumda, kodunuz dizinden taşacaktır. std :: vector <T> :: size_type tercih edilmelidir.
Vincent Saulue-Laborde

Bu bir örnek @ VincentSaulue-Laborde İhtiyacınıza göre veri türünü ve boyutunu alabilirsiniz.
Ravi Kumar Yadav

-5

Kolay. C ++ 11, bir vektörün elemanlarını özetlemek için kolay bir yol sağlar.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.