Created
May 21, 2016 13:47
-
-
Save bemusementpark/1489ac38f6a2dead03175d0c7eeff928 to your computer and use it in GitHub Desktop.
Online implementations of Mean, Median and Mode
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.*; | |
public class Averages { | |
public static void main(String[] args) { | |
Scanner s = new Scanner(System.in); | |
Mean mean = new Mean(); | |
GeometricMean geometricMean = new GeometricMean(); | |
Median<Double> median = new Median<>((a, b) -> (a + b) / 2); | |
Mode<Double> mode = new Mode<>(); | |
while (true) { | |
double d = s.nextDouble(); | |
System.out.println("mean: " + mean.add(d).calculate()); | |
System.out.println("geometric mean: " + geometricMean.add(d).calculate()); | |
System.out.println("median: " + median.add(d).calculate()); | |
System.out.println("mode: " + mode.add(d).calculate()); | |
} | |
} | |
/** | |
* Calculate the mean of a series of numbers. | |
*/ | |
private static class Mean { | |
double total = 0; | |
long count = 0; | |
/** | |
* add a number to the list of numbers and calculate the average by calling {@link #calculate()} | |
* <p> | |
* O(1) time complexity | |
* | |
* @return this object so methods can be chained | |
*/ | |
Mean add(double value) { | |
count++; | |
total += value; | |
return this; | |
} | |
/** | |
* O(1) time complexity | |
* | |
* @return the average of all numbers given by {@link #add(double)} | |
*/ | |
double calculate() { | |
return count != 0 ? total / count : 0; | |
} | |
} | |
/** | |
* Calculate the geometric mean of a series of numbers | |
*/ | |
private static class GeometricMean { | |
int count = 0; | |
double total = 1; | |
/** | |
* O(1) time complexity | |
*/ | |
GeometricMean add(double d) { | |
count++; | |
total *= d; | |
return this; | |
} | |
/** | |
* O(1) time complexity | |
*/ | |
double calculate() { | |
return Math.pow(total, 1d / count); | |
} | |
} | |
/** | |
* Calculate the median of a series of {@link Comparable} items. | |
*/ | |
private static class Median<T extends Comparable<? super T>> { | |
private final Averager<T> averager; | |
PriorityQueue<T> low = new PriorityQueue<>((a, b) -> b.compareTo(a)); | |
PriorityQueue<T> high = new PriorityQueue<>(); | |
/** | |
* @param averager an {@link Averager} that will return the average of two values in the event that {@link #calculate()} | |
* is called when the list is even and the average of the 2 median values must be returned. | |
*/ | |
Median(Averager<T> averager) { | |
this.averager = averager; | |
} | |
/** | |
* Adds an element to the median calculation returned by {@link #calculate()}. | |
* <p> | |
* Time complexity is O(log(n)) As {@link PriorityQueue#add(Object)} is a log(n) operation. | |
* Upon transitioning the remove-add sequences with an O(1) decreaseKey() method, like that in a Fibonnaci | |
* Heap, this method could take O(1) time. | |
*/ | |
Median<T> add(T value) { | |
if (low.size() > high.size()) { | |
if (value.compareTo(low.peek()) >= 0) { | |
high.add(value); | |
} else { | |
high.add(low.peek()); | |
// TODO use a more efficient Heap that supports O(1) decrease-key | |
low.remove(); | |
low.add(value); | |
} | |
} else if (high.size() > low.size()) { | |
if (value.compareTo(high.peek()) <= 0) { | |
low.add(value); | |
} else { | |
low.add(high.peek()); | |
// TODO use a more efficient Heap that supports O(1) decrease-key | |
high.remove(); | |
high.add(value); | |
} | |
} else { // lo.size == hi.size | |
if (low.isEmpty()) { | |
low.add(value); | |
} else if (value.compareTo(low.peek()) > 0) { | |
low.add(high.peek()); | |
// TODO use a more efficient Heap that supports O(1) decrease-key | |
high.remove(); | |
high.add(value); | |
} else { | |
high.add(low.peek()); | |
// TODO use a more efficient Heap that supports O(1) decrease-key | |
low.remove(); | |
low.add(value); | |
} | |
} | |
return this; | |
} | |
/** | |
* @return The median of the series numbers given in {@link #add(Comparable)}. | |
* <p> | |
* This is calculated in O(1) time complexity. | |
*/ | |
T calculate() { | |
if (low.size() == high.size()) { | |
return averager.call(low.peek(), high.peek()); | |
} else if (low.size() > high.size()) { | |
return low.peek(); | |
} else { | |
return high.peek(); | |
} | |
} | |
interface Averager<T> { | |
T call(T a, T b); | |
} | |
} | |
/** | |
* finds the mathematical mode (most occurring element(s)) of the series given in {@link #add(Object)} | |
*/ | |
private static class Mode<T> { | |
int bestScore = 0; | |
Set<T> best = new HashSet<>(); | |
Map<T, Integer> scores = new HashMap<>(); | |
/** | |
* O(1) time complexity | |
* <p> | |
* insert that calculates the mode after each insertion. | |
* | |
* @param value The value to add to the data set from which the mode will be calculated from in {@link #calculate()} | |
* @return this object for chaining. | |
*/ | |
Mode<T> add(T value) { | |
int score = 1; | |
if (scores.containsKey(value)) { | |
score += scores.get(value); | |
} | |
scores.put(value, score); | |
if (score > bestScore) { | |
bestScore = score; | |
best.clear(); | |
best.add(value); | |
} else if (score == bestScore) { | |
best.add(value); | |
} | |
return this; | |
} | |
/** | |
* @return the most occurring value (or values) that have been {@link #add(Object)}ed. | |
* <p> | |
* O(1) time | |
*/ | |
Set<T> calculate() { | |
return best; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment