Làm thế nào để tổng hợp các phần tử của một vectơ C ++?


240

Các cách tốt để tìm tổng của tất cả các yếu tố trong một là std::vectorgì?

Giả sử tôi có một vectơ std::vector<int> vectorvới một vài yếu tố trong đó. Bây giờ tôi muốn tìm tổng của tất cả các yếu tố. Các cách khác nhau cho cùng là gì?


1
"Bao nhiêu"? Có thật không? Đó dường như là một câu hỏi quá mơ hồ. : p Có thể hữu ích hơn để yêu cầu một cách tốt để làm điều đó.
jalf

3
Ý bạn là gì khi bạn nói "chức năng tương tự?" Bạn đang tìm kiếm một sự thay thế cho std::accumulatetrong Boost? (Nếu vậy, tại sao?) Bạn đang tìm kiếm các chức năng làm điều gì đó tương tự std::accumulate? (Nếu vậy, thì sao?)
James McNellis

4
Nếu bạn muốn một cái gì đó tương tự std::accumulate, có lẽ bạn cũng muốn nó khác biệt ở một khía cạnh nào đó (nếu không bạn chỉ có thể sử dụng std::accumulate); std::accumulatebạn đang tìm kiếm sự khác biệt nào?
CB Bailey

Câu trả lời:


429

Trên thực tế có khá nhiều phương pháp.

int sum_of_elems = 0;

C ++ 03

  1. Vòng lặp cổ điển:

    for(std::vector<int>::iterator it = vector.begin(); it != vector.end(); ++it)
        sum_of_elems += *it;
  2. Sử dụng thuật toán tiêu chuẩn:

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

    Lưu ý quan trọng: Loại của đối số cuối cùng không chỉ được sử dụng cho giá trị ban đầu mà còn cho loại kết quả . Nếu bạn đặt một int ở đó, nó sẽ tích lũy int ngay cả khi vectơ có float. Nếu bạn đang tính tổng các số dấu phẩy động, hãy đổi 0thành 0.0hoặc 0.0f(nhờ nneonneo). Xem thêm giải pháp C ++ 11 dưới đây.

C ++ 11 trở lên

  1. b. Tự động theo dõi loại vectơ ngay cả trong trường hợp thay đổi trong tương lai:

    #include <numeric>
    
    sum_of_elems = std::accumulate(vector.begin(), vector.end(),
                                   decltype(vector)::value_type(0));
  2. Sử dụng std::for_each:

    std::for_each(vector.begin(), vector.end(), [&] (int n) {
        sum_of_elems += n;
    });
  3. Sử dụng vòng lặp dựa trên phạm vi (nhờ Roger Pate):

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

6
Tất nhiên trong C ++ 03 bạn có thể sử dụng std::for_eachvới functor, nó chỉ cần nhiều dòng mã hơn để xác định so với lambda C ++ 0x.
Ben Voigt

8
Tại sao ví dụ lambda của bạn sử dụng for_each? accumulatesẽ ngắn gọn hơn (ngay cả khi nó không cần lambda)
jalf

4
@jalf: quan điểm của bạn là chính xác, đáng lẽ tôi nên sử dụng accumulatebên trong for_eachnhưng ví dụ này không hữu ích (vì mục đích học tập) vì nó cho thấy rằng chúng ta cũng có thể lồng nhau lambdas :-)
Prasoon Saurav

58
Cẩn thận với accumulate. Loại đối số cuối cùng được sử dụng không chỉ cho giá trị ban đầu, mà còn cho loại kết quả. Nếu bạn đặt intở đó, nó sẽ tích lũy intngay cả khi vectơ có float. Kết quả có thể sai một cách tinh vi và trình biên dịch sẽ đưa kết quả trở lại nổi mà không cho bạn biết.
nneonneo

3
Tại sao bạn sẽ sử dụng for_eachnếu bạn có accumulate?
juanchopanza

46

Cách dễ nhất là sử dụng std:accumuatemột vector<int> A:

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

34

Prasoon đã đưa ra một loạt các cách khác nhau (và tốt) để làm điều này, không có cách nào cần lặp lại ở đây. Tuy nhiên, tôi muốn đề xuất một phương pháp thay thế cho tốc độ.

Nếu bạn sẽ làm điều này khá nhiều, bạn có thể muốn xem xét "phân lớp phụ" vectơ của mình để một tổng các phần tử được duy trì riêng biệt (không thực sự là vectơ phân lớp phụ là iffy do thiếu một hàm hủy ảo - Tôi đang nói nhiều hơn về một lớp chứa tổng và vectơ bên trong nó, has-achứ không phải is-a, và cung cấp các phương thức giống như vectơ).

Đối với một vectơ trống, tổng được đặt thành không. Trên mỗi lần chèn vào vectơ, thêm phần tử được chèn vào tổng. Trên mỗi lần xóa, trừ nó. Về cơ bản, bất cứ điều gì có thể thay đổi vectơ cơ bản đều bị chặn để đảm bảo tổng được giữ ổn định.

