using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BotSharp.Algorithm.HiddenMarkovModel.MathUtils.Distribution { public class FDistribution { public const double EPSILON = .0000000001; /// /// Return the probability density function of a F-distribution /// /// reference value for the variable x following the F-distribution /// degrees of freedom of numerator /// degrees of freedom of the denominator /// /// The probability densitiy function public static double GetPDF(double F, double DF1, double DF2, double deltaF = 0.0001) { double F1 = F - deltaF / 2; double F2 = F + deltaF / 2; if (F1 <= EPSILON) { F1 = F; deltaF = deltaF / 2; } double p1 = GetPercentile(F1, DF1, DF2); double p2 = GetPercentile(F2, DF1, DF2); double areaP = p2 - p1; return areaP / deltaF; } /// /// Return the critical value F for p = P(x <= F), where p is the percentile /// /// The implementation here is adapted from http://www.cs.umb.edu/~rickb/files/disc_proj/disc/weka/weka-3-2-3/weka/core/Statistics.java /// /// percentile P(x <= F) /// degrees of freedom of numerator /// degrees of freedom of denominoator /// The critical value F for p = P(x <= F) public static double GetQuantile(double p, double DF1, double DF2) { double fval; double maxf = 99999.0; // maximum possible F ratio double minf = .000001; // minimum possible F ratio if (p <= 0.0 || p >= 1.0) return (0.0); fval = 1.0 / p; // initial value for guess fval, the smaller the p, the larger the F while (System.Math.Abs(maxf - minf) > .000001) { if (GetPercentile(fval, DF1, DF2) > p) // F too large maxf = fval; else // F too small minf = fval; fval = (maxf + minf) * 0.5; } return (fval); } /// /// Return P(x <= F), where x follows the F-distribution /// /// This is the CDF /// The implementation is based on conversion from javascript at http://davidmlane.com/hyperstat/F_table.html /// /// reference value for the variable x following the F-distribution /// degrees of freedom of numerator /// degrees of freedom of denominator /// Cumulative probability that can also be represented by the probability P(x <= F) that x is less than F. public static double GetPercentile(double F, double DF1, double DF2) { if (DF1 > .01 & DF2 > .01 & F > EPSILON) { var p = 1 - ProbF(DF1, DF2, F); return p; } else { throw new Exception("DF1, DF2, and F must be numbers greater than 0."); } } /// /// Return the probabilty P(x > F) where x follows the F-distribution /// /// /// /// /// private static double ProbF(double dn, double dd, double fr) { var f = fr; var a = dn; var b = dd; var iv = 0; if (System.Math.Floor(a / 2) * 2 == a) { //even numerator df double fp = L401(a, f, b, iv); return fp; } else if (System.Math.Floor(b / 2) * 2 != b) { double fp = L504(a, f, b, iv); return fp; } f = 1 / f; a = dd; b = dn; iv = 1; return L401(a, f, b, iv); } private static double L504(double a, double f, double b, double iv) { var q = a * f / (a * f + b); var sa = System.Math.Sqrt(q); var sl = System.Math.Log(sa); var ca = System.Math.Sqrt(1 - q); var cl = System.Math.Log(ca); var al = System.Math.Atan(sa / System.Math.Sqrt(-sa * sa + 1)); var fp = 1 - 2 * al / System.Math.PI; var r = 0.0; if (b != 1) { double c = System.Math.Log(2 * sa / System.Math.PI); fp -= System.Math.Exp(c + cl); if (b != 3) { var n = System.Math.Floor((b - 3) / 2); for (int i = 1; i <= n; i++) { var x = 2 * i + 1; r += System.Math.Log((x - 1) / x); var rr = r + cl * x + c; if (rr > -78.4) { fp -= System.Math.Exp(rr); } } } } if (a != 1) { var c = r; if (b > 1) { c += System.Math.Log(b - 1); } c += System.Math.Log(2 / System.Math.PI) + sl + cl * b; if (c > -78.4) { fp += System.Math.Exp(c); } if (a != 3) { var n = System.Math.Floor((a - 3) / 2); r = 0; for (int i = 1; i <= n; i++) { double x = i * 2 + 1; r += System.Math.Log((b + x - 2) / x); double rr = r + sl * (x - 1) + c; if (rr > -78.4) { fp += System.Math.Exp(rr); } } } } return fp; } private static double L401(double a, double f, double b, double iv) { var q = a * f / (a * f + b); var ql = System.Math.Log(q); var fp = 0.0; var c = System.Math.Log(1 - q) * b / 2; if (c > -78.4) { fp = System.Math.Exp(c); } if (a != 2) { var n = System.Math.Floor(a / 2 - 1); var r = 0.0; for (int i = 1; i <= n; i++) { var x = 2 * i; r += System.Math.Log(b + x - 2) - System.Math.Log(x) + ql; if (r + c > -78.4) { fp += System.Math.Exp(r + c); } } } if (iv == 1) { fp = 1 - fp; } return fp; } } }