Find the average within variable number of doubles - java

I have an ArrayList of doubles, and i need to find the average between all numbers.
The amount of Double instances in the arraylist is not constant, could be 2, could be 90
I have been trying for hours to get the algorithm myself but could not get it to work in anyway.
do you have any advice? or maybe you can link me to an existing library to get this average?
Thank you

Create a sum variable:
double sum = 0;
Go through the elements in the list using a for-loop:
for (double d : yourList)
in each iteration, add to sum the current value:
sum += d;
after the loop, to find the average, divide sum with the number of elements in the list:
double avg = sum / yourList.size();
Here's for everyone that thinks this was too simple...
The above solution is actually not perfect. If the first couple of elements in the list are extremely large and the last elements are small, the sum += d may not make a difference towards the end due to the precision of doubles.
The solution is to sort the list before doing the average-computation:
Collections.sort(doublesList);
Now the small elements will be added first, which makes sure they are all counted as accurately as possible and contribute to the final sum :-)

If you like streams:
List<Number> list = Arrays.asList(number1, number2, number3, number4);
// calculate the average
double average = list.stream()
.mapToDouble(Number::doubleValue)
.average()
.getAsDouble();

The definition of the average is the sum of all values, divided by the number of values. Loop over the values, add them, and divide the sum by the size of the list.

If by "average between all numbers" you mean the average of all the doubles in your list, you can simply add all the doubles in your list (with a loop) and divide that total by the number of doubles in your list.

Have you tried:
List<Double> list = new ArrayList<Double>();
...
double countTotal = 0;
for (Double number : list)
{
countTotal += number;
}
double average = countTotal / list.size();

Maybe I haven't got the question... but it's not something like this?
double sum = 0.0;
for (double element : list) {
sum += element;
}
double average = sum / list.size();

If you're worried about overflow as you sum the whole list together, you can use a running average. Because it has more operations, it will be slower than adding everything together and dividing once.
for (int x = 0; x < list.size(); x++)
average = (average / (x+1)) * (x) + list.get(x) / (x+1);

Related

How to divide a total value of 1 between x variables with a biased weighting

I'm tasked with a problem that I'm not quite sure how to solve mathematically.
I am trying to create a method that takes an int array as an argument. the length of the array will vary but will never be zero. The values in the array are not important as the method will overwrite them with the values determined below.
The purpose of the method is to divide a total of 1.0 between each position in the array. This is straightforward enough however an additional complexity is that the division should be biased. The values on the left should be higher than the values on the right (see example output below).
An example would be passing an int array of size 7. I would expect an output similar to:
[.3, .25, .15, .1, .09, .07, 0.04]
where the sum of all the values = 1
I'm using Java but even pseudo code will help!
I'd generate a list of unique random numbers, then normalize it by dividing all of them by their sum.
Then sort and reverse your list.
int n = 7;
// make a list of n unique random numbers
double[] randomValues = new Random().doubles(0, 1).distinct().limit(n).toArray();
// normalize the list and reverse sort it
double sum = Arrays.stream(randomValues).sum();
List<Double> array = Arrays.stream(randomValues).boxed()
.map(d -> d/sum)
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
Your array should now have random values adding up to 1, sorted in reverse order
Caveat:
You might want to actually recalculate the latest value by subtracting the other ones from 1 to minimize rounding errors. It depends on the precision you require.
If you need exact values, you can't work with doubles, work with ints instead (like we do with currencies).
double[] distributeDecreasing(int n) {
Random random = new Random();
double[] values = new double[n];
final double nEps = 0.0001 * n;
double sum = 0.0;
for (int i = 0; i < n; ++n) {
double value = nEps + random.next();
sum += value;
values[i] = value;
}
double sumFrom1 = 0.0;
for (int i = 1; i < n; ++n) {
values[i] /= sum;
sumFrom1 += values[i];
}
if (n > 0) {
values[0] = 1.0 - sumFrom1;
}
Arrays.sort(values, Comparator.reverseOrder());
return values;
}
The biasing done by decreasing order.
The sum 1.0 realised by dividing of the sum of Random.next (between 0 and 1),
plus an epsilon to not be zero.
For minimal floating point error, correct the first element as 1.0 - sum of the rest.
You didn't specify eactly what bias you are looking for or what distribution, but a straightforward aprroach for a biased non-uniform distribution would be:
Draw the first number a1 from [0,1], draw the second number a2 from [0,1-a1], draw the third number a3 from [0,1-a1-a2] and so on. set an as the complement to 1 of the current total, and sort everything at the end.

