How to get a number closest to the average in c++? - 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;
}

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.

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;

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;

Related

Is there a limit on the number of values added to a boost::accumulator?

Is there a limit on how many values that can be added to a boost::accumulator? If a large number of entries were added, is there any point in which the accumulator would cease to work properly or is the internal algorithm robust enough to account for a set of values approaching infinity?
#include <iostream>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
using namespace boost::accumulators;
int main()
{
// Define an accumulator set for calculating the mean and the
// 2nd moment ...
accumulator_set<double, stats<tag::mean, tag::moment<2> > > acc;
// push in some data ...
for (std::size_t i=0; i<VERY_LARGE_NUMBER; i++)
{
acc(i);
}
// Display the results ...
std::cout << "Mean: " << mean(acc) << std::endl;
std::cout << "Moment: " << moment<2>(acc) << std::endl;
return 0;
}
If your int is a 32 bit integer, you'll get a signed integer overflow at 46341 * 46341 when calculating moment<2> and your program therefore has undefined behavior.
To avoid that, cast i to the type you're using in the accumulator:
acc(static_cast<double>(i));
This will now have the same limits as a normal double. You can add as many elements as you'd like to it as long as you don't exceed the limit (std::numeric_limits<double>::max()) for a double in the internal moment calculations (x2 for moment<2> or a sum that exceeds the limit).
The accumulator statistics do not account for overflow, so you need to select the accumulator type carefully. It doesn't need to match the initial type of the objects you are adding—you can cast it when accumulating, then get the statistics and cast it back to the original type.
You can see it with this simple example:
#include <bits/stdc++.h>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
using namespace boost::accumulators;
int main(void) {
accumulator_set<int8_t, features<tag::mean>> accInt8;
accumulator_set<double, features<tag::mean>> accDouble;
int8_t sum = 0; // range of int8_t: -128 to 127
for (int8_t i = 1; i <= 100; i++) {
sum += i; // this will overflow!
accInt8(i); // this will also overflow
accDouble((double)i);
}
std::cout << "sum from 1 to 100: " << (int)sum << " (overflow)\n";
std::cout << "mean(<int8_t>): " << extract::mean(accInt8) << " (overflow)\n";
std::cout << "mean(<double>): " << (int)extract::mean(accDouble) << "\n";
return 0;
}
I used int8_t which has a very small range (-128 to 127) to demonstrate that getting the mean from values 1 to 100 (which should be 50) overflows if you use int8_t as the internal type for the accumulator_set.
The output is:
sum from 1 to 100: -70 (overflow)
mean(<int8_t>): -7 (overflow)
mean(<double>): 50

Code Review for Stroustrup - Principles of Programming - Ch - 4 - Question:3 - Error: Vector subscript out of range

Read a sequence of double values into a vector. Think of each value as
the distance between two cities along a given route. Compute and print
the total distance (the sum of all distances). Find and print the smallest
and greatest distance between two neighboring cities. Find and print the
mean distance between two neighboring cities.
The problem that I am having is that I am getting a debugging error stating that my vector subscript is out of range. I can't seem to see where that is occurring.
#include "pch.h"
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <cmath>
using std::cout;
using std::cin;
using std::vector;
using std::string;
int main()
{
// Read a sequence of double values into a vector
vector <double> distance; // declaring the vector named "distance"
double sum = 0;
double smallest;
double greatest;
for (double x; cin >> x;) { // read into distance, to terminate putting values in vector use anything that is not of variable type of vector
distance.push_back(x); // put distance into vector
cout << '\n';
for (int i = 0; i < distance.size(); i = i + 1) { // keeping track of elements in vector by displaying them
cout << distance[i] << '\n';
}
}
for (int i = 0; i < distance.size(); i = i + 1) { // adding up all values of vector by iterating through all elements
sum = sum + distance[i];
}
cout << "The total sum of all the elements in the vecotr is: " << sum << '\n';
for (int i = 0; i < distance.size(); i = i + 1) { // determining the smallest value in the vector
if (smallest <= distance[i]) {
smallest = distance[i];
}
}
cout << "The smallest value in the vector is: " << smallest << '\n';
for (int i = 0; i < distance.size(); i = i + 1) { // determining the greatest value in the vector
if (greatest >= distance[i]) {
greatest = distance[i];
}
}
cout << "The smallest value in the vector is: " << smallest << '\n';
cout << "The mean distance between two neigbouring cities is: " << sum / distance.size() << '\n';
}
Bjarne wants you to find the appropriate function in the standard library for the particular problem.
E.g.
auto total_distance = std::accumulate(distance.begin(), distance.end(), 0.);
"The sum of the elements are..."
Look around https://en.cppreference.com/w/cpp/algorithm most of the function calls you want/need are described there.
Run your program like
myprog < inputdoubles.txt

Long double overflows but value smaller than maximum representable value

I'm trying to compute a series using C++.
The series is:
(for those wondering)
My code is the following:
#include <iostream>
#include <fstream>
#include <cmath> // exp
#include <iomanip> //setprecision, setw
#include <limits> //numeric_limits (http://en.cppreference.com/w/cpp/types/numeric_limits)
long double SminOneCenter(long double gamma)
{
using std::endl; using std::cout;
long double result=0.0l;
for (long double k = 1; k < 1000 ; k++)
{
if(isinf(pow(1.0l+pow(gamma,k),6.0l/4.0l)))
{
cout << "infinity for reached for gamma equals: " << gamma << "value of k: " << k ;
cout << "maximum allowed: " << std::numeric_limits<long double>::max()<< endl;
break;
}
// CAS PAIR: -1^n = 1
if ((int)k%2 == 0)
{
result += pow(4.0l*pow(gamma,k),3.0l/4.0l) /(pow(1+pow(gamma,k)),6.0l/4.0l);
}
// CAS IMPAIR:-1^n = -1
else if ((int)k%2!=0)
{
result -= pow(4.0l*pow(gamma,k),3.0l/4.0l) /(pow(1+pow(gamma,k)),6.0l/4.0l);
//if (!isinf(pow(k,2.0l)*zeta/2.0l))
}
// cout << result << endl;
}
return 1.0l + 2.0l*result;
}
Output will be, for instance with gamma = 1.7 :
infinity reached for gamma equals: 1.7 value of k: 892
The maximum value a long double can represent, as provided by the STL numeric_limits, is: 1.18973e+4932.
However (1+1.7^892)= 2.19.... × 10^308 which is way lower than 10^4932, so it shouldn't be considered as infinity.
Provided my code is not wrong (but it very well might be), could anyone tell me why the discussed code evals to infinity when it should not?
You need to use powl rather than pow if you want to supply long double arguments.
Currently you are hitting the numeric_limits<double>::max() in your pow calls.
As an alternative, consider using std::pow which has appropriate overloads.
Reference http://en.cppreference.com/w/c/numeric/math/pow

My small std::unique example not working

