จะสรุปองค์ประกอบของเวกเตอร์ C ++ ได้อย่างไร


240

อะไรคือวิธีที่ดีในการหาผลรวมขององค์ประกอบทั้งหมดในstd::vector?

สมมติว่าฉันมีเวกเตอร์ที่std::vector<int> vectorมีองค์ประกอบบางอย่างอยู่ในนั้น ตอนนี้ฉันต้องการหาผลรวมขององค์ประกอบทั้งหมด อะไรกันในวิธีเดียวกัน


1
"เท่าไหร่"? จริงๆ? นั่นเป็นคำถามที่คลุมเครือเกินไป : p อาจเป็นประโยชน์มากกว่าถ้าขอวิธีที่ดีในการทำ
jalf

3
คุณหมายถึงอะไรเมื่อคุณพูดว่า "ฟังก์ชั่นที่คล้ายกับ" คุณกำลังมองหาสิ่งทดแทนstd::accumulateใน Boost หรือไม่? (ถ้าเป็นเช่นนั้นทำไม?) คุณกำลังมองหาฟังก์ชั่นที่ทำสิ่งที่คล้ายกันstd::accumulateใช่ไหม (ถ้าเช่นนั้นคืออะไร)
James McNellis

4
ถ้าคุณต้องการบางสิ่งที่คล้ายกันstd::accumulateสมมุติว่าคุณต้องการให้มันแตกต่างในบางแง่ (ไม่เช่นนั้นคุณสามารถใช้std::accumulate); std::accumulateคุณกำลังมองหาอะไรที่แตกต่างจาก
CB Bailey

คำตอบ:


429

จริงๆแล้วมีวิธีการค่อนข้างน้อย

int sum_of_elems = 0;

C ++ 03

  1. คลาสสิกสำหรับวง:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. ใช้อัลกอริทึมมาตรฐาน:

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

    หมายเหตุสำคัญ:ประเภทของอาร์กิวเมนต์ล่าสุดนั้นไม่ได้ใช้เฉพาะกับค่าเริ่มต้น แต่สำหรับประเภทของผลลัพธ์เช่นกัน ถ้าคุณใส่ค่า int ตรงนั้นมันจะสะสม ints แม้ว่าเวกเตอร์จะลอย หากคุณกำลังรวมจำนวนจุดลอยตัวให้เปลี่ยน0เป็น0.0หรือ0.0f(ขอบคุณ nneonneo) ดูเพิ่มเติมที่โซลูชัน C ++ 11 ด้านล่าง

C ++ 11 และสูงกว่า

  1. ข ติดตามชนิดเวกเตอร์โดยอัตโนมัติแม้ในกรณีที่มีการเปลี่ยนแปลงในอนาคต:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                   decltype(vector)::value_type(0));
  2. การใช้std::for_each:

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. ใช้ช่วงสำหรับวง (ขอบคุณ Roger Pate):

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

6
แน่นอนว่าใน C ++ 03 คุณสามารถใช้std::for_eachร่วมกับ functor ได้มันต้องใช้โค้ดหลายบรรทัดในการกำหนดมากกว่าแลมบ์ดา C ++ 0x
Ben Voigt

8
ทำไมตัวอย่างแลมบ์ดาของคุณใช้for_each? accumulateจะกระชับมากขึ้น (ถึงแม้ว่ามันจะไม่จำเป็นต้องแลมบ์ดา)
jalf

4
@jalf: จุดของคุณถูกต้องฉันควรจะใช้accumulateภายในfor_eachแต่ไม่ใช่ตัวอย่างนี้มีประโยชน์ (เพื่อการเรียนรู้) เพราะมันแสดงให้เห็นว่าเราสามารถมี lambdas ที่ซ้อนกัน :-)
Prasoon Saurav

58
โปรดใช้ความระมัดระวังaccumulateด้วย ประเภทของอาร์กิวเมนต์สุดท้ายไม่เพียง แต่ใช้สำหรับค่าเริ่มต้น แต่สำหรับประเภทของผลลัพธ์ หากคุณใส่intนั้นก็จะสะสมints floatแม้ว่าเวกเตอร์มี ผลลัพธ์อาจผิดอย่างละเอียดและคอมไพเลอร์จะส่งผลลัพธ์กลับไปลอยโดยไม่บอกคุณ
nneonneo

3
ทำไมคุณจะใช้for_eachถ้าคุณมีaccumulate?
juanchopanza

46

