The C++ Standard Library header <numeric> contains several generic numeric algorithms. In this post we take a look at how they can be used for solving real world examples.

Accumulating values

Problem: Given the monthly sales values compute the total sales during a year.

Solution: This is a simple sum that can be computed by iterating over the sales and summing them.

There is an algorithm for that called accumulate. It computes the sum in a given range and the provided initial value. So for our problem this can be put as:

std::vector<double> sales = {1000, 1500, 2000, 1500, 2000, 1000, 3000, 1500, 1200, 1800, 2200, 2000}; auto total_sales = std::accumulate(std::begin(sales), std::end(sales), 0.0); 1 2 3 4 std :: vector < double > sales = { 1000 , 1500 , 2000 , 1500 , 2000 , 1000 , 3000 , 1500 , 1200 , 1800 , 2200 , 2000 } ; auto total_sales = std :: accumulate ( std :: begin ( sales ) , std :: end ( sales ) , 0.0 ) ;

Problem: Given the monthly inflation rates compute the total inflation during a year.

Solution: If the inflation was 10% in January and 20% in February, the inflation at the end of February is not 30% but 32%. The monthly inflation rates must be multiplied. An overloaded accumulate allows to specify a binary operation to be used for accumulating values, instead of operator+ . So the solution to our problem could look like this:

std::vector<double> inflation = {1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.06, 1.05, 1.04, 1.03, 1.02, 1.01}; auto total_inflation = std::accumulate(std::begin(inflation), std::end(inflation), 1.0, std::multiplies<double>()); 1 2 3 4 5 6 std :: vector < double > inflation = { 1.01 , 1.02 , 1.03 , 1.04 , 1.05 , 1.06 , 1.06 , 1.05 , 1.04 , 1.03 , 1.02 , 1.01 } ; auto total_inflation = std :: accumulate ( std :: begin ( inflation ) , std :: end ( inflation ) , 1.0 , std :: multiplies < double > ( ) ) ;

Partial accumulations

Problem: Given the monthly sales values compute the accumulated total sales per whole year at the end of each month. In other words show the sales for January, January to February, January to March, … January to December.

Solution: This is again a simple summing operation where in each loop you have a partial sum. s[0] = v[0] , s[1] = v[0] + v[1] , s[2] = v[0] + v[1] + v[2] , etc.

There is an algorithm called partial_sum that does exactly that: it computes the partial sums of the elements in the given range and writes them in a destination range. There are two overloads of it, one using operator+ and one that allows to specify a binary operation.

std::vector<double> sales = {1000, 1500, 2000, 1500, 2000, 1000, 3000, 1500, 1200, 1800, 2200, 2000}; std::vector<double> acc_sales; std::partial_sum(std::begin(sales), std::end(sales), std::back_inserter(acc_sales)); 1 2 3 4 5 std :: vector < double > sales = { 1000 , 1500 , 2000 , 1500 , 2000 , 1000 , 3000 , 1500 , 1200 , 1800 , 2200 , 2000 } ; std :: vector < double > acc_sales ; std :: partial_sum ( std :: begin ( sales ) , std :: end ( sales ) , std :: back_inserter ( acc_sales ) ) ;

The content of acc_sales will be {1000, 2500, 4500, 6000, 8000, 9000, 12000, 13500, 14700, 16500, 18700, 20700} .

Sum of products

Problem: Given the values of daily sales in USD compute the total sales in EUR.

Solution: Computing the total sales in USD and then multiplying with the USD/EUR exchange rate is not a correct solution because the exchange rate varies every day. We must compute the daily sales in EUR and then sum those values. That means we need a sum of products.

Another algorithm in the <numeric> header called inner_product does exactly that. It takes two ranges and an initial value and uses operator* to compute the product and operator+ to sum the products.

