Skip to content

Commit 28d7bb9

Browse files
committed
Added Java implementation of Average
Additionally added a unit test for inequalities which must hold, for all average calculations
1 parent eb0f3f6 commit 28d7bb9

2 files changed

Lines changed: 248 additions & 0 deletions

File tree

Average/Java/joshimoo/Average.java

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package average;
2+
3+
import java.util.*;
4+
import java.util.stream.DoubleStream;
5+
6+
/**
7+
* @author Joshua Moody (joshimoo@hotmail.de)
8+
*/
9+
public final class Average {
10+
/**
11+
* Don't let anyone instantiate this class.
12+
*/
13+
private Average() {}
14+
15+
/**
16+
* calculate a regular arithmetic mean average
17+
* @throws NoSuchElementException if the array is empty
18+
*/
19+
public static double arithmeticMean(double... numbers) {
20+
assert numbers != null && numbers.length > 0;
21+
return DoubleStream.of(numbers).average().getAsDouble();
22+
}
23+
24+
/**
25+
* calculates a weighted mean average
26+
* numbers and weights need to have the same count
27+
*/
28+
public static double weightedMean(double[] numbers, double[] weights) {
29+
assert numbers.length == weights.length;
30+
double weightedSum = 0;
31+
for (int i = 0; i < numbers.length; i++) {
32+
weightedSum += numbers[i] * weights[i];
33+
}
34+
35+
return weightedSum / DoubleStream.of(weights).sum();
36+
}
37+
38+
/**
39+
* calculates the harmonic mean average
40+
* according to this formula: n/(1/x1 + 1/x2 + ... + 1/xn)
41+
*/
42+
public static double harmonicMean(double... numbers) {
43+
return numbers.length / DoubleStream.of(numbers).map(x -> 1.0 / x).sum();
44+
}
45+
46+
/**
47+
* calculates the contra harmonic mean average
48+
* according to this formula: (x1^2 + x2^2 + ... + xn^2)/(x1 + x2 + ... + xn)
49+
*/
50+
public static double contraHarmonicMean(double... numbers) {
51+
return DoubleStream.of(numbers).map(x -> Math.pow(x, 2.0)).sum() / DoubleStream.of(numbers).sum();
52+
}
53+
54+
/**
55+
* calculates the geometric mean average
56+
* for small amount of numbers according to this formula: (x1*x2*...xn) ^ (1/n)
57+
* for large amount of numbers, by summing the logarithms of each x
58+
*/
59+
public static double geometricMean(double... numbers) {
60+
// TODO: For large numbers, consider summing the logarithms of each x
61+
// (x1*x2*...xn) ^ (1/n)
62+
return Math.pow(DoubleStream.of(numbers).reduce(1.0, (a, x) -> (a * x)), 1.0d / numbers.length);
63+
}
64+
65+
/**
66+
* calculates the quadratic mean average
67+
* according to this formula: sqrt( (x1)^2+(x2)^2+(x3)^2+...+(xn)^2 /n )
68+
*/
69+
public static double quadraticMean(double... numbers) {
70+
return generalizedMean(numbers, 2.0);
71+
}
72+
73+
/**
74+
* calculates a generalized mean average
75+
* according to this formula: y-root( (x1)^y+(x2)^y+(x3)^y+...+(xn)^y / n )
76+
*/
77+
public static double generalizedMean(double[] numbers, double power) {
78+
return Math.pow(arithmeticMean(DoubleStream.of(numbers).map(x -> Math.pow(x, power)).toArray()), 1.0 / power);
79+
}
80+
81+
/**
82+
* Get the mid value beetwen min and max
83+
*/
84+
public static double midrange(double... numbers) {
85+
DoubleSummaryStatistics stat = DoubleStream.of(numbers).summaryStatistics();
86+
return arithmeticMean(stat.getMin(), stat.getMax());
87+
}
88+
89+
/**
90+
* calculates the median average for sorted list of numbers
91+
* @param numbers needs to be sorted
92+
* @return mean average of left and right for even numbers, else mid element
93+
*/
94+
public static double median(double... numbers) {
95+
if (numbers.length % 2 == 0) {
96+
double left = numbers[(numbers.length / 2) - 1];
97+
double right = numbers[numbers.length / 2];
98+
return arithmeticMean(left, right);
99+
} else {
100+
double mid = numbers[((numbers.length + 1) / 2) - 1];
101+
return mid;
102+
}
103+
}
104+
105+
/**
106+
* calculates the mode average
107+
* which returns the element, with highest occurrence count
108+
*/
109+
public static double mode(double... numbers) {
110+
// Stupid Type System, all this boxing :(
111+
Map<Double, Integer> frequencies = new HashMap<>();
112+
113+
for(double key : numbers) {
114+
frequencies.put(key, frequencies.getOrDefault(key, 0) + 1);
115+
}
116+
117+
Double maxElement = numbers[0];
118+
for (Map.Entry<Double, Integer> element : frequencies.entrySet()) {
119+
if(element.getValue() > frequencies.get(maxElement)) {
120+
maxElement = element.getKey();
121+
}
122+
}
123+
124+
return maxElement;
125+
}
126+
127+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package average;
2+
3+
import org.junit.Test;
4+
import java.util.stream.DoubleStream;
5+
import static org.junit.Assert.*;
6+
7+
/**
8+
* @author Joshua Moody (joshimoo@hotmail.de)
9+
*/
10+
public class AverageTest {
11+
private static final double DELTA = 0.01;
12+
13+
@Test
14+
public void testAverageInequality() throws Exception {
15+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
16+
17+
/**
18+
* Mean Inequalities:
19+
* Some means are in a constant relationship to one another.
20+
* If we denote the arithmentic mean of x and y by A,
21+
* their geometric mean by G,
22+
* their harmonic mean by H,
23+
* their root mean square (quadratic mean) by R,
24+
* and their contraharmonic mean by C,
25+
*
26+
* then the following chain of inequalities is always true
27+
* C >= R >= A >= G >= H
28+
*/
29+
30+
double contraHarmonic = Average.contraHarmonicMean(numbers);
31+
double quadratic = Average.quadraticMean(numbers);
32+
double arithmetic = Average.arithmeticMean(numbers);
33+
double geometric = Average.geometricMean(numbers);
34+
double harmonic = Average.harmonicMean(numbers);
35+
assertTrue("Your Average inequalities are not correct", (contraHarmonic >= quadratic) && (quadratic >= arithmetic) && (arithmetic >= geometric) && (geometric >= harmonic));
36+
}
37+
38+
@Test
39+
public void testArithmeticMean() throws Exception {
40+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
41+
double expected = DoubleStream.of(numbers).average().getAsDouble();
42+
assertEquals(expected, Average.arithmeticMean(numbers), DELTA);
43+
}
44+
45+
@Test
46+
public void testWeightedMean() throws Exception {
47+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
48+
double[] weights = new double[] { 1, 1, 1, 1, 1 };
49+
double expected = DoubleStream.of(numbers).average().getAsDouble();
50+
assertEquals(expected, Average.weightedMean(numbers, weights), DELTA);
51+
}
52+
53+
@Test
54+
public void testHarmonicMean() throws Exception {
55+
// n/(1/x1 + 1/x2 + ... + 1/xn)
56+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
57+
double expected = numbers.length / DoubleStream.of(numbers).map(x -> 1.0 / x).sum();
58+
assertEquals(expected, Average.harmonicMean(numbers), DELTA);
59+
}
60+
61+
@Test
62+
public void testContraHarmonicMean() throws Exception {
63+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
64+
double expected = DoubleStream.of(numbers).map(x -> Math.pow(x, 2.0)).sum() / DoubleStream.of(numbers).sum();
65+
assertEquals(expected, Average.contraHarmonicMean(numbers), DELTA);
66+
}
67+
68+
@Test
69+
public void testGeometricMean() throws Exception {
70+
// (x1*x2*...xn) ^ (1/n)
71+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
72+
double expected = Math.pow((numbers[0] * numbers[1] * numbers[2] * numbers[3] * numbers[4]), 1.0 / numbers.length);
73+
assertEquals(expected, Average.geometricMean(numbers), DELTA);
74+
}
75+
76+
@Test
77+
public void testQuadraticMean() throws Exception {
78+
// sqrt((x1)^2+(x2)^2+(x3)^2+...+(xn)^2 /n)
79+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
80+
double expected = Math.sqrt(DoubleStream.of(numbers).map(x -> Math.pow(x, 2)).sum() / numbers.length);
81+
assertEquals(expected, Average.quadraticMean(numbers), DELTA);
82+
}
83+
84+
@Test
85+
public void testGeneralizedMean() throws Exception {
86+
// y-root((x1)^y+(x2)^y+(x3)^y+...+(xn)^y / n)
87+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
88+
double power = 4;
89+
double expected = Math.pow((DoubleStream.of(numbers).map(x -> Math.pow(x, 4)).sum() / numbers.length), 1.0 / power);
90+
assertEquals(expected, Average.generalizedMean(numbers, power), DELTA);
91+
}
92+
93+
@Test
94+
public void testMidrange() throws Exception {
95+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
96+
double expected = (numbers[0] + numbers[numbers.length - 1]) / 2.0;
97+
assertEquals(expected, Average.midrange(numbers), DELTA);
98+
}
99+
100+
@Test
101+
public void testMedianEven() throws Exception {
102+
double[] numbers = new double[] { 1, 2, 3, 4, 5, 6};
103+
double expected = (numbers[(numbers.length / 2) - 1] + numbers[numbers.length / 2]) / 2.0;
104+
assertEquals(expected, Average.median(numbers), DELTA);
105+
}
106+
107+
@Test
108+
public void testMedianOdd() throws Exception {
109+
double[] numbers = new double[] { 1, 2, 3, 4, 5 };
110+
double expected = numbers[((int) Math.floor(numbers.length / 2))];
111+
assertEquals(expected, Average.median(numbers), DELTA);
112+
}
113+
114+
@Test
115+
public void testMode() throws Exception {
116+
// frequency based average
117+
double[] numbers = new double[] { 1, 2, 3, 3, 3, 4, 5 };
118+
double expected = 3;
119+
assertEquals(expected, Average.mode(numbers), DELTA);
120+
}
121+
}

0 commit comments

Comments
 (0)