วิธีที่ง่ายที่สุดคือการใช้std:accumuateของvector<int> A:

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

34

Prasoon ได้เสนอวิธีการที่แตกต่าง (และดี) ให้กับโฮสต์แล้วซึ่งไม่จำเป็นต้องทำซ้ำที่นี่ ฉันต้องการแนะนำวิธีอื่นสำหรับความเร็วอย่างไรก็ตาม

หากคุณกำลังจะทำเช่นนี้ไม่น้อยคุณอาจต้องการที่จะต้องพิจารณา "ย่อย classing" เวกเตอร์ของคุณเพื่อให้ผลรวมขององค์ประกอบที่จะยังคงแยกกัน (ไม่จริงย่อย classing เวกเตอร์ซึ่งเป็นเต็มไปด้วยปัญหาเกิดจากการขาดการ virtual destructor - ฉันกำลังพูดถึงคลาสที่มีผลรวมและเวกเตอร์ในคลาสhas-aมากกว่าis-aและมีวิธีการเหมือนเวกเตอร์)

สำหรับเวกเตอร์ที่ว่างเปล่าผลรวมจะถูกตั้งค่าเป็นศูนย์ ในทุกการแทรกของเวกเตอร์ให้เพิ่มองค์ประกอบที่ถูกแทรกลงในผลรวม ในการลบทุกครั้งให้ลบออก โดยพื้นฐานแล้วสิ่งใดก็ตามที่สามารถเปลี่ยนเวกเตอร์ต้นแบบนั้นจะถูกดักเพื่อให้แน่ใจว่าผลรวมนั้นคงที่

ด้วยวิธีนี้คุณมีวิธี O (1) ที่มีประสิทธิภาพมากในการ "คำนวณ" ผลรวม ณ เวลาใด ๆ (เพียงแค่คืนผลรวมที่คำนวณได้ในปัจจุบัน) การแทรกและการลบจะใช้เวลานานขึ้นเล็กน้อยในขณะที่คุณปรับยอดรวมและคุณควรคำนึงถึงประสิทธิภาพการทำงานนี้ด้วย

เวกเตอร์ที่จำเป็นต้องใช้ผลรวมบ่อยกว่าเวกเตอร์ที่มีการเปลี่ยนแปลงเป็นสิ่งที่น่าจะได้รับประโยชน์จากโครงการนี้เนื่องจากค่าใช้จ่ายในการคำนวณผลรวมนั้นตัดจำหน่ายตลอดการเข้าถึงทั้งหมด เห็นได้ชัดว่าถ้าคุณต้องการเพียงผลรวมทุกชั่วโมงและเวกเตอร์มีการเปลี่ยนแปลงสามพันครั้งต่อวินาทีมันจะไม่เหมาะสม

สิ่งนี้จะพอเพียง:

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

เห็นได้ชัดว่าเป็นรหัสหลอกและคุณอาจต้องการฟังก์ชั่นเพิ่มเติมอีกเล็กน้อย แต่มันแสดงแนวคิดพื้นฐาน


7
น่าสนใจ แต่ควรระวังเนื่องจากstd::vectorไม่ได้มีไว้สำหรับการทำคลาสย่อย
Evan Teran

7
ขออภัยฉันควรชัดเจนกว่านี้ - คุณสามารถสร้างชั้นเรียนของคุณเองด้วยวิธีการเดียวกับเวกเตอร์ที่รักษาhas-aเวกเตอร์ไว้ภายในแทนที่จะเป็นคลาสย่อยที่เหมาะสม ( is-a)
paxdiablo

1
นี่เป็นปัญหาเว้นแต่ว่าคุณจะปิดการใช้งาน accessors ในข้อมูลรวมถึง แต่ไม่ จำกัด เฉพาะoperator[](int)iterators ที่ไม่ใช่ const ...
David Rodríguez - dribeas

1
@paxdiablo ฉันเชื่อว่า David หมายถึงว่าข้อมูลที่จัดเก็บในเวกเตอร์นั้นถูกควบคุมผ่านการใช้โอเปอเรเตอร์ [] หรือโดยอ้อมผ่านตัววนซ้ำที่ไม่ใช่ const ค่าที่ตำแหน่งที่จัดการตอนนี้จะแตกต่างกันซึ่งจะทำให้ผลรวมไม่ถูกต้อง ไม่มีวิธีใดที่จะรับรองผลรวมถูกต้องหากรหัสลูกค้าสามารถเก็บการอ้างอิงที่ไม่แน่นอนกับองค์ประกอบใด ๆ ภายในเวกเตอร์ "subclassed"
Bret Kuhns

