Skip to content

Instantly share code, notes, and snippets.

@bemusementpark
Created May 21, 2016 13:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bemusementpark/1489ac38f6a2dead03175d0c7eeff928 to your computer and use it in GitHub Desktop.
Save bemusementpark/1489ac38f6a2dead03175d0c7eeff928 to your computer and use it in GitHub Desktop.
Online implementations of Mean, Median and Mode
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