/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, a copy is available at
* http://www.r-project.org/Licenses/
*/
package jdistlib;
import static java.lang.Double.NaN;
import static java.lang.Math.log;
import static java.lang.Math.pow;
import static jdistlib.math.MathFunctions.gharmonic;
import static jdistlib.math.MathFunctions.isInfinite;
import static jdistlib.math.MathFunctions.lgharmonic;
import static jdistlib.math.MathFunctions.logspace_add;
import jdistlib.exception.PrecisionException;
import jdistlib.generic.GenericDistribution;
import jdistlib.rng.RandomEngine;
import jdistlib.util.Debug;
/**
* Zipf distribution
* Parts taken from VGAM
*
*/
public class Zipf extends GenericDistribution {
public static final double density(int x, int N, double s, boolean give_log) {
if (isInfinite(s)) return s;
if (N <= 0 || s <= 0) return NaN;
if (x <= 0 || x > N) return give_log ? Double.NEGATIVE_INFINITY : 0;
return give_log ? -s * log(x) - lgharmonic(N, s) : pow(x, -s) / gharmonic(N, s);
}
public static final double cumulative(int x, int N, double s, boolean lower_tail, boolean log_p) {
if (isInfinite(s)) return s;
if (N <= 0 || s <= 0) return NaN;
if (x <= 0) return log_p ? Double.NEGATIVE_INFINITY : 0;
if (x >= N) return log_p ? 0 : 1;
if (lower_tail)
return log_p ? lgharmonic(x, s) - lgharmonic(N, s) : gharmonic(x, s) / gharmonic(N, s);
double sum = 0;
if (log_p) {
for (int i = x+1; i <= N; i++)
sum = logspace_add(sum, -s * log(i));
return sum - lgharmonic(N, s);
}
for (int i = x+1; i <= N; i++)
sum += pow(i, -s);
return sum / gharmonic(N, s);
}
public static final double quantile(double p, int N, double s, boolean lower_tail, boolean log_p) {
if (isInfinite(s)) return s;
if (N <= 0 || s <= 0) return Double.NaN;
if (log_p) {
if (p > 0) return NaN;
if (p == 0) return lower_tail ? N : 0;
if (p == Double.NEGATIVE_INFINITY) return lower_tail ? 0 : N;
} else {
if (p < 0 || p > 1) return NaN;
if (p == 0) return lower_tail ? 0 : N;
if (p == 1) return lower_tail ? N : 0;
}
int lo = 0, hi = N, mid;
double f_lo = cumulative(lo, N, s, lower_tail, log_p), f_hi = cumulative(hi, N, s, lower_tail, log_p), f_mid;
boolean pathological = false;
do {
mid = (lo + hi);
f_mid = cumulative(mid, N, s, lower_tail, log_p);
// When the case is pathological, prefer to shrink the
// upper bound when lower_tail == true (shrink the lower bound otherwise)
if (f_mid == p && (f_hi == p || f_lo == p) && (hi - lo > 2))
pathological = true;
if (lower_tail) {
if (f_lo >= p) return lo;
if (f_mid > p) {
hi = mid;
f_hi = f_mid;
} else {
lo = mid;
f_lo = f_mid;
}
} else {
if (f_hi <= p) return hi;
if (f_mid < p) {
lo = mid;
f_lo = f_mid;
} else {
hi = mid;
f_hi = f_mid;
}
}
} while (hi - lo > 1);
if (pathological) {
System.err.println("Pathological case of Zipf.quantile! Quantile estimate may not be accurate!");
if (Debug.warningAsError) {
double ans = (lower_tail) ? (f_hi <= p ? hi : f_mid <= p ? mid : lo) : (f_lo >= p ? lo : f_mid >= p ? mid : hi);
throw new PrecisionException("Pathological case of Zipf.quantile! Quantile estimate may not be accurate!", ans);
}
}
if (lower_tail)
return f_hi <= p ? hi : f_mid <= p ? mid : lo;
return f_lo >= p ? lo : f_mid >= p ? mid : hi;
}
public static final double random(int N, double s, RandomEngine random) {
if (N <= 0 || s <= 0) return Double.NaN;
double u1 = random.nextDouble();
u1 = (int) (134217728 * u1) + random.nextDouble();
u1 = quantile(u1 / 134217728, N, s, true, false);
return u1;
}
public static final double[] random(int n, int N, double s, RandomEngine random) {
double[] rand = new double[n];
for (int i = 0; i < n; i++)
rand[i] = random(N, s, random);
return rand;
}
protected int N;
protected double s;
public Zipf(int N, double s) {
this.N = N; this.s = s;
}
@Override
public double density(double x, boolean log) {
return density((int) x, N, s, log);
}
@Override
public double cumulative(double p, boolean lower_tail, boolean log_p) {
return cumulative((int) p, N, s, lower_tail, log_p);
}
@Override
public double quantile(double q, boolean lower_tail, boolean log_p) {
return quantile(q, N, s, lower_tail, log_p);
}
@Override
public double random() {
return random(N, s, random);
}
}
Ads help maintain this website.