Averaging Items in an Array, in an Array

I want to loop through my array list roster and for each Student item I want to return the average of an array of grades. I know there are 3 grades so this code works, but how can I implement this if I don't know how many grades are in the Student's grades array before hand?
public static void printAverageGrades(){
System.out.println("Print Average Grades");
for(Student item : roster)
{
double div = roster.size();
**double average = (item.getGrades()[0] + item.getGrades()[1] + item.getGrades()[2]) / div;**
System.out.printf("%.2f \n", average);
}
}
You can do this two ways. The more preferable Java 8 way is listed first.
Use Arrays.stream to encapsulate the array as a stream, then getting the average is just a couple of method calls away.
double avg = Arrays.stream(item.getGrades()).average().getAsDouble();
Use another for loop and keep a running total of the elements. Don't forget to manually coerce your numerator to a double or you'll run into integer division.
int sum = 0;
for(int i : item.getGrades()) {
sum += i;
}
double avg = (sum * 1.0) / item.getGrades().length;
In practice, never hard-code your index locations. You can always get the length of an array using .length.
You can just use this function, in which would iterate through the array, by dynamically iterating through for loop, we compute sum.
Then average would be divided by the length of array.
double getAverage(double[] data)
{
double sum = 0.0;
for(double a : data)
sum += a;
return sum/data.length;
}

Is it possible to change range of random numbers?

