binomial_distribution.h 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #ifndef _LIBCPP___RANDOM_BINOMIAL_DISTRIBUTION_H
  9. #define _LIBCPP___RANDOM_BINOMIAL_DISTRIBUTION_H
  10. #include <__config>
  11. #include <__random/is_valid.h>
  12. #include <__random/uniform_real_distribution.h>
  13. #include <cmath>
  14. #include <iosfwd>
  15. #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
  16. # pragma GCC system_header
  17. #endif
  18. _LIBCPP_PUSH_MACROS
  19. #include <__undef_macros>
  20. _LIBCPP_BEGIN_NAMESPACE_STD
  21. template<class _IntType = int>
  22. class _LIBCPP_TEMPLATE_VIS binomial_distribution
  23. {
  24. static_assert(__libcpp_random_is_valid_inttype<_IntType>::value, "IntType must be an integer type larger than char");
  25. public:
  26. // types
  27. typedef _IntType result_type;
  28. class _LIBCPP_TEMPLATE_VIS param_type
  29. {
  30. result_type __t_;
  31. double __p_;
  32. double __pr_;
  33. double __odds_ratio_;
  34. result_type __r0_;
  35. public:
  36. typedef binomial_distribution distribution_type;
  37. explicit param_type(result_type __t = 1, double __p = 0.5);
  38. _LIBCPP_INLINE_VISIBILITY
  39. result_type t() const {return __t_;}
  40. _LIBCPP_INLINE_VISIBILITY
  41. double p() const {return __p_;}
  42. friend _LIBCPP_INLINE_VISIBILITY
  43. bool operator==(const param_type& __x, const param_type& __y)
  44. {return __x.__t_ == __y.__t_ && __x.__p_ == __y.__p_;}
  45. friend _LIBCPP_INLINE_VISIBILITY
  46. bool operator!=(const param_type& __x, const param_type& __y)
  47. {return !(__x == __y);}
  48. friend class binomial_distribution;
  49. };
  50. private:
  51. param_type __p_;
  52. public:
  53. // constructors and reset functions
  54. #ifndef _LIBCPP_CXX03_LANG
  55. _LIBCPP_INLINE_VISIBILITY
  56. binomial_distribution() : binomial_distribution(1) {}
  57. _LIBCPP_INLINE_VISIBILITY
  58. explicit binomial_distribution(result_type __t, double __p = 0.5)
  59. : __p_(param_type(__t, __p)) {}
  60. #else
  61. _LIBCPP_INLINE_VISIBILITY
  62. explicit binomial_distribution(result_type __t = 1, double __p = 0.5)
  63. : __p_(param_type(__t, __p)) {}
  64. #endif
  65. _LIBCPP_INLINE_VISIBILITY
  66. explicit binomial_distribution(const param_type& __p) : __p_(__p) {}
  67. _LIBCPP_INLINE_VISIBILITY
  68. void reset() {}
  69. // generating functions
  70. template<class _URNG>
  71. _LIBCPP_INLINE_VISIBILITY
  72. result_type operator()(_URNG& __g)
  73. {return (*this)(__g, __p_);}
  74. template<class _URNG> result_type operator()(_URNG& __g, const param_type& __p);
  75. // property functions
  76. _LIBCPP_INLINE_VISIBILITY
  77. result_type t() const {return __p_.t();}
  78. _LIBCPP_INLINE_VISIBILITY
  79. double p() const {return __p_.p();}
  80. _LIBCPP_INLINE_VISIBILITY
  81. param_type param() const {return __p_;}
  82. _LIBCPP_INLINE_VISIBILITY
  83. void param(const param_type& __p) {__p_ = __p;}
  84. _LIBCPP_INLINE_VISIBILITY
  85. result_type min() const {return 0;}
  86. _LIBCPP_INLINE_VISIBILITY
  87. result_type max() const {return t();}
  88. friend _LIBCPP_INLINE_VISIBILITY
  89. bool operator==(const binomial_distribution& __x,
  90. const binomial_distribution& __y)
  91. {return __x.__p_ == __y.__p_;}
  92. friend _LIBCPP_INLINE_VISIBILITY
  93. bool operator!=(const binomial_distribution& __x,
  94. const binomial_distribution& __y)
  95. {return !(__x == __y);}
  96. };
  97. #ifndef _LIBCPP_MSVCRT_LIKE
  98. extern "C" double lgamma_r(double, int *);
  99. #endif
  100. inline _LIBCPP_INLINE_VISIBILITY double __libcpp_lgamma(double __d) {
  101. #if defined(_LIBCPP_MSVCRT_LIKE)
  102. return lgamma(__d);
  103. #else
  104. int __sign;
  105. return lgamma_r(__d, &__sign);
  106. #endif
  107. }
  108. template<class _IntType>
  109. binomial_distribution<_IntType>::param_type::param_type(result_type __t, double __p)
  110. : __t_(__t), __p_(__p)
  111. {
  112. if (0 < __p_ && __p_ < 1)
  113. {
  114. __r0_ = static_cast<result_type>((__t_ + 1) * __p_);
  115. __pr_ = _VSTD::exp(__libcpp_lgamma(__t_ + 1.) -
  116. __libcpp_lgamma(__r0_ + 1.) -
  117. __libcpp_lgamma(__t_ - __r0_ + 1.) + __r0_ * _VSTD::log(__p_) +
  118. (__t_ - __r0_) * _VSTD::log(1 - __p_));
  119. __odds_ratio_ = __p_ / (1 - __p_);
  120. }
  121. }
  122. // Reference: Kemp, C.D. (1986). `A modal method for generating binomial
  123. // variables', Commun. Statist. - Theor. Meth. 15(3), 805-813.
  124. template<class _IntType>
  125. template<class _URNG>
  126. _IntType
  127. binomial_distribution<_IntType>::operator()(_URNG& __g, const param_type& __pr)
  128. {
  129. static_assert(__libcpp_random_is_valid_urng<_URNG>::value, "");
  130. if (__pr.__t_ == 0 || __pr.__p_ == 0)
  131. return 0;
  132. if (__pr.__p_ == 1)
  133. return __pr.__t_;
  134. uniform_real_distribution<double> __gen;
  135. double __u = __gen(__g) - __pr.__pr_;
  136. if (__u < 0)
  137. return __pr.__r0_;
  138. double __pu = __pr.__pr_;
  139. double __pd = __pu;
  140. result_type __ru = __pr.__r0_;
  141. result_type __rd = __ru;
  142. while (true)
  143. {
  144. bool __break = true;
  145. if (__rd >= 1)
  146. {
  147. __pd *= __rd / (__pr.__odds_ratio_ * (__pr.__t_ - __rd + 1));
  148. __u -= __pd;
  149. __break = false;
  150. if (__u < 0)
  151. return __rd - 1;
  152. }
  153. if ( __rd != 0 )
  154. --__rd;
  155. ++__ru;
  156. if (__ru <= __pr.__t_)
  157. {
  158. __pu *= (__pr.__t_ - __ru + 1) * __pr.__odds_ratio_ / __ru;
  159. __u -= __pu;
  160. __break = false;
  161. if (__u < 0)
  162. return __ru;
  163. }
  164. if (__break)
  165. return 0;
  166. }
  167. }
  168. template <class _CharT, class _Traits, class _IntType>
  169. basic_ostream<_CharT, _Traits>&
  170. operator<<(basic_ostream<_CharT, _Traits>& __os,
  171. const binomial_distribution<_IntType>& __x)
  172. {
  173. __save_flags<_CharT, _Traits> __lx(__os);
  174. typedef basic_ostream<_CharT, _Traits> _OStream;
  175. __os.flags(_OStream::dec | _OStream::left | _OStream::fixed |
  176. _OStream::scientific);
  177. _CharT __sp = __os.widen(' ');
  178. __os.fill(__sp);
  179. return __os << __x.t() << __sp << __x.p();
  180. }
  181. template <class _CharT, class _Traits, class _IntType>
  182. basic_istream<_CharT, _Traits>&
  183. operator>>(basic_istream<_CharT, _Traits>& __is,
  184. binomial_distribution<_IntType>& __x)
  185. {
  186. typedef binomial_distribution<_IntType> _Eng;
  187. typedef typename _Eng::result_type result_type;
  188. typedef typename _Eng::param_type param_type;
  189. __save_flags<_CharT, _Traits> __lx(__is);
  190. typedef basic_istream<_CharT, _Traits> _Istream;
  191. __is.flags(_Istream::dec | _Istream::skipws);
  192. result_type __t;
  193. double __p;
  194. __is >> __t >> __p;
  195. if (!__is.fail())
  196. __x.param(param_type(__t, __p));
  197. return __is;
  198. }
  199. _LIBCPP_END_NAMESPACE_STD
  200. _LIBCPP_POP_MACROS
  201. #endif // _LIBCPP___RANDOM_BINOMIAL_DISTRIBUTION_H