From 4bfaac682491a09c3b14d4a9475167c74d09cc80 Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Wed, 14 Aug 2013 11:39:46 +0200 Subject: [PATCH] BUG: Fix Hypergeometric CDF semantics, clarify distribution parameters --- .../HypergeometricDistribution.cs | 8 +- .../Distributions/Discrete/Hypergeometric.cs | 242 ++++++++++-------- .../Discrete/HypergeometricTests.cs | 170 ++++++------ 3 files changed, 226 insertions(+), 194 deletions(-) diff --git a/src/Examples/DiscreteDistributions/HypergeometricDistribution.cs b/src/Examples/DiscreteDistributions/HypergeometricDistribution.cs index f68e1018..b5b14bd6 100644 --- a/src/Examples/DiscreteDistributions/HypergeometricDistribution.cs +++ b/src/Examples/DiscreteDistributions/HypergeometricDistribution.cs @@ -65,7 +65,7 @@ namespace Examples.DiscreteDistributionsExamples { // 1. Initialize the new instance of the Hypergeometric distribution class with parameters PopulationSize = 10, M = 2, N = 8 var hypergeometric = new Hypergeometric(30, 15, 10); - Console.WriteLine(@"1. Initialize the new instance of the Hypergeometric distribution class with parameters PopulationSize = {0}, M = {1}, N = {2}", hypergeometric.PopulationSize, hypergeometric.M, hypergeometric.N); + Console.WriteLine(@"1. Initialize the new instance of the Hypergeometric distribution class with parameters Population = {0}, Success = {1}, Draws = {2}", hypergeometric.Population, hypergeometric.Success, hypergeometric.Draws); Console.WriteLine(); // 2. Distributuion properties: @@ -125,9 +125,9 @@ namespace Examples.DiscreteDistributionsExamples // 5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram Console.WriteLine(@"5. Generate 100000 samples of the Hypergeometric(52, 13, 5) distribution and display histogram"); - hypergeometric.PopulationSize = 52; - hypergeometric.M = 13; - hypergeometric.N = 5; + hypergeometric.Population = 52; + hypergeometric.Success = 13; + hypergeometric.Draws = 5; for (var i = 0; i < data.Length; i++) { data[i] = hypergeometric.Sample(); diff --git a/src/Numerics/Distributions/Discrete/Hypergeometric.cs b/src/Numerics/Distributions/Discrete/Hypergeometric.cs index a6396a49..c54cdbab 100644 --- a/src/Numerics/Distributions/Discrete/Hypergeometric.cs +++ b/src/Numerics/Distributions/Discrete/Hypergeometric.cs @@ -4,7 +4,7 @@ // http://github.com/mathnet/mathnet-numerics // http://mathnetnumerics.codeplex.com // -// Copyright (c) 2009-2010 Math.NET +// Copyright (c) 2009-2013 Math.NET // // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation @@ -28,11 +28,12 @@ // OTHER DEALINGS IN THE SOFTWARE. // +using MathNet.Numerics.Properties; +using System.Collections.Generic; + namespace MathNet.Numerics.Distributions { using System; - using System.Collections.Generic; - using Properties; /// /// This class implements functionality for the Hypergeometric distribution. This distribution is @@ -50,19 +51,19 @@ namespace MathNet.Numerics.Distributions public class Hypergeometric : IDiscreteDistribution { /// - /// The size of the population. + /// The size of the population (N). /// - int _populationSize; + int _population; /// - /// The m parameter of the distribution. + /// The number successes within the population (K, M). /// - int _m; + int _success; /// - /// The n parameter (number to draw) of the distribution. + /// The number of draws without replacement (n). /// - int _n; + int _draws; /// /// The distribution's random number generator. @@ -72,61 +73,61 @@ namespace MathNet.Numerics.Distributions /// /// Initializes a new instance of the Hypergeometric class. /// - /// The population size. - /// The m parameter of the distribution. - /// The n parameter of the distribution. - public Hypergeometric(int populationSize, int m, int n) + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). + public Hypergeometric(int population, int success, int draws) { _random = new Random(); - SetParameters(populationSize, m, n); + SetParameters(population, success, draws); } /// /// Initializes a new instance of the Hypergeometric class. /// - /// The population size. - /// The m parameter of the distribution. - /// The n parameter of the distribution. + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). /// The random number generator which is used to draw random samples. - public Hypergeometric(int populationSize, int m, int n, Random randomSource) + public Hypergeometric(int population, int success, int draws, Random randomSource) { _random = randomSource ?? new Random(); - SetParameters(populationSize, m, n); + SetParameters(population, success, draws); } /// /// Sets the parameters of the distribution after checking their validity. /// - /// The Total parameter of the distribution. - /// The m parameter of the distribution. - /// The n parameter of the distribution. - void SetParameters(int total, int m, int n) + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). + void SetParameters(int population, int success, int draws) { - if (Control.CheckDistributionParameters && !IsValidParameterSet(total, m, n)) + if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws)) { throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); } - _populationSize = total; - _m = m; - _n = n; + _population = population; + _success = success; + _draws = draws; } /// /// Checks whether the parameters of the distribution are valid. /// - /// The Total parameter of the distribution. - /// The m parameter of the distribution. - /// The n parameter of the distribution. + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). /// true when the parameters are valid, false otherwise. - static bool IsValidParameterSet(int total, int m, int n) + static bool IsValidParameterSet(int population, int success, int draws) { - if (total < 0 || m < 0 || n < 0) + if (population < 0 || success < 0 || draws < 0) { return false; } - if (m > total || n > total) + if (success > population || draws > population) { return false; } @@ -135,30 +136,60 @@ namespace MathNet.Numerics.Distributions } /// - /// Gets or sets the population size. + /// Gets or sets the size of the population (N). + /// + public int Population + { + get { return _population; } + set { SetParameters(value, _success, _draws); } + } + + /// + /// Gets or sets the number of draws without replacement (n). + /// + public int Draws + { + get { return _draws; } + set { SetParameters(_population, value, _draws); } + } + + /// + /// Gets or sets the number successes within the population (K, M). + /// + public int Success + { + get { return _success; } + set { SetParameters(_population, _success, value); } + } + + /// + /// Gets or sets the size of the population (N). /// + [Obsolete("Use Population instead. Scheduled for removal in v3.0.")] public int PopulationSize { - get { return _populationSize; } - set { SetParameters(value, _m, _n); } + get { return _population; } + set { SetParameters(value, _success, _draws); } } /// - /// Gets or sets the n parameter of the distribution. + /// Gets or sets the number of draws without replacement (n). /// + [Obsolete("Use Draws instead. Scheduled for removal in v3.0.")] public int N { - get { return _n; } - set { SetParameters(_populationSize, value, _n); } + get { return _draws; } + set { SetParameters(_population, value, _draws); } } /// - /// Gets or sets the m parameter of the distribution. + /// Gets or sets the number successes within the population (K, M). /// + [Obsolete("Use Success instead. Scheduled for removal in v3.0.")] public int M { - get { return _m; } - set { SetParameters(_populationSize, _m, value); } + get { return _success; } + set { SetParameters(_population, _success, value); } } /// @@ -169,11 +200,9 @@ namespace MathNet.Numerics.Distributions /// public override string ToString() { - return "Hypergeometric(N = " + _populationSize + ", m = " + _m + ", n = " + _n + ")"; + return "Hypergeometric(N = " + _population + ", M = " + _success + ", n = " + _draws + ")"; } - #region IDistribution Members - /// /// Gets or sets the random number generator which is used to draw random samples. /// @@ -196,7 +225,7 @@ namespace MathNet.Numerics.Distributions /// public double Mean { - get { return (double) _m*_n/_populationSize; } + get { return (double) _success*_draws/_population; } } /// @@ -204,7 +233,7 @@ namespace MathNet.Numerics.Distributions /// public double Variance { - get { return _n*_m*(_populationSize - _n)*(_populationSize - _m)/(_populationSize*_populationSize*(_populationSize - 1.0)); } + get { return _draws*_success*(_population - _draws)*(_population - _success)/(_population*_population*(_population - 1.0)); } } /// @@ -228,48 +257,15 @@ namespace MathNet.Numerics.Distributions /// public double Skewness { - get { return (Math.Sqrt(_populationSize - 1.0)*(_populationSize - (2*_n))*(_populationSize - (2*_m)))/(Math.Sqrt(_n*_m*(_populationSize - _m)*(_populationSize - _n))*(_populationSize - 2.0)); } - } - - /// - /// Computes the cumulative distribution function of the distribution. - /// - /// The location at which to compute the cumulative density. - /// the cumulative density at . - public double CumulativeDistribution(double x) - { - int alpha = Minimum; - int beta = Maximum; - if (x <= alpha) - { - return 0.0; - } - - if (x > beta) - { - return 1.0; - } - - var sum = 0.0; - var k = (int) Math.Ceiling(x - alpha) - 1; - for (var i = alpha; i <= alpha + k; i++) - { - sum += SpecialFunctions.Binomial(_m, i)*SpecialFunctions.Binomial(_populationSize - _m, _n - i); - } - - return sum/SpecialFunctions.Binomial(_populationSize, _n); + get { return (Math.Sqrt(_population - 1.0)*(_population - (2*_draws))*(_population - (2*_success)))/(Math.Sqrt(_draws*_success*(_population - _success)*(_population - _draws))*(_population - 2.0)); } } - #endregion - - #region IDiscreteDistribution Members - /// /// Gets the mode of the distribution. /// public int Mode { - get { return (_n + 1)*(_m + 1)/(_populationSize + 2); } + get { return (_draws + 1)*(_success + 1)/(_population + 2); } } /// @@ -285,7 +281,7 @@ namespace MathNet.Numerics.Distributions /// public int Minimum { - get { return Math.Max(0, _n + _m - _populationSize); } + get { return Math.Max(0, _draws + _success - _population); } } /// @@ -293,11 +289,11 @@ namespace MathNet.Numerics.Distributions /// public int Maximum { - get { return Math.Min(_m, _n); } + get { return Math.Min(_success, _draws); } } /// - /// Computes values of the probability mass function. + /// Computes values of the probability mass function (PMF), i.e. P(X = x). /// /// The location in the domain where we want to evaluate the probability mass function. /// @@ -305,11 +301,11 @@ namespace MathNet.Numerics.Distributions /// public double Probability(int k) { - return SpecialFunctions.Binomial(_m, k)*SpecialFunctions.Binomial(_populationSize - _m, _n - k)/SpecialFunctions.Binomial(_populationSize, _n); + return SpecialFunctions.Binomial(_success, k)*SpecialFunctions.Binomial(_population - _success, _draws - k)/SpecialFunctions.Binomial(_population, _draws); } /// - /// Computes values of the log probability mass function. + /// Computes values of the log probability mass function (lnPMF), i.e. ln(P(X = x)). /// /// The location in the domain where we want to evaluate the log probability mass function. /// @@ -320,33 +316,57 @@ namespace MathNet.Numerics.Distributions return Math.Log(Probability(k)); } - #endregion + /// + /// Computes the cumulative distribution function (CDF) of the distribution, i.e. P(X <= x). + /// + /// The location at which to compute the cumulative density. + /// the cumulative density at . + public double CumulativeDistribution(double x) + { + if (x < Minimum) + { + return 0.0; + } + if (x >= Maximum) + { + return 1.0; + } + + var k = (int) Math.Floor(x); + var denominatorLn = SpecialFunctions.BinomialLn(_population, _draws); + var sum = 0.0; + for (var i = 0; i <= k; i++) + { + sum += Math.Exp(SpecialFunctions.BinomialLn(_success, i) + SpecialFunctions.BinomialLn(_population - _success, _draws - i) - denominatorLn); + } + return sum; + } /// /// Generates a sample from the Hypergeometric distribution without doing parameter checking. /// /// The random number generator to use. - /// The Total parameter of the distribution. - /// The m parameter of the distribution. - /// The n parameter of the distribution. + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The n parameter of the distribution. /// a random number from the Hypergeometric distribution. - internal static int SampleUnchecked(Random rnd, int size, int m, int n) + internal static int SampleUnchecked(Random rnd, int population, int success, int draws) { var x = 0; do { - var p = (double) m/size; + var p = (double) success/population; var r = rnd.NextDouble(); if (r < p) { x++; - m--; + success--; } - size--; - n--; - } while (0 < n); + population--; + draws--; + } while (0 < draws); return x; } @@ -357,7 +377,7 @@ namespace MathNet.Numerics.Distributions /// The number of successes in n trials. public int Sample() { - return SampleUnchecked(RandomSource, _populationSize, _m, _n); + return SampleUnchecked(RandomSource, _population, _success, _draws); } /// @@ -368,7 +388,7 @@ namespace MathNet.Numerics.Distributions { while (true) { - yield return SampleUnchecked(RandomSource, _populationSize, _m, _n); + yield return SampleUnchecked(RandomSource, _population, _success, _draws); } } @@ -376,36 +396,36 @@ namespace MathNet.Numerics.Distributions /// Samples a random variable. /// /// The random number generator to use. - /// The population size. - /// The m parameter of the distribution. - /// The n parameter of the distribution. - public static int Sample(Random rnd, int populationSize, int m, int n) + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). + public static int Sample(Random rnd, int population, int success, int draws) { - if (Control.CheckDistributionParameters && !IsValidParameterSet(populationSize, m, n)) + if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws)) { throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); } - return SampleUnchecked(rnd, populationSize, m, n); + return SampleUnchecked(rnd, population, success, draws); } /// /// Samples a sequence of this random variable. /// /// The random number generator to use. - /// The population size. - /// The m parameter of the distribution. - /// The n parameter of the distribution. - public static IEnumerable Samples(Random rnd, int populationSize, int m, int n) + /// The size of the population (N). + /// The number successes within the population (K, M). + /// The number of draws without replacement (n). + public static IEnumerable Samples(Random rnd, int population, int success, int draws) { - if (Control.CheckDistributionParameters && !IsValidParameterSet(populationSize, m, n)) + if (Control.CheckDistributionParameters && !IsValidParameterSet(population, success, draws)) { throw new ArgumentOutOfRangeException(Resources.InvalidDistributionParameters); } while (true) { - yield return SampleUnchecked(rnd, populationSize, m, n); + yield return SampleUnchecked(rnd, population, success, draws); } } } diff --git a/src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs b/src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs index 966f610f..28a4dbcf 100644 --- a/src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs +++ b/src/UnitTests/DistributionTests/Discrete/HypergeometricTests.cs @@ -49,36 +49,36 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete /// /// Can create Hypergeometric. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. [TestCase(0, 0, 0)] [TestCase(1, 1, 1)] [TestCase(2, 1, 1)] [TestCase(2, 2, 2)] [TestCase(10, 1, 1)] [TestCase(10, 5, 3)] - public void CanCreateHypergeometric(int size, int m, int n) + public void CanCreateHypergeometric(int population, int success, int draws) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual(size, d.PopulationSize); - Assert.AreEqual(m, d.M); - Assert.AreEqual(n, d.N); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual(population, d.Population); + Assert.AreEqual(success, d.Success); + Assert.AreEqual(draws, d.Draws); } /// /// Hypergeometric create fails with bad parameters. /// - /// Population size. - /// M parameter. + /// Population size. + /// M parameter. /// N parameter. [TestCase(2, 3, 2)] [TestCase(10, 5, 20)] [TestCase(-2, 1, 1)] [TestCase(0, 1, 1)] - public void HypergeometricCreateFailsWithBadParameters(int size, int m, int n) + public void HypergeometricCreateFailsWithBadParameters(int population, int success, int n) { - Assert.Throws(() => new Hypergeometric(size, m, n)); + Assert.Throws(() => new Hypergeometric(population, success, n)); } /// @@ -88,90 +88,90 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete public void ValidateToString() { var d = new Hypergeometric(10, 1, 1); - Assert.AreEqual("Hypergeometric(N = 10, m = 1, n = 1)", d.ToString()); + Assert.AreEqual("Hypergeometric(N = 10, M = 1, n = 1)", d.ToString()); } /// /// Can set size. /// - /// Population size. + /// Population size. [TestCase(5)] [TestCase(10)] [TestCase(20)] - public void CanSetSize(int size) + public void CanSetPopulation(int population) { new Hypergeometric(10, 1, 1) - { - PopulationSize = size - }; + { + Population = population + }; } /// /// Set size fails with bad values. /// - /// Population size. + /// Population size. [TestCase(-1)] [TestCase(0)] - public void SetSizeFails(int size) + public void SetPopulationFails(int population) { var d = new Hypergeometric(10, 1, 1); - Assert.Throws(() => d.PopulationSize = size); + Assert.Throws(() => d.Population = population); } /// /// Can set M. /// - /// M parameter. + /// M parameter. [TestCase(0)] [TestCase(1)] [TestCase(2)] [TestCase(5)] - public void CanSetm(int m) + public void CanSetSuccess(int success) { new Hypergeometric(10, 1, 1) - { - M = m - }; + { + Success = success + }; } /// /// Set M fails with bad values. /// - /// M parameter. + /// M parameter. [TestCase(11)] [TestCase(-1)] - public void SetmFails(int m) + public void SetSuccessFails(int success) { var d = new Hypergeometric(10, 1, 1); - Assert.Throws(() => d.M = m); + Assert.Throws(() => d.Success = success); } /// /// Can set N. /// - /// N parameter. + /// N parameter. [TestCase(0)] [TestCase(1)] [TestCase(2)] [TestCase(5)] - public void CanSetn(int n) + public void CanSetDraws(int draws) { new Hypergeometric(10, 1, 1) - { - N = n - }; + { + Draws = draws + }; } /// /// Set N fails with bad values. /// - /// N parameter. + /// N parameter. [TestCase(11)] [TestCase(-1)] - public void SetnFails(int n) + public void SetDrawsFails(int draws) { var d = new Hypergeometric(10, 1, 1); - Assert.Throws(() => d.N = n); + Assert.Throws(() => d.Draws = draws); } /// @@ -187,37 +187,37 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete /// /// Validate skewness. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. [TestCase(0, 0, 0)] [TestCase(1, 1, 1)] [TestCase(2, 1, 1)] [TestCase(2, 2, 2)] [TestCase(10, 1, 1)] [TestCase(10, 5, 3)] - public void ValidateSkewness(int size, int m, int n) + public void ValidateSkewness(int population, int success, int draws) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual((Math.Sqrt(size - 1.0) * (size - (2 * n)) * (size - (2 * m))) / (Math.Sqrt(n * m * (size - m) * (size - n)) * (size - 2.0)), d.Skewness); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual((Math.Sqrt(population - 1.0)*(population - (2*draws))*(population - (2*success)))/(Math.Sqrt(draws*success*(population - success)*(population - draws))*(population - 2.0)), d.Skewness); } /// /// Validate mode. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. [TestCase(0, 0, 0)] [TestCase(1, 1, 1)] [TestCase(2, 1, 1)] [TestCase(2, 2, 2)] [TestCase(10, 1, 1)] [TestCase(10, 5, 3)] - public void ValidateMode(int size, int m, int n) + public void ValidateMode(int population, int success, int draws) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual((n + 1) * (m + 1) / (size + 2), d.Mode); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual((draws + 1)*(success + 1)/(population + 2), d.Mode); } /// @@ -233,45 +233,45 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete /// /// Validate minimum. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. [TestCase(0, 0, 0)] [TestCase(1, 1, 1)] [TestCase(2, 1, 1)] [TestCase(2, 2, 2)] [TestCase(10, 1, 1)] [TestCase(10, 5, 3)] - public void ValidateMinimum(int size, int m, int n) + public void ValidateMinimum(int population, int success, int draws) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual(Math.Max(0, n + m - size), d.Minimum); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual(Math.Max(0, draws + success - population), d.Minimum); } /// /// Validate maximum. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. [TestCase(0, 0, 0)] [TestCase(1, 1, 1)] [TestCase(2, 1, 1)] [TestCase(2, 2, 2)] [TestCase(10, 1, 1)] [TestCase(10, 5, 3)] - public void ValidateMaximum(int size, int m, int n) + public void ValidateMaximum(int population, int success, int draws) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual(Math.Min(m, n), d.Maximum); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual(Math.Min(success, draws), d.Maximum); } /// /// Validate probability. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. /// Input X value. [TestCase(0, 0, 0, 0)] [TestCase(1, 1, 1, 1)] @@ -282,18 +282,18 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete [TestCase(10, 1, 1, 1)] [TestCase(10, 5, 3, 1)] [TestCase(10, 5, 3, 3)] - public void ValidateProbability(int size, int m, int n, int x) + public void ValidateProbability(int population, int success, int draws, int x) { - var d = new Hypergeometric(size, m, n); - Assert.AreEqual(SpecialFunctions.Binomial(m, x) * SpecialFunctions.Binomial(size - m, n - x) / SpecialFunctions.Binomial(size, n), d.Probability(x)); + var d = new Hypergeometric(population, success, draws); + Assert.AreEqual(SpecialFunctions.Binomial(success, x)*SpecialFunctions.Binomial(population - success, draws - x)/SpecialFunctions.Binomial(population, draws), d.Probability(x)); } /// /// Validate probability log. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. /// Input X value. [TestCase(0, 0, 0, 0)] [TestCase(1, 1, 1, 1)] @@ -304,9 +304,9 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete [TestCase(10, 1, 1, 1)] [TestCase(10, 5, 3, 1)] [TestCase(10, 5, 3, 3)] - public void ValidateProbabilityLn(int size, int m, int n, int x) + public void ValidateProbabilityLn(int population, int success, int draws, int x) { - var d = new Hypergeometric(size, m, n); + var d = new Hypergeometric(population, success, draws); Assert.AreEqual(Math.Log(d.Probability(x)), d.ProbabilityLn(x)); } @@ -334,9 +334,9 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete /// /// Validate cumulative distribution. /// - /// Population size. - /// M parameter. - /// N parameter. + /// Population size. + /// M parameter. + /// N parameter. /// Input X value. /// Expected value. [TestCase(0, 0, 0, 0.5, 1.0)] @@ -347,11 +347,23 @@ namespace MathNet.Numerics.UnitTests.DistributionTests.Discrete [TestCase(10, 1, 1, 0.3, 0.9)] [TestCase(10, 1, 1, 1.2, 1.0)] [TestCase(10, 5, 3, 1.1, 0.5)] - [TestCase(10, 5, 3, 3.0, 0.916666666666667)] - public void ValidateCumulativeDistribution(int size, int m, int n, double x, double cdf) + [TestCase(10, 5, 3, 2.0, 11.0/12.0)] + [TestCase(10, 5, 3, 3.0, 1.0)] + [TestCase(10000, 2, 9800, 0.0, 199.0/499950.0)] + [TestCase(10000, 2, 9800, 0.5, 199.0/499950.0)] + [TestCase(10000, 2, 9800, 1.5, 19799.0/499950.0)] + public void ValidateCumulativeDistribution(int population, int success, int draws, double x, double cdf) + { + var d = new Hypergeometric(population, success, draws); + AssertHelpers.AlmostEqual(cdf, d.CumulativeDistribution(x), 10); + } + + [Test] + public void CumulativeDistributionMustNotOverflow_CodePlexIssue5729() { - var d = new Hypergeometric(size, m, n); - AssertHelpers.AlmostEqual(cdf, d.CumulativeDistribution(x), 14); + var d = new Hypergeometric(10000, 2, 9800); + Assert.That(d.CumulativeDistribution(0.0), Is.Not.NaN); + Assert.That(d.CumulativeDistribution(0.1), Is.Not.NaN); } } }