Alex Zielinski
Alex Zielinski

Reputation: 75

How to get a number closest to the average in c++?

What I'm trying to achieve is to take the average of the numbers stored in the array and find the number which is closest to it. My code compiles, but has an error just after starting. I think it's something to do with the memory handling (I don't feel confident with pointers, etc. yet) Could some nice guy take a look at my code and tell me what's wrong with it? (don't be hard on me, I'm a beginner)

#include <iostream>
#include <cmath>

using namespace std;

double* aver(double* arr, size_t size, double& average);

int main()
{
    double arr[] = {1,2,3,4,5,7};
    size_t size = sizeof(arr)/sizeof(arr[0]);
    double average = 0;
    double* p = aver(arr,size,average);
    cout << *p << " " << average << endl;
}

double* aver(double* arr, size_t size, double& average){
    int i,j,sum;
    double* m = 0;
    int tmp[7];
    for(i=0;i<size;i++)
        sum += arr[i];
    average = sum/size;
    for(j=0;j<size;j++){
        tmp[j] = arr[j] - average;
        if(abs(tmp[j])>*m)
        *m = tmp[j];
    }
    return m;
}

Upvotes: 1

Views: 2140

Answers (3)

Johan Lundberg
Johan Lundberg

Reputation: 27028

building on to the answer by Walter, adding the 'find value closest to average' part:

#include <iostream>
#include <vector>
#include <numeric>
#include <cmath>
#include <algorithm>

int main()
{
  std::vector<double> arr = {1,2,3,4,5,7};
  auto average = std::accumulate(std::begin(arr),std::end(arr),0.0) / arr.size();
  std::cout  << " average = " << average << std::endl;
  auto comp = [average](double left,double right){
     return std::abs(left-average)<std::abs(right-average);  };
  auto mini=std::min_element(std::begin(arr),std::end(arr),comp);
  std::cout << "value closest to average was " << *mini <<std::endl;
}

Alternative (slower) implementation using transform (reduces the number of calls to fabs, but does a copy):

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>

int main()
{
  std::vector<double> arr = {1,2,3,4,5,7};
  auto average = std::accumulate(std::begin(arr),std::end(arr),0.0) / arr.size();
  std::cout  << " average = " << average << std::endl;
  auto pred=[average](double x){return std::abs(x-average);};
  auto arrcpy = arr;
  std::transform(std::begin(arr),std::end(arr),std::begin(arrcpy),pred);
  auto result = std::min_element(std::begin(arrcpy),std::end(arrcpy));
  std::cout << "value closest to average was: " << arr[result-std::begin(arrcpy)];
}

Using a standard algorithm is usually the right thing to do as it is more maintainable. In this case I did not find a way to use a standard algorithm as fast as this (about 30% faster than the first solution above with 10e7 elements and -O2):

  std::pair<double,double> smallest(std::abs(average-arr[0]),arr[0]);
  for(auto a: arr){
    auto v=std::abs(average-a);
    if(v<smallest.first){
       smallest={v,a};
    }
  }
  std::cout << "value closest to average was " << smallest.second <<std::endl;

Upvotes: 1

Walter
Walter

Reputation: 45434

1 What's wrong with your code? The use of pointers and the resulting beginner errors.

2 How to compute the average in C++? Roughly like this:

#include <iostream>
#include <iomanip>
#include <vector>
#include <numeric>

int main()
{
  std::vector<double> arr = {1,2,3,4,5,7};
  auto average = std::accumulate(std::begin(arr),std::end(arr),0.0) / arr.size();
  std::cout  << " average = " << std::setprecision(16) << average << std::endl;
}

(note: compile with option -std=c++11)

3 How to find the number closest to it (the average)? If your computer is IEEE compliant (most are but some compiler optimisations violate that), the result of any arithmetic is rounded to the closest representable number. So, nothing special needs to be done here. However, accumulation of numbers is subject to round-off error and hence loss of precision. This can be minimised by accumulating the numbers in descending order of their absolute value, though there exist pathological cases where the computed accumulation is still rather imprecise. Find out more on, say, wikipedia.

4 How to find the array value closest to it (the average)? One way is shown in Johan's answer. However, it unnecessarily alters the array by performing a partial sort. Better use std::min_element (no need to std::transform):

auto comp = [average](double left, double right)
  { return std::abs(left-average) < std::abs(right-average); };
auto closest = std::min_element(std::begin(arr), std::end(arr), comp);
std::cout << " value closest to average was "
          << *closest << " at position "
          << (closest-std::begin(arr))
          << std::endl;

Upvotes: 2

NPE
NPE

Reputation: 500377

The following

double* m = 0;

sets m to the null pointer. Any attempt to dereference it will result in undefined behaviour.

Change the above to:

double m = 0;

and replace *m with m everywhere, also changing the function's return type to just double.

Finally, you don't need the tmp array.

Upvotes: 3

Related Questions