std::vector<double> sales_usd = {1000, 2000, 1500, 2000, 2500, 500, 500}; std::vector<double> usd_eur_rates = {0.7260, 0.7249, 0.7263, 0.7287, 0.7287, 0.7287, 0.7297, 0.7250, 0.7218}; auto sales_eur = std::inner_product(std::begin(sales_usd), std::end(sales_usd), std::begin(usd_eur_rates), 0.0); 1 2 3 4 5 6 7 8 std :: vector < double > sales_usd = { 1000 , 2000 , 1500 , 2000 , 2500 , 500 , 500 } ; std :: vector < double > usd_eur_rates = { 0.7260 , 0.7249 , 0.7263 , 0.7287 , 0.7287 , 0.7287 , 0.7297 , 0.7250 , 0.7218 } ; auto sales_eur = std :: inner_product ( std :: begin ( sales_usd ) , std :: end ( sales_usd ) , std :: begin ( usd_eur_rates ) , 0.0 ) ;

Problem: Given the monthly budgets and the actual sales over an year count how many times the budget was met or exceeded.

Solution: To count how many times the sales where equal or greater than the budget we have to compare the values in each month and increment a value each time the condition was true.

Instead of coding this explicitly we could use an overload of the inner_product algorithm that takes two additional parameters, both binary operations, for the sum operation and the product operation. For the sum we can use std::plus () and for comparison std::greater_equal () (need to include header <functional> ).

std::array<double, 12> budget = {1000, 1500, 2000, 1500, 2000, 1000, 3000, 1500, 1200, 1800, 2200, 2000}; std::array<double, 12> sales = {1000, 1400, 2100, 2000, 1800, 900, 3500, 1300, 1000, 2000, 2000, 2500}; auto count = std::inner_product(std::begin(sales), std::end(sales), std::begin(budget), 0, std::plus<int>(), std::greater_equal<double>()); 1 2 3 4 5 6 7 8 9 10 11 std :: array < double , 12 > budget = { 1000 , 1500 , 2000 , 1500 , 2000 , 1000 , 3000 , 1500 , 1200 , 1800 , 2200 , 2000 } ; std :: array < double , 12 > sales = { 1000 , 1400 , 2100 , 2000 , 1800 , 900 , 3500 , 1300 , 1000 , 2000 , 2000 , 2500 } ; auto count = std :: inner_product ( std :: begin ( sales ) , std :: end ( sales ) , std :: begin ( budget ) , 0 , std :: plus < int > ( ) , std :: greater_equal < double > ( ) ) ;

The result of the count for the input data is 6.

Adjacent differences

Problem: Given the monthly sales for an year compute the variations (in percentage) between the current and previous monthly sales. If sales in January were 1000 and in February were 1500, then the sales variation for February is 50%.

Solution: We have to iterate through the monthly value and compute (current - prev) / prev for each month. This is an operation applied to adjacent values.

An algorithm called adjacent_difference computes difference between adjacent values in a given ranges and writes them in a destination range. The first element of the input range is copied unmodified to the beginning of the destination range. An overload allows to specify a binary operation to be used instead of operator- .

To solve the proposed problem we call this algorithm on the sales and then remove the first element from the destination range (since it’s just a copy of the sales for the first month). We use a lambda for the binary operation and it simply returns (current - prev) / prev .

std::vector<double> sales = {1000, 1500, 2000, 1500, 2000, 1000, 3000, 1500, 1200, 1800, 2200, 2000}; std::vector<double> vars; std::adjacent_difference(std::begin(sales), std::end(sales), std::back_inserter(vars), [](double const & v2, double const v1) {return (v2-v1)/v1;}); vars.erase(std::begin(vars)); 1 2 3 4 5 6 7 8 9 std :: vector < double > sales = { 1000 , 1500 , 2000 , 1500 , 2000 , 1000 , 3000 , 1500 , 1200 , 1800 , 2200 , 2000 } ; std :: vector < double > vars ; std :: adjacent_difference ( std :: begin ( sales ) , std :: end ( sales ) , std :: back_inserter ( vars ) , [ ] ( double const & v2 , double const v1 ) { return ( v2 - v1 ) / v1 ; } ) ; vars . erase ( std :: begin ( vars ) ) ;

The result for the given range (shown with 4 decimals) is {0.5000, 0.3333, -0.2500, 0.3333, -0.5000, 2.0000, -0.5000, -0.2000, 0.5000, 0.2222, -0.0909} .

Share this: Twitter

LinkedIn

StumbleUpon

Facebook

Reddit

More

Google

Email



Print

