hypergeometric.hpp 14 KB

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