discrete_distribution.cc 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // Copyright 2017 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "absl/random/discrete_distribution.h"
  15. #include <cassert>
  16. #include <cmath>
  17. #include <cstddef>
  18. #include <iterator>
  19. #include <numeric>
  20. #include <utility>
  21. #include <vector>
  22. #include "absl/base/config.h"
  23. namespace absl {
  24. ABSL_NAMESPACE_BEGIN
  25. namespace random_internal {
  26. // Initializes the distribution table for Walker's Aliasing algorithm, described
  27. // in Knuth, Vol 2. as well as in https://en.wikipedia.org/wiki/Alias_method
  28. std::vector<std::pair<double, size_t>> InitDiscreteDistribution(
  29. std::vector<double>* probabilities) {
  30. // The empty-case should already be handled by the constructor.
  31. assert(probabilities);
  32. assert(!probabilities->empty());
  33. // Step 1. Normalize the input probabilities to 1.0.
  34. double sum = std::accumulate(std::begin(*probabilities),
  35. std::end(*probabilities), 0.0);
  36. if (std::fabs(sum - 1.0) > 1e-6) {
  37. // Scale `probabilities` only when the sum is too far from 1.0. Scaling
  38. // unconditionally will alter the probabilities slightly.
  39. for (double& item : *probabilities) {
  40. item = item / sum;
  41. }
  42. }
  43. // Step 2. At this point `probabilities` is set to the conditional
  44. // probabilities of each element which sum to 1.0, to within reasonable error.
  45. // These values are used to construct the proportional probability tables for
  46. // the selection phases of Walker's Aliasing algorithm.
  47. //
  48. // To construct the table, pick an element which is under-full (i.e., an
  49. // element for which `(*probabilities)[i] < 1.0/n`), and pair it with an
  50. // element which is over-full (i.e., an element for which
  51. // `(*probabilities)[i] > 1.0/n`). The smaller value can always be retired.
  52. // The larger may still be greater than 1.0/n, or may now be less than 1.0/n,
  53. // and put back onto the appropriate collection.
  54. const size_t n = probabilities->size();
  55. std::vector<std::pair<double, size_t>> q;
  56. q.reserve(n);
  57. std::vector<size_t> over;
  58. std::vector<size_t> under;
  59. size_t idx = 0;
  60. for (const double item : *probabilities) {
  61. assert(item >= 0);
  62. const double v = item * n;
  63. q.emplace_back(v, 0);
  64. if (v < 1.0) {
  65. under.push_back(idx++);
  66. } else {
  67. over.push_back(idx++);
  68. }
  69. }
  70. while (!over.empty() && !under.empty()) {
  71. auto lo = under.back();
  72. under.pop_back();
  73. auto hi = over.back();
  74. over.pop_back();
  75. q[lo].second = hi;
  76. const double r = q[hi].first - (1.0 - q[lo].first);
  77. q[hi].first = r;
  78. if (r < 1.0) {
  79. under.push_back(hi);
  80. } else {
  81. over.push_back(hi);
  82. }
  83. }
  84. // Due to rounding errors, there may be un-paired elements in either
  85. // collection; these should all be values near 1.0. For these values, set `q`
  86. // to 1.0 and set the alternate to the identity.
  87. for (auto i : over) {
  88. q[i] = {1.0, i};
  89. }
  90. for (auto i : under) {
  91. q[i] = {1.0, i};
  92. }
  93. return q;
  94. }
  95. } // namespace random_internal
  96. ABSL_NAMESPACE_END
  97. } // namespace absl