Bằng cách đó, bạn có một phương pháp O (1) rất hiệu quả để "tính toán" tổng số tại bất kỳ thời điểm nào (chỉ cần trả lại tổng số hiện đang tính). Việc chèn và xóa sẽ mất nhiều thời gian hơn khi bạn điều chỉnh tổng số và bạn nên cân nhắc hiệu năng này.

Các vectơ trong đó tổng cần thiết thường xuyên hơn vectơ được thay đổi là những vectơ có thể được hưởng lợi từ sơ đồ này, vì chi phí tính tổng được khấu hao trên tất cả các truy cập. Rõ ràng, nếu bạn chỉ cần tổng mỗi giờ và vectơ thay đổi ba nghìn lần một giây, nó sẽ không phù hợp.

Một cái gì đó như thế này sẽ đủ:

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

Rõ ràng, đó là mã giả và bạn có thể muốn có thêm một chút chức năng, nhưng nó cho thấy khái niệm cơ bản.


7
thú vị, nhưng hãy cẩn thận vì std::vectorkhông có nghĩa là phân lớp.
Evan Teran

7
Xin lỗi, tôi nên rõ ràng hơn - bạn có thể tạo lớp của riêng mình với các phương thức giống như vectơ duy trì một has-avectơ bên trong nó, thay vì là một lớp con ( is-a) thích hợp .
paxdiablo

1
Đây là vấn đề trừ khi bạn vô hiệu hóa các bộ truy cập vào dữ liệu, bao gồm nhưng không giới hạn ở operator[](int)các trình lặp không phải là const ...
David Rodríguez - dribeas

1
@paxdiablo Tôi tin rằng David có nghĩa là nếu dữ liệu được lưu trữ trong vectơ được thao tác thông qua việc sử dụng toán tử [] hoặc gián tiếp thông qua một trình vòng lặp không phải là const. Giá trị tại vị trí thao tác bây giờ sẽ khác nhau, điều này sẽ làm cho tổng không chính xác. Không có cách nào để đảm bảo tổng là chính xác nếu mã máy khách có thể giữ một tham chiếu có thể thay đổi đến bất kỳ phần tử nào trong vectơ "phân lớp".
Bret Kuhns

2
Cách tiếp cận này gây ra hình phạt hiệu suất cho các hoạt động vector cơ bản.
Basilevs

23

Tại sao thực hiện tổng kết về phía trước khi bạn có thể làm điều đó ngược lại ? Được:

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

Chúng ta có thể sử dụng đăng ký, đếm ngược:

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

Chúng tôi có thể sử dụng "đăng ký" được kiểm tra phạm vi, đếm ngược (chỉ trong trường hợp):

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

Chúng ta có thể sử dụng các trình vòng lặp ngược trong một vòng lặp for:

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

Chúng ta có thể sử dụng các trình vòng lặp tiến, lặp lại ngược, trong một vòng lặp for (oooh, tricky!):

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

Chúng ta có thể sử dụng accumulatevới các trình vòng lặp ngược:

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

Chúng ta có thể sử dụng for_eachvới biểu thức lambda bằng cách sử dụng các trình vòng lặp ngược:

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

Vì vậy, như bạn có thể thấy, có rất nhiều cách để tổng hợp vectơ ngược lại cũng như tính tổng vectơ về phía trước, và một số trong số này thú vị hơn nhiều và cung cấp cơ hội lớn hơn nhiều cho các lỗi ngoài luồng.


36
Và tại sao không quay vòng qua vectơ bằng cách thêm một số nguyên tố với toán tử mô đun cho phép quay vòng? :-)
paxdiablo

3
@paxdiablo Bạn chỉ thực sự cần phải tương đối chính v.size().
clstrfsck

1
-1: vector :: size () trả về giá trị không dấu, làm cho các biểu thức như (v.size () - 1) tạo cảnh báo hoặc trường mìn trong trường hợp xấu nhất.
Julien Guertault

1
Tại sao câu trả lời này tồn tại? Có một lợi thế để tổng hợp ngược, hoặc bạn chỉ đang troll?
Lynn

4
@Lynn: Nếu phần cuối của vectơ nóng trong bộ đệm (từ vòng lặp trước đã chuyển tiếp), thì có, việc lặp ngược có thể nhanh hơn có thể đo được trên CPU Intel x86 hiện tại. Ngoài ra, việc đếm bộ đếm vòng lặp xuống 0 có thể lưu trình biên dịch một lệnh trong mã asm, điều này có thể có ý nghĩa nếu nó không hủy vòng lặp. Tuy nhiên, việc tìm nạp trước đôi khi hoạt động tốt hơn một chút khi lặp về phía trước, vì vậy, nói chung, không phải lúc nào cũng tốt hơn để luôn lặp lại.
Peter Cordes

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

