geometric_examples.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // geometric_examples.cpp
  2. // Copyright Paul A. Bristow 2010.
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0.
  5. // (See accompanying file LICENSE_1_0.txt
  6. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. // This file is written to be included from a Quickbook .qbk document.
  8. // It can still be compiled by the C++ compiler, and run.
  9. // Any output can also be added here as comment or included or pasted in elsewhere.
  10. // Caution: this file contains Quickbook markup as well as code
  11. // and comments: don't change any of the special comment markups!
  12. // Examples of using the geometric distribution.
  13. //[geometric_eg1_1
  14. /*`
  15. For this example, we will opt to #define two macros to control
  16. the error and discrete handling policies.
  17. For this simple example, we want to avoid throwing
  18. an exception (the default policy) and just return infinity.
  19. We want to treat the distribution as if it was continuous,
  20. so we choose a discrete_quantile policy of real,
  21. rather than the default policy integer_round_outwards.
  22. */
  23. #define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
  24. #define BOOST_MATH_DISCRETE_QUANTILE_POLICY real
  25. /*`
  26. [caution It is vital to #include distributions etc *after* the above #defines]
  27. After that we need some includes to provide easy access to the negative binomial distribution,
  28. and we need some std library iostream, of course.
  29. */
  30. #include <boost/math/distributions/geometric.hpp>
  31. // for geometric_distribution
  32. using ::boost::math::geometric_distribution; //
  33. using ::boost::math::geometric; // typedef provides default type is double.
  34. using ::boost::math::pdf; // Probability mass function.
  35. using ::boost::math::cdf; // Cumulative density function.
  36. using ::boost::math::quantile;
  37. #include <boost/math/distributions/negative_binomial.hpp>
  38. // for negative_binomial_distribution
  39. using boost::math::negative_binomial; // typedef provides default type is double.
  40. #include <boost/math/distributions/normal.hpp>
  41. // for negative_binomial_distribution
  42. using boost::math::normal; // typedef provides default type is double.
  43. #include <iostream>
  44. using std::cout; using std::endl;
  45. using std::noshowpoint; using std::fixed; using std::right; using std::left;
  46. #include <iomanip>
  47. using std::setprecision; using std::setw;
  48. #include <limits>
  49. using std::numeric_limits;
  50. //] [geometric_eg1_1]
  51. int main()
  52. {
  53. cout <<"Geometric distribution example" << endl;
  54. cout << endl;
  55. cout.precision(4); // But only show a few for this example.
  56. try
  57. {
  58. //[geometric_eg1_2
  59. /*`
  60. It is always sensible to use try and catch blocks because defaults policies are to
  61. throw an exception if anything goes wrong.
  62. Simple try'n'catch blocks (see below) will ensure that you get a
  63. helpful error message instead of an abrupt (and silent) program abort.
  64. [h6 Throwing a dice]
  65. The Geometric distribution describes the probability (/p/) of a number of failures
  66. to get the first success in /k/ Bernoulli trials.
  67. (A [@http://en.wikipedia.org/wiki/Bernoulli_distribution Bernoulli trial]
  68. is one with only two possible outcomes, success of failure,
  69. and /p/ is the probability of success).
  70. Suppose an 'fair' 6-face dice is thrown repeatedly:
  71. */
  72. double success_fraction = 1./6; // success_fraction (p) = 0.1666
  73. // (so failure_fraction is 1 - success_fraction = 5./6 = 1- 0.1666 = 0.8333)
  74. /*`If the dice is thrown repeatedly until the *first* time a /three/ appears.
  75. The probablility distribution of the number of times it is thrown *not* getting a /three/
  76. (/not-a-threes/ number of failures to get a /three/)
  77. is a geometric distribution with the success_fraction = 1/6 = 0.1666[recur].
  78. We therefore start by constructing a geometric distribution
  79. with the one parameter success_fraction, the probability of success.
  80. */
  81. geometric g6(success_fraction); // type double by default.
  82. /*`
  83. To confirm, we can echo the success_fraction parameter of the distribution.
  84. */
  85. cout << "success fraction of a six-sided dice is " << g6.success_fraction() << endl;
  86. /*`So the probability of getting a three at the first throw (zero failures) is
  87. */
  88. cout << pdf(g6, 0) << endl; // 0.1667
  89. cout << cdf(g6, 0) << endl; // 0.1667
  90. /*`Note that the cdf and pdf are identical because the is only one throw.
  91. If we want the probability of getting the first /three/ on the 2nd throw:
  92. */
  93. cout << pdf(g6, 1) << endl; // 0.1389
  94. /*`If we want the probability of getting the first /three/ on the 1st or 2nd throw
  95. (allowing one failure):
  96. */
  97. cout << "pdf(g6, 0) + pdf(g6, 1) = " << pdf(g6, 0) + pdf(g6, 1) << endl;
  98. /*`Or more conveniently, and more generally,
  99. we can use the Cumulative Distribution Function CDF.*/
  100. cout << "cdf(g6, 1) = " << cdf(g6, 1) << endl; // 0.3056
  101. /*`If we allow many more (12) throws, the probability of getting our /three/ gets very high:*/
  102. cout << "cdf(g6, 12) = " << cdf(g6, 12) << endl; // 0.9065 or 90% probability.
  103. /*`If we want to be much more confident, say 99%,
  104. we can estimate the number of throws to be this sure
  105. using the inverse or quantile.
  106. */
  107. cout << "quantile(g6, 0.99) = " << quantile(g6, 0.99) << endl; // 24.26
  108. /*`Note that the value returned is not an integer:
  109. if you want an integer result you should use either floor, round or ceil functions,
  110. or use the policies mechanism.
  111. See __understand_dis_quant.
  112. The geometric distribution is related to the negative binomial
  113. __spaces `negative_binomial_distribution(RealType r, RealType p);` with parameter /r/ = 1.
  114. So we could get the same result using the negative binomial,
  115. but using the geometric the results will be faster, and may be more accurate.
  116. */
  117. negative_binomial nb(1, success_fraction);
  118. cout << pdf(nb, 1) << endl; // 0.1389
  119. cout << cdf(nb, 1) << endl; // 0.3056
  120. /*`We could also the complement to express the required probability
  121. as 1 - 0.99 = 0.01 (and get the same result):
  122. */
  123. cout << "quantile(complement(g6, 1 - p)) " << quantile(complement(g6, 0.01)) << endl; // 24.26
  124. /*`
  125. Note too that Boost.Math geometric distribution is implemented as a continuous function.
  126. Unlike other implementations (for example R) it *uses* the number of failures as a *real* parameter,
  127. not as an integer. If you want this integer behaviour, you may need to enforce this by
  128. rounding the parameter you pass, probably rounding down, to the nearest integer.
  129. For example, R returns the success fraction probability for all values of failures
  130. from 0 to 0.999999 thus:
  131. [pre
  132. __spaces R> formatC(pgeom(0.0001,0.5, FALSE), digits=17) " 0.5"
  133. ] [/pre]
  134. So in Boost.Math the equivalent is
  135. */
  136. geometric g05(0.5); // Probability of success = 0.5 or 50%
  137. // Output all potentially significant digits for the type, here double.
  138. #ifdef BOOST_NO_CXX11_NUMERIC_LIMITS
  139. int max_digits10 = 2 + (boost::math::policies::digits<double, boost::math::policies::policy<> >() * 30103UL) / 100000UL;
  140. cout << "BOOST_NO_CXX11_NUMERIC_LIMITS is defined" << endl;
  141. #else
  142. int max_digits10 = std::numeric_limits<double>::max_digits10;
  143. #endif
  144. cout << "Show all potentially significant decimal digits std::numeric_limits<double>::max_digits10 = "
  145. << max_digits10 << endl;
  146. cout.precision(max_digits10); //
  147. cout << cdf(g05, 0.0001) << endl; // returns 0.5000346561579232, not exact 0.5.
  148. /*`To get the R discrete behaviour, you simply need to round with,
  149. for example, the `floor` function.
  150. */
  151. cout << cdf(g05, floor(0.0001)) << endl; // returns exactly 0.5
  152. /*`
  153. [pre
  154. `> formatC(pgeom(0.9999999,0.5, FALSE), digits=17) [1] " 0.25"`
  155. `> formatC(pgeom(1.999999,0.5, FALSE), digits=17)[1] " 0.25" k = 1`
  156. `> formatC(pgeom(1.9999999,0.5, FALSE), digits=17)[1] "0.12500000000000003" k = 2`
  157. ] [/pre]
  158. shows that R makes an arbitrary round-up decision at about 1e7 from the next integer above.
  159. This may be convenient in practice, and could be replicated in C++ if desired.
  160. [h6 Surveying customers to find one with a faulty product]
  161. A company knows from warranty claims that 2% of their products will be faulty,
  162. so the 'success_fraction' of finding a fault is 0.02.
  163. It wants to interview a purchaser of faulty products to assess their 'user experience'.
  164. To estimate how many customers they will probably need to contact
  165. in order to find one who has suffered from the fault,
  166. we first construct a geometric distribution with probability 0.02,
  167. and then chose a confidence, say 80%, 95%, or 99% to finding a customer with a fault.
  168. Finally, we probably want to round up the result to the integer above using the `ceil` function.
  169. (We could also use a policy, but that is hardly worthwhile for this simple application.)
  170. (This also assumes that each customer only buys one product:
  171. if customers bought more than one item,
  172. the probability of finding a customer with a fault obviously improves.)
  173. */
  174. cout.precision(5);
  175. geometric g(0.02); // On average, 2 in 100 products are faulty.
  176. double c = 0.95; // 95% confidence.
  177. cout << " quantile(g, " << c << ") = " << quantile(g, c) << endl;
  178. cout << "To be " << c * 100
  179. << "% confident of finding we customer with a fault, need to survey "
  180. << ceil(quantile(g, c)) << " customers." << endl; // 148
  181. c = 0.99; // Very confident.
  182. cout << "To be " << c * 100
  183. << "% confident of finding we customer with a fault, need to survey "
  184. << ceil(quantile(g, c)) << " customers." << endl; // 227
  185. c = 0.80; // Only reasonably confident.
  186. cout << "To be " << c * 100
  187. << "% confident of finding we customer with a fault, need to survey "
  188. << ceil(quantile(g, c)) << " customers." << endl; // 79
  189. /*`[h6 Basket Ball Shooters]
  190. According to Wikipedia, average pro basket ball players get
  191. [@http://en.wikipedia.org/wiki/Free_throw free throws]
  192. in the baskets 70 to 80 % of the time,
  193. but some get as high as 95%, and others as low as 50%.
  194. Suppose we want to compare the probabilities
  195. of failing to get a score only on the first or on the fifth shot?
  196. To start we will consider the average shooter, say 75%.
  197. So we construct a geometric distribution
  198. with success_fraction parameter 75/100 = 0.75.
  199. */
  200. cout.precision(2);
  201. geometric gav(0.75); // Shooter averages 7.5 out of 10 in the basket.
  202. /*`What is probability of getting 1st try in the basket, that is with no failures? */
  203. cout << "Probability of score on 1st try = " << pdf(gav, 0) << endl; // 0.75
  204. /*`This is, of course, the success_fraction probability 75%.
  205. What is the probability that the shooter only scores on the fifth shot?
  206. So there are 5-1 = 4 failures before the first success.*/
  207. cout << "Probability of score on 5th try = " << pdf(gav, 4) << endl; // 0.0029
  208. /*`Now compare this with the poor and the best players success fraction.
  209. We need to constructing new distributions with the different success fractions,
  210. and then get the corresponding probability density functions values:
  211. */
  212. geometric gbest(0.95);
  213. cout << "Probability of score on 5th try = " << pdf(gbest, 4) << endl; // 5.9e-6
  214. geometric gmediocre(0.50);
  215. cout << "Probability of score on 5th try = " << pdf(gmediocre, 4) << endl; // 0.031
  216. /*`So we can see the very much smaller chance (0.000006) of 4 failures by the best shooters,
  217. compared to the 0.03 of the mediocre.*/
  218. /*`[h6 Estimating failures]
  219. Of course one man's failure is an other man's success.
  220. So a fault can be defined as a 'success'.
  221. If a fault occurs once after 100 flights, then one might naively say
  222. that the risk of fault is obviously 1 in 100 = 1/100, a probability of 0.01.
  223. This is the best estimate we can make, but while it is the truth,
  224. it is not the whole truth,
  225. for it hides the big uncertainty when estimating from a single event.
  226. "One swallow doesn't make a summer."
  227. To show the magnitude of the uncertainty, the geometric
  228. (or the negative binomial) distribution can be used.
  229. If we chose the popular 95% confidence in the limits, corresponding to an alpha of 0.05,
  230. because we are calculating a two-sided interval, we must divide alpha by two.
  231. */
  232. double alpha = 0.05;
  233. double k = 100; // So frequency of occurrence is 1/100.
  234. cout << "Probability is failure is " << 1/k << endl;
  235. double t = geometric::find_lower_bound_on_p(k, alpha/2);
  236. cout << "geometric::find_lower_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  237. << t << endl; // 0.00025
  238. t = geometric::find_upper_bound_on_p(k, alpha/2);
  239. cout << "geometric::find_upper_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  240. << t << endl; // 0.037
  241. /*`So while we estimate the probability is 0.01, it might lie between 0.0003 and 0.04.
  242. Even if we relax our confidence to alpha = 90%, the bounds only contract to 0.0005 and 0.03.
  243. And if we require a high confidence, they widen to 0.00005 to 0.05.
  244. */
  245. alpha = 0.1; // 90% confidence.
  246. t = geometric::find_lower_bound_on_p(k, alpha/2);
  247. cout << "geometric::find_lower_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  248. << t << endl; // 0.0005
  249. t = geometric::find_upper_bound_on_p(k, alpha/2);
  250. cout << "geometric::find_upper_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  251. << t << endl; // 0.03
  252. alpha = 0.01; // 99% confidence.
  253. t = geometric::find_lower_bound_on_p(k, alpha/2);
  254. cout << "geometric::find_lower_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  255. << t << endl; // 5e-005
  256. t = geometric::find_upper_bound_on_p(k, alpha/2);
  257. cout << "geometric::find_upper_bound_on_p(" << int(k) << ", " << alpha/2 << ") = "
  258. << t << endl; // 0.052
  259. /*`In real life, there will usually be more than one event (fault or success),
  260. when the negative binomial, which has the neccessary extra parameter, will be needed.
  261. */
  262. /*`As noted above, using a catch block is always a good idea,
  263. even if you hope not to use it!
  264. */
  265. }
  266. catch(const std::exception& e)
  267. { // Since we have set an overflow policy of ignore_error,
  268. // an overflow exception should never be thrown.
  269. std::cout << "\nMessage from thrown exception was:\n " << e.what() << std::endl;
  270. /*`
  271. For example, without a ignore domain error policy,
  272. if we asked for ``pdf(g, -1)`` for example,
  273. we would get an unhelpful abort, but with a catch:
  274. [pre
  275. Message from thrown exception was:
  276. Error in function boost::math::pdf(const exponential_distribution<double>&, double):
  277. Number of failures argument is -1, but must be >= 0 !
  278. ] [/pre]
  279. */
  280. //] [/ geometric_eg1_2]
  281. }
  282. return 0;
  283. } // int main()
  284. /*
  285. Output is:
  286. Geometric distribution example
  287. success fraction of a six-sided dice is 0.1667
  288. 0.1667
  289. 0.1667
  290. 0.1389
  291. pdf(g6, 0) + pdf(g6, 1) = 0.3056
  292. cdf(g6, 1) = 0.3056
  293. cdf(g6, 12) = 0.9065
  294. quantile(g6, 0.99) = 24.26
  295. 0.1389
  296. 0.3056
  297. quantile(complement(g6, 1 - p)) 24.26
  298. 0.5000346561579232
  299. 0.5
  300. quantile(g, 0.95) = 147.28
  301. To be 95% confident of finding we customer with a fault, need to survey 148 customers.
  302. To be 99% confident of finding we customer with a fault, need to survey 227 customers.
  303. To be 80% confident of finding we customer with a fault, need to survey 79 customers.
  304. Probability of score on 1st try = 0.75
  305. Probability of score on 5th try = 0.0029
  306. Probability of score on 5th try = 5.9e-006
  307. Probability of score on 5th try = 0.031
  308. Probability is failure is 0.01
  309. geometric::find_lower_bound_on_p(100, 0.025) = 0.00025
  310. geometric::find_upper_bound_on_p(100, 0.025) = 0.037
  311. geometric::find_lower_bound_on_p(100, 0.05) = 0.00051
  312. geometric::find_upper_bound_on_p(100, 0.05) = 0.03
  313. geometric::find_lower_bound_on_p(100, 0.005) = 5e-005
  314. geometric::find_upper_bound_on_p(100, 0.005) = 0.052
  315. */