user3003912
user3003912

Reputation: 1

Having trouble averaging arrays

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

Upvotes: 0

Views: 280

Answers (5)

Zac Howland
Zac Howland

Reputation: 15872

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

Upvotes: 1

Benjamin Lindley
Benjamin Lindley

Reputation: 103693

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.

Upvotes: 3

Vlad from Moscow
Vlad from Moscow

Reputation: 310960

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;

Upvotes: 0

petrku
petrku

Reputation: 69

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.

Upvotes: 0

HeywoodFloyd
HeywoodFloyd

Reputation: 176

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());

Upvotes: 0

Related Questions