2
วิธีการนี้ทำให้เกิดการปรับประสิทธิภาพสำหรับการดำเนินงานเวกเตอร์ขั้นพื้นฐาน
Basilevs

23

เหตุใดจึงต้องมีการรวมกันส่งต่อเมื่อคุณสามารถทำย้อนกลับได้ ? ได้รับ:

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

เราสามารถใช้ตัวห้อยนับถอยหลังไปด้านหลัง:

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

เราสามารถใช้ "การห้อย," การนับช่วงถอยหลัง (ในกรณี):

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

เราสามารถใช้ตัววนซ้ำแบบย้อนกลับในการ for:

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

เราสามารถใช้ตัววนซ้ำไปข้างหน้าวนซ้ำไปข้างหลังในวงวน (oooh, หากิน!):

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

เราสามารถใช้accumulateกับตัววนซ้ำแบบย้อนกลับ:

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

เราสามารถใช้for_eachกับแลมบ์ดานิพจน์โดยใช้ตัววนซ้ำแบบย้อนกลับ:

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

ดังที่คุณเห็นมีหลายวิธีในการหาผลรวมของเวกเตอร์ไปข้างหลังเหมือนกับที่จะรวมเวกเตอร์ไปข้างหน้าและสิ่งเหล่านี้น่าตื่นเต้นมากขึ้น


36
และทำไมไม่หมุนรอบเวกเตอร์ด้วยการเพิ่มจำนวนเฉพาะด้วยตัวดำเนินการโมดูลัสของการพันด้วย? :-)
paxdiablo

3
@paxdiablo คุณต้องเป็นคนสำคัญv.size()จริงๆ
clstrfsck

1
-1: vector :: size () ส่งคืนค่าที่ไม่ได้ลงนามทำให้นิพจน์เช่น (v.size () - 1) สร้างคำเตือนหรือเขตที่วางทุ่นระเบิดในกรณีที่เลวร้ายที่สุด
Julien Guertault

1
ทำไมคำตอบนี้จึงมีอยู่? มีข้อได้เปรียบในการรวมกลับไปด้านหลังหรือคุณแค่หมุนรอบ?
ลินน์

4
@Lynn: หากจุดสิ้นสุดของเวกเตอร์นั้นร้อนในแคช (จากลูปก่อนหน้าซึ่งไปข้างหน้า) ดังนั้นใช่การวนซ้ำไปข้างหลังสามารถวัดได้เร็วขึ้นใน CPU Intel x86 ปัจจุบัน นอกจากนี้การนับลูปตัวนับลงไปที่ศูนย์สามารถบันทึกคำสั่งคอมไพเลอร์ใน asm ซึ่งอาจมีความสำคัญหากไม่ได้ปลดลูป แม้ว่าบางครั้งการโหลดล่วงหน้าจะทำงานได้ดีขึ้นเล็กน้อยเมื่อวนไปข้างหน้าดังนั้นโดยทั่วไปจึงไม่ควรวนซ้ำไปข้างหลังเสมอ
Peter Cordes

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

ขอบคุณสำหรับคำตอบ. BTW ความแตกต่างระหว่าง std :: accumulate และ boost :: สะสมในความซับซ้อนของเวลาคืออะไร?
Prasoon Saurav

1
ความซับซ้อนของเวลานั้นเหมือนกันสำหรับ std's และ boost's - linear ในกรณีนี้ boost :: accumulate นั้นง่ายต่อการพิมพ์มากกว่าการส่งในตอนเริ่มต้นและสิ้นสุดด้วยตนเอง ไม่มีความแตกต่างที่แท้จริง
โลหะ

7
boost::accumulatestd::accumulateเป็นเพียงเสื้อคลุมรอบ
rafak

2
วิธีที่ไม่เพิ่มไม่ได้ยากมาก: และ#include <numeric> std::accumulate(v.begin(), v.end(), (int64_t)0);โปรดสังเกตว่าประเภทของค่าตัวสะสมเริ่มต้นนั้นจะใช้เป็นประเภทตัวสะสมดังนั้นหากคุณต้องการรวมองค์ประกอบ 8 บิตเข้ากับผลลัพธ์ 64 บิตนั่นเป็นวิธีที่คุณทำ
Peter Cordes

6

ท่านสามารถใช้std::valarray<T>เช่นนี้

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

