package org.streaminer.stream.quantile;
import java.util.Random;
/**
* Implementation of the Frugal2U Algorithm.
*
* Reference:
* Ma, Qiang, S. Muthukrishnan, and Mark Sandler. "Frugal Streaming for
* Estimating Quantiles." Space-Efficient Data Structures, Streams, and
* Algorithms. Springer Berlin Heidelberg, 2013. 77-96.
* Available at: http://arxiv.org/abs/1407.1121
*
* Original code: <https://github.com/dgryski/go-frugal>
* More info: http://blog.aggregateknowledge.com/2013/09/16/sketch-of-the-day-frugal-streaming/
*
* @author Maycon Viana Bordin <mayconbordin@gmail.com>
*/
public class Frugal2U implements IQuantiles<Integer> {
private final Quantile quantiles[];
public Frugal2U(Quantile[] quantiles) {
this.quantiles = quantiles;
}
public Frugal2U(double[] quantiles, int initialEstimate) {
this.quantiles = new Quantile[quantiles.length];
for (int i=0; i<quantiles.length; i++) {
this.quantiles[i] = new Quantile(initialEstimate, quantiles[i]);
}
}
@Override
public void offer(Integer value) {
for (Quantile q : quantiles) {
q.insert(value);
}
}
@Override
public Integer getQuantile(double q) throws QuantilesException {
for (Quantile quantile : quantiles) {
if (quantile.q == q)
return quantile.m;
}
return 0;
}
public class Quantile {
int m;
double q;
int step = 1;
int sign = 0;
Random r = new Random(new Random().nextInt());
Quantile(int estimate, double quantile) {
m = estimate;
q = quantile;
}
void insert(int s) {
if (sign == 0) {
m = s;
sign = 1;
return;
}
double rnd = r.nextDouble();
if (s > m && rnd > 1-q) {
step += sign * f(step);
if (step > 0) {
m += step;
} else {
m += 1;
}
if (m > s) {
step += (s - m);
m = s;
}
if (sign < 0 && step > 1) {
step = 1;
}
sign = 1;
} else if (s < m && rnd > q) {
step += -sign * f(step);
if (step > 0) {
m -= step;
} else {
m--;
}
if (m < s) {
step += (m - s);
m = s;
}
if (sign > 0 && step > 1) {
step = 1;
}
sign = -1;
}
}
int f(int step) {
return 1;
}
}
}