0

I am trying to average the negative numbers in the list and average the positive numbers in the list as well as the average of the whole list. This is what i have so far I know it is not much but i'm not sure what my next step should be...

//This will read in a list of n values, where n is not known ahead of time. The number of values read into the array will be saved in n.

vector<int> readList()
{
std::vector<int> result;

ifstream inFile;

inFile.open("setA.txt");

for (int x; inFile >> x; )
{
    result.push_back(x);
}

return result;
}

//array is a one-dimensional array of integers and n is the number of elements in that array that contain valid data values. Both of these are input parameters to the function. The function must calculate 1) the average of the n integers in array, storing the result in ave; 2) the average of the positive numbers (> 0), storing the result in avePos, and 3) the average of the negative numbers (< 0), storing the result in aveNeg.

void avgs (std::vector<int> &array, int &ave, int &avePos, int &aveNeg)
{
     int sum = 0, pos_sum = 0, neg_sum = 0, pos_count = 0, neg_count = 0;
for (auto i : array)
{
    sum += i;
    if (i > 0) { pos_sum += i; ++pos_count; }
    if (i < 0) { neg_sum += i; ++neg_count; }
}

if(pos_sum) avePos = pos_sum / pos_count;
if(neg_sum) aveNeg = neg_sum / neg_count;
}
  • This is my new edit if you guys could recommend anything (the bottom part) – user3003912 Nov 18 '13 at 21:29
  • By the way I must use those perimeters in my functions – user3003912 Nov 18 '13 at 21:30
  • I edited my question, sorry guys for making other posts. – user3003912 Nov 18 '13 at 22:09
  • Come on man. You can't expect us to do *everything* for you. At some point in your programming career, you're going to have to think for yourself. If you have a sum and a count, and you can't figure out how to get an average, you need to take a math class. If you do know how to find the average, but can't figure out how to express that calculation in C++, then you need to read the first few chapters of [a C++ book](http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). – Benjamin Lindley Nov 18 '13 at 22:22
  • I know how to get an average, i'm just confuse of how to get an average by only averaging the negatives and the positives. @BenjaminLindley – user3003912 Nov 18 '13 at 22:34
  • But you had the sum of the positives, and the count of the positives from my answer. You also had the sum and count of the negatives, and the total sum and count. Then you asked another question because you were doing the calculation wrong. So what problem are you still having? – Benjamin Lindley Nov 18 '13 at 22:54
  • i'm not sure how to use the count_if in the for loop so i could get the averages.@BenjaminLindley – user3003912 Nov 18 '13 at 23:27
  • Why do you think you need `count_if`? What do you think `pos_count` and `neg_count` are for? – Benjamin Lindley Nov 18 '13 at 23:30
  • Never mind I see what you mean. In that case do I get rid of the if(pos_sum) and if(neg_sum)? Please help, i'm sorry if I sound dumb but I am a beginner.@BenjaminLindley – user3003912 Nov 18 '13 at 23:36
  • Well, you need to check if `pos_count` and `neg_count` are zero, in order to prevent dividing by zero. But your test works too, because if `pos_sum` is zero, so is `pos_count` (because the sum of zero positive integers is zero), and the same thing for the negative side. – Benjamin Lindley Nov 18 '13 at 23:41

5 Answers5

3

You could make 3 seperate calls to accumulate with custom summing functors. Or you could use one call to accumulate with a very ugly summing functor. But a simple for loop which does all three at once would be better here, both for readability, and efficiency.

int sum = 0, pos_sum = 0, neg_sum = 0, pos_count = 0, neg_count = 0;
for (auto i : array)
{
    sum += i;
    if (i > 0) { pos_sum += i; ++pos_count; }
    if (i < 0) { neg_sum += i; ++neg_count; }
}

Make sure to check for zero on the pos_count and the neg_count before dividing.

Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
1
ave = sum/array.size();

That will do integer division. The average is very likely going to be a floating point value, so you need to cast first:

float ave = static_cast<float>(sum) / array.size();