Cảm ơn câu trả lời. BTW sự khác biệt giữa std :: tích lũy và boost :: tích lũy trong độ phức tạp thời gian là gì?
Prasoon Saurav

1
Độ phức tạp thời gian là như nhau đối với tích lũy của std và boost - tuyến tính. Trong trường hợp này, boost :: tích lũy chỉ dễ nhập hơn là gửi ở đầu và cuối bằng tay. Không có sự khác biệt thực sự.
kim loại

7
boost::accumulatechỉ là một bao bọc xung quanh std::accumulate.
rafak

2
Cách không tăng không khó hơn nhiều: #include <numeric>std::accumulate(v.begin(), v.end(), (int64_t)0);. Lưu ý rằng loại giá trị bộ tích lũy ban đầu được sử dụng làm loại bộ tích lũy, vì vậy nếu bạn muốn tính tổng các phần tử 8 bit thành kết quả 64 bit, đó là cách bạn thực hiện.
Peter Cordes

6

Người ta cũng có thể sử dụng std::valarray<T>như thế này

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

Một số có thể không tìm thấy cách này hiệu quả vì kích thước của valarraynhu cầu phải lớn bằng kích thước của vectơ và việc khởi tạo valarraycũng sẽ mất thời gian.

Trong trường hợp đó, không sử dụng nó và coi đó là một cách khác để tóm tắt trình tự.


5

Chỉ C ++ 0x:

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

Điều này tương tự như BOOST_FOREACH được đề cập ở nơi khác và có cùng lợi ích của sự rõ ràng trong các tình huống phức tạp hơn, so với các hàm xử lý trạng thái được sử dụng với tích lũy hoặc for_each.


3
Nếu bạn thay đổi for (int n : v) sum += n;vào for (auto n : v) sum += n;nó sẽ làm việc với bất kỳ mẫu vector. Tôi biết OP đề cập đến vectơ <int>, nhưng cách này hơi chung chung hơn :-)
Jonas

5

Tôi là người dùng Perl, một trò chơi chúng tôi có là tìm mọi cách khác nhau để tăng một biến ... điều đó không thực sự khác biệt ở đây. Câu trả lời cho có bao nhiêu cách để tìm tổng các phần tử của một vectơ trong C ++ có lẽ là an infinity...

2 xu của tôi:

Sử dụng BOOST_FOREACH, để thoát khỏi cú pháp lặp xấu xí:

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

lặp đi lặp lại trên các chỉ số (thực sự dễ đọc).

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

Cái khác này là phá hoại, truy cập vào vector như một ngăn xếp:

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

Tại sao bạn nói lặp đi lặp lại trên các chỉ số là không hiệu quả? Cơ sở của bạn để nói điều đó là gì?
bobobobo

@bobobobo: tốt, không hiệu quả có lẽ là quá mức. Bạn có cả hai để tính toán vị trí dữ liệu hiệu quả từ vectơ và bộ đếm tăng, nhưng một trong hai thao tác này là đủ, nhưng chi phí cho các trình lặp lặp hội nghị thậm chí có thể còn tệ hơn. Do đó tôi sẽ loại bỏ từ này.
kriss

Một trình biên dịch tối ưu hóa có thể tối ưu hóa biến chỉ số và chỉ cần sử dụng gia số con trỏ nếu muốn. (Nó có thể làm cho điều kiện thoát vòng lặp là so sánh con trỏ với start + length). Lặp đi lặp lại thực tế cũng nên tối ưu hóa hoàn toàn đi. Hãy nhớ rằng, nó không phải là perl; nó được biên dịch đầy đủ thành asm, không được giải thích.
Peter Cordes

-1

Tôi tìm thấy cách dễ nhất để tìm tổng của tất cả các phần tử của một vectơ

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

}

Trong chương trình này, tôi có một vectơ kích thước 10 và được khởi tạo bằng 1. Tôi đã tính tổng bằng một vòng lặp đơn giản như trong mảng.


1
Sử dụng intđể lập chỉ mục a std::vectorkhông an toàn nói chung. v.size()có thể lớn hơn giá trị cao nhất có thể được lưu trữ int(lưu ý rằng với một số nền tảng và trình biên dịch đích size_of(int) < size_of(size_t)). Trong trường hợp này, mã của bạn sẽ tràn chỉ mục. std :: vector <T> :: size_type nên được ưu tiên.
Vincent Saulue-Laborde

Đó là một ví dụ @ VincentSaulue-Laborde Bạn có thể lấy loại dữ liệu và kích thước của nó theo yêu cầu của bạn.
Ravi Kumar Yadav

-5

Nó rất dễ dàng. C ++ 11 cung cấp một cách dễ dàng để tổng hợp các phần tử của một vectơ.

sum = 0; 
vector<int> vec = {1,2,3,4,5,....}
for(auto i:vec) 
   sum+=i;
cout<<" The sum is :: "<<sum<<endl; 
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.