I'm trying to use cxx-11's std::unique() to find
the unique elements in an array:
#include <iostream>
#include <algorithm>
#include <vector>
#include <typeinfo>
int main(){
const int n=11;
double x[n],a3[n],a1[n];
x[0]=-0.717778;
x[1]=-0.496843;
x[2]=-0.429063;
x[3]=-0.3596;
x[4]=-0.205607;
x[5]=0.0730536;
x[6]=0.138018;
x[7]=0.585526;
x[8]=2.40104;
x[9]=3.75268;
x[10]=4.55704;
a3[0]=0.790832;
a3[1]=0.569896;
a3[2]=0.502116;
a3[3]=0.432653;
a3[4]=0.343625;
a3[5]=0.512472;
a3[6]=0.56708;
a3[7]=1.01459;
a3[8]=2.32799;
a3[9]=3.67962;
a3[10]=4.48398;
std::cout.precision(10);
std::copy(a3,a3+n,a1);
for(int i=0;i<n;i++) a1[i]+=x[i];
std::sort(a1,a1+n);
for(int i=0;i<n;i++) std::cout << a1[i] << std::endl;
std::cout << "---" << std::endl;
int n_1=std::unique(a1,a1+n)-a1;
std::cout << "length of unique subvector " << n_1 << std::endl;
std::cout << "---" << std::endl;
for(int i=0;i<n_1;i++) std::cout << a1[i] << std::endl;
std::cout << "---" << std::endl;
}
but when I'm running this code (link to coliru)
it returns:
original array
0.073053
0.073053
0.073053
0.073054
0.138018
0.5855256
0.705098
1.600116
4.72903
7.4323
9.04102
---
length of unique subarray 10
---
unique array
0.073053
0.073053
0.073054
0.138018
0.5855256
0.705098
1.600116
4.72903
7.4323
9.04102
---
the unique array still contains a duplicate (and so is wrong)!
what am I doing wrong?
Let's try with a bit more precision, std::cout.precision(20):
0.073052999999999979064
0.073053000000000034575
0.073053999999999952308
0.13801800000000000179
0.58552559999999997942
0.70509800000000000253
1.6001160000000000938
4.7290299999999998448
7.4322999999999996845
9.0410199999999996123
Since most decimal fractions can't be represented exactly by a binary floating point format, slightly different rounding errors cause slightly different results.
In general, you can't expect the results of different floating point calculations to be exactly equal, even if the corresponding calculations applied to mathematical real numbers would be.
You could instead test for "almost equality", carefully choosing a tolerance that's appropriate for your numerical domain. unique allows you to specify your own predicate, instead of a simple equality test:
std::unique(a1,a1+n,[](double x, double y){return std::abs(x-y) < tolerance;});
How about:
int n_1 = std::unique(a1,a1+n,
[](float a, float b)
{
return std::fabs(a-b) < 10e-9;
}
) - a1;
?
Live demo link

using std::accumulate, getting a "too many arguments" error

std::accumulate is supposed to be able to take either three or four arguments. In the former case it's just when you want to add the numbers in a container; in the latter case it's when you want to first apply a function and then add them. I've written code that generates a vector of random doubles and then does some stuff to them: first it performs an x->x^2 transform using std::transform, then adds them up with std::accumulate, and lastly combines the two actions into one using the four-argument version of std::accumulate.
Everything works except for step 3. Looking at the example code to be found at http://www.cplusplus.com/reference/numeric/accumulate/, I can't see any reason why this shouldn't work, but I'm getting a "Too many arguments error" when compiling (I'm using XCode. For some reason it doesn't tell me the line number, but I've narrowed it down to the second usage of std::accumulate). Any insights?
#include <numeric>
#include <time.h>
#include <math.h>
using std::vector;
using std::cout;
using std::endl;
double square(double a) {
return a*a;
}
void problem_2_1() {
vector<double> original;
//GENERATE RANDOM VALUES
srand((int)time(NULL));//seed the rand function to time
for (int i=0; i<10; ++i) {
double rand_val = (rand() % 100)/10.0;
original.push_back(rand_val);
cout << rand_val << endl;
}
//USING TRANSFORM
vector<double> squared;
squared.resize(original.size());
std::transform(original.begin(), original.end(), squared.begin(), square);
for (int i=0; i<original.size(); ++i) {
std::cout << original[i] << '\t' << squared[i] << std::endl;
}
//USING ACCUMULATE
double squaredLength = std::accumulate(squared.begin(), squared.end(), 0.0);
double length = sqrt(squaredLength);
cout << "Magnitude of the vector is: " << length << endl;
//USING 4-VARIABLE ACCUMULATE
double alt_squaredLength = std::accumulate(original.begin(), original.end(), 0.0, square);
double alt_length = sqrt(alt_squaredLength);
cout << "Magnitude of the vector is: " << alt_length << endl;
}
The fourth argument to that std::accumulate overload needs to be a binary operator. Currently you are using a unary one.
std::accumulate performs a binary operation between successive elements in a container, hence the need for a binary operator. The fourth argument replaces the default binary operation, addition. It does not apply a unary operation and then perform addition. If you want to square the elements and then add them, you would need something like
double addSquare(double a, double b)
{
return a + b*b;
}
Then
double x = std::accumulate(original.begin(), original.end(), 0.0, addSquare);