บางคนอาจไม่พบวิธีนี้ที่มีประสิทธิภาพเนื่องจากขนาดของvalarrayความต้องการจะใหญ่เท่ากับขนาดของเวกเตอร์และการเริ่มต้นvalarrayจะใช้เวลาเช่นกัน

ในกรณีนี้อย่าใช้มันและใช้เป็นอีกวิธีในการสรุปลำดับ


5

C ++ 0x เท่านั้น:

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

สิ่งนี้คล้ายกับ BOOST_FOREACH ที่กล่าวถึงที่อื่นและได้รับประโยชน์จากความชัดเจนในสถานการณ์ที่ซับซ้อนมากขึ้นเมื่อเทียบกับฟังก์ชั่น stateful ที่ใช้กับสะสมหรือ for_each


3
ถ้าคุณเปลี่ยนfor (int n : v) sum += n;ลงไปfor (auto n : v) sum += n;มันจะทำงานร่วมกับแม่แบบเวกเตอร์ใด ๆ ฉันรู้ว่า OP อ้างถึง vector <int> แต่วิธีนี้ค่อนข้างทั่วไปมากกว่า :-)
Jonas

5

ฉันเป็นผู้ใช้ Perl เกมที่เรามีคือการค้นหาทุกวิธีที่แตกต่างกันในการเพิ่มตัวแปร ... นั่นไม่ได้แตกต่างไปจากที่นี่จริงๆ คำตอบของวิธีหาจำนวนผลรวมขององค์ประกอบของเวกเตอร์ใน C ++ น่าจะเป็นan infinity...

2 เซนต์ของฉัน:

ใช้ BOOST_FOREACH เพื่อรับไวยากรณ์ iterator ที่น่าเกลียด:

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

วนซ้ำดัชนี (อ่านง่ายจริงๆ)

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

อีกอันหนึ่งนั้นเป็นแบบทำลายล้างการเข้าถึงเวกเตอร์เหมือนสแต็ก:

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

ทำไมคุณถึงบอกว่าการทำดัชนีซ้ำไม่มีประสิทธิภาพ? พื้นฐานของคุณสำหรับการพูดนั้นคืออะไร?
bobobobo

@bobobobo: ดีไม่มีประสิทธิภาพน่าจะมากเกินไป คุณทั้งคู่ต้องคำนวณตำแหน่งข้อมูลที่มีประสิทธิภาพจาก vector และตัวเพิ่มที่เพิ่มขึ้น แต่หนึ่งในสองการดำเนินการเหล่านี้น่าจะเพียงพอ ดังนั้นฉันจะลบคำ
kriss

คอมไพเลอร์การปรับให้เหมาะสมสามารถปรับตัวแปรดัชนีให้เหมาะสมและใช้การเพิ่มตัวชี้หากต้องการ (มันสามารถทำให้เงื่อนไขการออกจากลูปเป็นการเปรียบเทียบตัวชี้กับstart + length) ตัววนซ้ำจริงควรปรับให้เหมาะสมทั้งหมด จำไว้ว่ามันไม่ใช่ perl; มันรวบรวมอย่างสมบูรณ์เพื่อ asm ไม่ตีความ
Peter Cordes

-1

ฉันพบวิธีที่ง่ายที่สุดในการหาผลรวมขององค์ประกอบทั้งหมดของเวกเตอร์

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

}

ในโปรแกรมนี้ฉันมีเวกเตอร์ขนาด 10 และเริ่มต้นด้วย 1 ฉันได้คำนวณผลรวมด้วยลูปแบบง่ายเช่นเดียวกับในอาร์เรย์


1
การใช้intดรรชนี a std::vectorไม่ปลอดภัยโดยทั่วไป v.size()อาจใหญ่กว่าค่าสูงสุดที่สามารถจัดเก็บไว้ได้int(โปรดทราบว่ามีแพลตฟอร์มและคอมไพเลอร์เป้าหมายบางตัวsize_of(int) < size_of(size_t)) ในกรณีนี้รหัสของคุณจะล้นดัชนี std :: vector <T> :: size_typeควรเป็นที่ต้องการ
Vincent Saulue-Laborde

มันเป็นตัวอย่าง @ VincentSaulue-Laborde คุณสามารถใช้ชนิดข้อมูลและขนาดตามความต้องการของคุณ
Ravi Kumar Yadav

-5

มันง่ายมาก C ++ 11 เป็นวิธีที่ง่ายในการสรุปองค์ประกอบของเวกเตอร์

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.