To do the other averages, you'll want to sum up only the positive and negative values, respectively, and then divide by the number of positive and negative numbers, respectively.

Just to show it can be done:

void averages(const std::vector<int>& vec, double& overall_average, double& positive_average, double& negative_average)
{
    std::tuple<int, int, int, int, int, int> sums_and_counts = std::accumulate(vec.begin(), vec.end(), std::make_tuple(0, 0, 0, 0, 0, 0), [](std::tuple<int, int, int, int, int, int> t, int i)
    {
        std::get<0>(t) += i;
        std::get<1>(t) += 1;
        if (i < 0)
        {
            std::get<2>(t) += i;
            std::get<3>(t) += 1;
        }

        if (i > 0)
        {
            std::get<4>(t) += i;
            std::get<5>(t) += 1;
        }
        return t;
    });

    overall_average = positive_average = negative_average = 0.0;
    if (std::get<1>(sums_and_counts))
    {
        overall_average = static_cast<double>(std::get<0>(sums_and_counts)) / std::get<1>(sums_and_counts);
    }

    if (std::get<3>(sums_and_counts))
    {
        negative_average = static_cast<double>(std::get<2>(sums_and_counts)) / std::get<3>(sums_and_counts);
    }

    if (std::get<5>(sums_and_counts))
    {
        positive_average = static_cast<double>(std::get<4>(sums_and_counts)) / std::get<5>(sums_and_counts);
    }  
}
Zac Howland
  • 15,777
  • 1
  • 26
  • 42
  • Yep, that's the ugly functor I referred to in my answer :) +1. But if you want to make it slightly more readable, you could define some function local constants with appropriate names for 0 through 5. – Benjamin Lindley Nov 18 '13 at 22:33
  • I didn't want to make it too easy to read ;) I actually toyed with the idea of returning a `tuple`. – Zac Howland Nov 18 '13 at 22:47
0

I'm too lazy to test it, but I think this will work:

float avePos = static_cast<float>(accumulate(array.begin(), array.end(), 
                                   [](int sum, int x]->int
                                   {
                                      return sum + max(x, 0);
                                   }) / static_cast<float>(array.size());

float aveNeg = static_cast<float>(accumulate(array.begin(), array.end(), 
                                   [](int sum, int x]->int
                                   {
                                      return sum + min(x, 0);
                                   }) / static_cast<float>(array.size());
HeywoodFloyd
  • 166
  • 1
  • 12
  • No, it won't work as is. Either the number of positive numbers, or the number of negative numbers, or both, is going to be less than `array.size()`. But in both cases, you are still dividing by `array.size()`, giving lower absolute value results. – Benjamin Lindley Nov 18 '13 at 21:24
  • [Benjamin Lindley](http://stackoverflow.com/users/440119/benjamin-lindley), I was so eager to show off that I knew how to do lamba functions that I forgot basic math. – HeywoodFloyd Nov 19 '13 at 14:27
0

First off all you should count how many there are positive and negative elements in array.

You could use the same std::accumulate algorithm. For example

std::pair<int, int> count = std::accumulate( array.begin(), array.end(), std::make_pair( 0, 0 ),
                                             []( std::pair<int, int> p, int x )
                                             {
                                                 return ( p.first += x < 0, p.second += x > 0, p );
                                             } ); 

Then you can use the range-based for statement to get corresponding sums:

ave = aveNeg = avePos = 0;
for ( int x : array )
{
    ave += x;
    if ( x < 0 ) aveNeg += x;
    if ( 0 < x ) avePos += x;
}

if ( !array.empty() ) ave /= ( int )array.size();
if ( count.first ) aveNeg /= count.first;
if ( count.second ) avePos /= count.second;
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

I think answer given by Benjamin Lindley is OK, but be careful with using int type for sum variable. It could easily overflow depending on your input values. Maybe use 64 bit integer if you are using 32 bit int, or use double. It all depends on what is the expected range of input values.

petrku
  • 69
  • 3