hypergeometric.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. // Copyright 2008 Gautam Sewani
  2. // Copyright 2008 John Maddock
  3. // Copyright 2021 Paul A. Bristow
  4. //
  5. // Use, modification and distribution are subject to the
  6. // Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt
  8. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. #ifndef BOOST_MATH_DISTRIBUTIONS_HYPERGEOMETRIC_HPP
  10. #define BOOST_MATH_DISTRIBUTIONS_HYPERGEOMETRIC_HPP
  11. #include <boost/math/distributions/detail/common_error_handling.hpp>
  12. #include <boost/math/distributions/complement.hpp>
  13. #include <boost/math/distributions/detail/hypergeometric_pdf.hpp>
  14. #include <boost/math/distributions/detail/hypergeometric_cdf.hpp>
  15. #include <boost/math/distributions/detail/hypergeometric_quantile.hpp>
  16. #include <boost/math/special_functions/fpclassify.hpp>
  17. namespace boost { namespace math {
  18. template <class RealType = double, class Policy = policies::policy<> >
  19. class hypergeometric_distribution
  20. {
  21. public:
  22. typedef RealType value_type;
  23. typedef Policy policy_type;
  24. hypergeometric_distribution(unsigned r, unsigned n, unsigned N) // Constructor. r=defective/failures/success, n=trials/draws, N=total population.
  25. : m_n(n), m_N(N), m_r(r)
  26. {
  27. static const char* function = "boost::math::hypergeometric_distribution<%1%>::hypergeometric_distribution";
  28. RealType ret;
  29. check_params(function, &ret);
  30. }
  31. // Accessor functions.
  32. unsigned total()const
  33. {
  34. return m_N;
  35. }
  36. unsigned defective()const // successes/failures/events
  37. {
  38. return m_r;
  39. }
  40. unsigned sample_count()const
  41. {
  42. return m_n;
  43. }
  44. bool check_params(const char* function, RealType* result)const
  45. {
  46. if(m_r > m_N)
  47. {
  48. *result = boost::math::policies::raise_domain_error<RealType>(
  49. function, "Parameter r out of range: must be <= N but got %1%", static_cast<RealType>(m_r), Policy());
  50. return false;
  51. }
  52. if(m_n > m_N)
  53. {
  54. *result = boost::math::policies::raise_domain_error<RealType>(
  55. function, "Parameter n out of range: must be <= N but got %1%", static_cast<RealType>(m_n), Policy());
  56. return false;
  57. }
  58. return true;
  59. }
  60. bool check_x(unsigned x, const char* function, RealType* result)const
  61. {
  62. if(x < static_cast<unsigned>((std::max)(0, static_cast<int>(m_n + m_r) - static_cast<int>(m_N))))
  63. {
  64. *result = boost::math::policies::raise_domain_error<RealType>(
  65. function, "Random variable out of range: must be > 0 and > m + r - N but got %1%", static_cast<RealType>(x), Policy());
  66. return false;
  67. }
  68. if(x > (std::min)(m_r, m_n))
  69. {
  70. *result = boost::math::policies::raise_domain_error<RealType>(
  71. function, "Random variable out of range: must be less than both n and r but got %1%", static_cast<RealType>(x), Policy());
  72. return false;
  73. }
  74. return true;
  75. }
  76. private:
  77. // Data members:
  78. unsigned m_n; // number of items picked or drawn.
  79. unsigned m_N; // number of "total" items.
  80. unsigned m_r; // number of "defective/successes/failures/events items.
  81. }; // class hypergeometric_distribution
  82. typedef hypergeometric_distribution<double> hypergeometric;
  83. template <class RealType, class Policy>
  84. inline const std::pair<unsigned, unsigned> range(const hypergeometric_distribution<RealType, Policy>& dist)
  85. { // Range of permissible values for random variable x.
  86. #ifdef _MSC_VER
  87. # pragma warning(push)
  88. # pragma warning(disable:4267)
  89. #endif
  90. unsigned r = dist.defective();
  91. unsigned n = dist.sample_count();
  92. unsigned N = dist.total();
  93. unsigned l = static_cast<unsigned>((std::max)(0, static_cast<int>(n + r) - static_cast<int>(N)));
  94. unsigned u = (std::min)(r, n);
  95. return std::pair<unsigned, unsigned>(l, u);
  96. #ifdef _MSC_VER
  97. # pragma warning(pop)
  98. #endif
  99. }
  100. template <class RealType, class Policy>
  101. inline const std::pair<unsigned, unsigned> support(const hypergeometric_distribution<RealType, Policy>& d)
  102. {
  103. return range(d);
  104. }
  105. template <class RealType, class Policy>
  106. inline RealType pdf(const hypergeometric_distribution<RealType, Policy>& dist, const unsigned& x)
  107. {
  108. static const char* function = "boost::math::pdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  109. RealType result = 0;
  110. if(!dist.check_params(function, &result))
  111. return result;
  112. if(!dist.check_x(x, function, &result))
  113. return result;
  114. return boost::math::detail::hypergeometric_pdf<RealType>(
  115. x, dist.defective(), dist.sample_count(), dist.total(), Policy());
  116. }
  117. template <class RealType, class Policy, class U>
  118. inline RealType pdf(const hypergeometric_distribution<RealType, Policy>& dist, const U& x)
  119. {
  120. BOOST_MATH_STD_USING
  121. static const char* function = "boost::math::pdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  122. RealType r = static_cast<RealType>(x);
  123. unsigned u = itrunc(r, typename policies::normalise<Policy, policies::rounding_error<policies::ignore_error> >::type());
  124. if(u != r)
  125. {
  126. return boost::math::policies::raise_domain_error<RealType>(
  127. function, "Random variable out of range: must be an integer but got %1%", r, Policy());
  128. }
  129. return pdf(dist, u);
  130. }
  131. template <class RealType, class Policy>
  132. inline RealType cdf(const hypergeometric_distribution<RealType, Policy>& dist, const unsigned& x)
  133. {
  134. static const char* function = "boost::math::cdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  135. RealType result = 0;
  136. if(!dist.check_params(function, &result))
  137. return result;
  138. if(!dist.check_x(x, function, &result))
  139. return result;
  140. return boost::math::detail::hypergeometric_cdf<RealType>(
  141. x, dist.defective(), dist.sample_count(), dist.total(), false, Policy());
  142. }
  143. template <class RealType, class Policy, class U>
  144. inline RealType cdf(const hypergeometric_distribution<RealType, Policy>& dist, const U& x)
  145. {
  146. BOOST_MATH_STD_USING
  147. static const char* function = "boost::math::cdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  148. RealType r = static_cast<RealType>(x);
  149. unsigned u = itrunc(r, typename policies::normalise<Policy, policies::rounding_error<policies::ignore_error> >::type());
  150. if(u != r)
  151. {
  152. return boost::math::policies::raise_domain_error<RealType>(
  153. function, "Random variable out of range: must be an integer but got %1%", r, Policy());
  154. }
  155. return cdf(dist, u);
  156. }
  157. template <class RealType, class Policy>
  158. inline RealType cdf(const complemented2_type<hypergeometric_distribution<RealType, Policy>, unsigned>& c)
  159. {
  160. static const char* function = "boost::math::cdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  161. RealType result = 0;
  162. if(!c.dist.check_params(function, &result))
  163. return result;
  164. if(!c.dist.check_x(c.param, function, &result))
  165. return result;
  166. return boost::math::detail::hypergeometric_cdf<RealType>(
  167. c.param, c.dist.defective(), c.dist.sample_count(), c.dist.total(), true, Policy());
  168. }
  169. template <class RealType, class Policy, class U>
  170. inline RealType cdf(const complemented2_type<hypergeometric_distribution<RealType, Policy>, U>& c)
  171. {
  172. BOOST_MATH_STD_USING
  173. static const char* function = "boost::math::cdf(const hypergeometric_distribution<%1%>&, const %1%&)";
  174. RealType r = static_cast<RealType>(c.param);
  175. unsigned u = itrunc(r, typename policies::normalise<Policy, policies::rounding_error<policies::ignore_error> >::type());
  176. if(u != r)
  177. {
  178. return boost::math::policies::raise_domain_error<RealType>(
  179. function, "Random variable out of range: must be an integer but got %1%", r, Policy());
  180. }
  181. return cdf(complement(c.dist, u));
  182. }
  183. template <class RealType, class Policy>
  184. inline RealType quantile(const hypergeometric_distribution<RealType, Policy>& dist, const RealType& p)
  185. {
  186. BOOST_MATH_STD_USING // for ADL of std functions
  187. // Checking function argument
  188. RealType result = 0;
  189. const char* function = "boost::math::quantile(const hypergeometric_distribution<%1%>&, %1%)";
  190. if (false == dist.check_params(function, &result)) return result;
  191. if(false == detail::check_probability(function, p, &result, Policy())) return result;
  192. return static_cast<RealType>(detail::hypergeometric_quantile(p, RealType(1 - p), dist.defective(), dist.sample_count(), dist.total(), Policy()));
  193. } // quantile
  194. template <class RealType, class Policy>
  195. inline RealType quantile(const complemented2_type<hypergeometric_distribution<RealType, Policy>, RealType>& c)
  196. {
  197. BOOST_MATH_STD_USING // for ADL of std functions
  198. // Checking function argument
  199. RealType result = 0;
  200. const char* function = "quantile(const complemented2_type<hypergeometric_distribution<%1%>, %1%>&)";
  201. if (false == c.dist.check_params(function, &result)) return result;
  202. if(false == detail::check_probability(function, c.param, &result, Policy())) return result;
  203. return static_cast<RealType>(detail::hypergeometric_quantile(RealType(1 - c.param), c.param, c.dist.defective(), c.dist.sample_count(), c.dist.total(), Policy()));
  204. } // quantile
  205. // https://www.wolframalpha.com/input/?i=kurtosis+hypergeometric+distribution
  206. template <class RealType, class Policy>
  207. inline RealType mean(const hypergeometric_distribution<RealType, Policy>& dist)
  208. {
  209. return static_cast<RealType>(dist.defective() * dist.sample_count()) / dist.total();
  210. } // RealType mean(const hypergeometric_distribution<RealType, Policy>& dist)
  211. template <class RealType, class Policy>
  212. inline RealType variance(const hypergeometric_distribution<RealType, Policy>& dist)
  213. {
  214. RealType r = static_cast<RealType>(dist.defective());
  215. RealType n = static_cast<RealType>(dist.sample_count());
  216. RealType N = static_cast<RealType>(dist.total());
  217. return n * r * (N - r) * (N - n) / (N * N * (N - 1));
  218. } // RealType variance(const hypergeometric_distribution<RealType, Policy>& dist)
  219. template <class RealType, class Policy>
  220. inline RealType mode(const hypergeometric_distribution<RealType, Policy>& dist)
  221. {
  222. BOOST_MATH_STD_USING
  223. RealType r = static_cast<RealType>(dist.defective());
  224. RealType n = static_cast<RealType>(dist.sample_count());
  225. RealType N = static_cast<RealType>(dist.total());
  226. return floor((r + 1) * (n + 1) / (N + 2));
  227. }
  228. template <class RealType, class Policy>
  229. inline RealType skewness(const hypergeometric_distribution<RealType, Policy>& dist)
  230. {
  231. BOOST_MATH_STD_USING
  232. RealType r = static_cast<RealType>(dist.defective());
  233. RealType n = static_cast<RealType>(dist.sample_count());
  234. RealType N = static_cast<RealType>(dist.total());
  235. return (N - 2 * r) * sqrt(N - 1) * (N - 2 * n) / (sqrt(n * r * (N - r) * (N - n)) * (N - 2));
  236. } // RealType skewness(const hypergeometric_distribution<RealType, Policy>& dist)
  237. template <class RealType, class Policy>
  238. inline RealType kurtosis_excess(const hypergeometric_distribution<RealType, Policy>& dist)
  239. {
  240. // https://www.wolframalpha.com/input/?i=kurtosis+hypergeometric+distribution shown as plain text:
  241. // mean | (m n)/N
  242. // standard deviation | sqrt((m n(N - m) (N - n))/(N - 1))/N
  243. // variance | (m n(1 - m/N) (N - n))/((N - 1) N)
  244. // skewness | (sqrt(N - 1) (N - 2 m) (N - 2 n))/((N - 2) sqrt(m n(N - m) (N - n)))
  245. // kurtosis | ((N - 1) N^2 ((3 m(N - m) (n^2 (-N) + (n - 2) N^2 + 6 n(N - n)))/N^2 - 6 n(N - n) + N(N + 1)))/(m n(N - 3) (N - 2) (N - m) (N - n))
  246. // Kurtosis[HypergeometricDistribution[n, m, N]]
  247. RealType m = static_cast<RealType>(dist.defective()); // Failures or success events. (Also symbols K or M are used).
  248. RealType n = static_cast<RealType>(dist.sample_count()); // draws or trials.
  249. RealType n2 = n * n; // n^2
  250. RealType N = static_cast<RealType>(dist.total()); // Total population from which n draws or trials are made.
  251. RealType N2 = N * N; // N^2
  252. // result = ((N - 1) N^2 ((3 m(N - m) (n^2 (-N) + (n - 2) N^2 + 6 n(N - n)))/N^2 - 6 n(N - n) + N(N + 1)))/(m n(N - 3) (N - 2) (N - m) (N - n));
  253. RealType result = ((N-1)*N2*((3*m*(N-m)*(n2*(-N)+(n-2)*N2+6*n*(N-n)))/N2-6*n*(N-n)+N*(N+1)))/(m*n*(N-3)*(N-2)*(N-m)*(N-n));
  254. // Agrees with kurtosis hypergeometric distribution(50,200,500) kurtosis = 2.96917
  255. // N[kurtosis[hypergeometricdistribution(50,200,500)], 55] 2.969174035736058474901169623721804275002985337280263464
  256. return result;
  257. } // RealType kurtosis_excess(const hypergeometric_distribution<RealType, Policy>& dist)
  258. template <class RealType, class Policy>
  259. inline RealType kurtosis(const hypergeometric_distribution<RealType, Policy>& dist)
  260. {
  261. return kurtosis_excess(dist) + 3;
  262. } // RealType kurtosis_excess(const hypergeometric_distribution<RealType, Policy>& dist)
  263. }} // namespaces
  264. // This include must be at the end, *after* the accessors
  265. // for this distribution have been defined, in order to
  266. // keep compilers that support two-phase lookup happy.
  267. #include <boost/math/distributions/detail/derived_accessors.hpp>
  268. #endif // include guard