I have an array of non repeating random numbers generated in the range of 0-20 as
int[] numbers = {6,14,11,18,13};
Now, I want to convert these numbers in the range of 0-10 and I want result as non repeating as well.
Is it possible ?
Any kind of suggestion would be appreciated.
Dividing them by two is a good solution since you have the same size of input and it keeps the uniformity :
For every number every number x in [0;10[, it can come from two numers in [0;20[ : 2*x and 2*x+1.
It will give you the same result for numbers like 10 and 11 but who cares?
Here are three methods that I have come up with:
// divide by 2
Arrays.stream(numbers).map(x -> x / 2);
// subtract 10 from everything that's > 10
Arrays.stream(numbers).map(x -> x > 10 ? x - 10 : x);
// remove every number that's > 10
Arrays.stream(numbers).filter(x -> x < 10);
Now that I know that you don't want repeating numbers, you should probably remove all the duplicates by calling distinct.
You could subtract 10 if randomly generated number is greater than 10. I know it's not a proper solution but it'll definitely work for you.
If you are using Java8, you can use the below code to generate unique random numbers:
int[] rand = new Random().ints(0, 11).distinct().limit(5).toArray();
This code generates 5 unique random numbers ranging from 0 to 10.
The range 0-20 contains 21 numbers, 0-10 contains 11 numbers. So the only solution that leads to uniformly distributed numbers is to take just the numbers in the range 0-10 from the original set and discard numbers in the range 11-20.
You can iterate the array in a enhanced for loop with a if condition.You already have non repeated value since you get values from number array.You can use arraylist or any other data structure to store random numbers less than 10.
ArrayList<Integer> numberList = new ArrayList<>();
for(int number:numbers){
if(number<10){
numberList.add(number);
}
}
One possible solution is to use helper array with double values.
The algorithm is next.
Create an array with double values from origin array and values are divided by 2.
Find the maximum value in the new double array.
Scale all values in the new double array. This is should be done by multiplying all values by a coefficient. The coefficient is coefficient = 10 / max.
Convert the new double array to integer values. One of possible strategy is to round double values.
Notes
It is possible that this solution has some 'bug' and will not work for all combination of random numbers. In that case, the conversion double values to integer should be improved.
If origin array contains more than 10 values it is not possible to map the array to a new array.
And this is the code for it.
public static void main(String[] args) {
int[] numbers = { 6, 14, 11, 18, 13 };
// crate a new array with double values
double[] newNumbers = new double[numbers.length];
for (int i = 0; i < numbers.length; i++) {
newNumbers[i] = (double) numbers[i] / 2;
}
// get coefficient
double max = maxValue(newNumbers);
double coefficient = 10 / max;
// scale numbers
for (int i = 0; i < newNumbers.length; i++) {
newNumbers[i] = newNumbers[i] * coefficient;
}
int[] newIntNumbers = new int[newNumbers.length];
for (int i = 0; i < newNumbers.length; i++) {
newIntNumbers[i] = (int) Math.round(newNumbers[i]);
}
System.out.println(Arrays.toString(newNumbers));
System.out.println();
System.out.println(Arrays.toString(newIntNumbers));
}
private static double maxValue(double[] array) {
double max = Double.MIN_VALUE;
for (double num : array) {
max = Math.max(max, num);
}
return max;
}

What's the best way to find the average value of objects stored in an arraylist?

I have a method that returns a count of the objects that have a higher value than the average of all the objects within an ArrayList.
The 'User' object has the integer value stored within it's class (level).
My method works but I'm wondering if there's a better way of finding the average of all object values?
public int bestPlayers() {
ArrayList<User> players = new ArrayList<User>();
int bestPlayers = 0;
int totalPlayerLevel = 0;
double averageLevel = 0;
for (int i = 0; i < players.size(); i++) {
totalPlayerLevel += players.get(i).level;
}
averageLevel = totalPlayerLevel / players.size();
for (int i = 0; i < players.size(); i++) {
if (players.get(i).level > averageLevel) {
bestPlayers++;
}
}
return bestPlayers;
}
Java 8 provides a better way, using IntStream#average():
double average = players.stream()
.mapToInt(p -> p.level)
.average()
.orElse(0);
Then to output the number of "above average" players:
return (int) players.stream()
.filter(p -> p.level > average)
.count();
Your code works in O(n), since you travel through array twice. Well, since you have to calculate average, that means you have to travel at least once, so there is no option for performance faster then O(n).
Second thing you do, you must count players with level higher then average. Only speedup here could be if you have sorted array (have before, not calculating now, since its O(nlogn), then you can find first one with higher level then average and calculate number of the rest. That would cost O(logn), but its performance is still O(n), since you calculated average.

Adding several small float values overflows into infinity

I'm having trouble with a loop that is supposed to add together a number of very small float values to eventually produce a weighted average, like so:
for(int k = 0; k < slopes.size(); k++){
if(slopes.get(k).isClimbing() == false){
float tempWeight = (slopes.get(k).getzDiff() / highestFallZ);
weight += tempWeight;
highestFallX += (slopes.get(k).getEndX() * tempWeight);
}
highestFallX = highestFallX / weight;
}
Essentially what it does is produce a weighting from one attribute of an object (the result of which is always between 0 and 1), then modifies another attribute of the same object by that weighting and adds the result to a running tally, which is in the end divided by the sum of the weightings. All values and variables are of the type float.
Now the problem I'm running into is that within just a few steps the running tally (highestFallX) is growing exponentially into -infinity. I've run some diagonistics, and they've shown that each individual value added was in the range between -1 and -1*10^-5 (after the multiplication with the weighting) and no more than 60 of them were added together, so neither overflow nor underflow should be a problem. For comparison, here is a list of the last value added (LastFallX) and the tally (HighestFallX) during the first few steps of the loop:
LastFallX: -1.2650555E-4
HighestFallX: -1.2650555E-4
LastFallX: -6.3799386E-4
HighestFallX: -0.25996128
LastFallX: -4.602447E-4
HighestFallX: -87.01444
LastFallX: -0.0020183846
HighestFallX: -16370.462
LastFallX: -4.158747E-5
HighestFallX: -826683.3
From there on it keeps growing exponentially, and hits -infinity within about 10 more loops. The highestFallX variable isn't referenced nor modified by anything else during this loop.
One way of expressing an average is:
totalValue += nextValue * nextWeight;
totalWeight += nextWeight;
average = totalValue / totalWeight;
This is prone to overflow in totalValue, as you have seen.
Instead you can also do:
totalWeight += nextWeight;
average += ((nextValue * nextWeight) - average) / totalWeight;
In your case, I think that might look like:
for(int k = 0; k < slopes.size(); k++){
if(slopes.get(k).isClimbing() == false){
float tempWeight = (slopes.get(k).getzDiff() / highestFallZ);
weight += tempWeight;
float weightedValue = (slopes.get(k).getEndX() * tempWeight);
float delta = weightedValue - highestFallX;
highestFallX += delta / weight;
}
}
but I'm still trying to work out exactly how your weighted average should work, so I'm a little unsure of that last bit.
How precise does it have to be? You could simply drop everything past the third decimal to make sure it doesn't run into issues with the massive amounts of digits

Categories

Resources