File indexing completed on 2025-01-18 09:27:22
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015 #ifndef ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
0016 #define ABSL_RANDOM_ZIPF_DISTRIBUTION_H_
0017
0018 #include <cassert>
0019 #include <cmath>
0020 #include <istream>
0021 #include <limits>
0022 #include <ostream>
0023 #include <type_traits>
0024
0025 #include "absl/random/internal/iostream_state_saver.h"
0026 #include "absl/random/internal/traits.h"
0027 #include "absl/random/uniform_real_distribution.h"
0028
0029 namespace absl {
0030 ABSL_NAMESPACE_BEGIN
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051 template <typename IntType = int>
0052 class zipf_distribution {
0053 public:
0054 using result_type = IntType;
0055
0056 class param_type {
0057 public:
0058 using distribution_type = zipf_distribution;
0059
0060
0061
0062
0063
0064
0065 explicit param_type(result_type k = (std::numeric_limits<IntType>::max)(),
0066 double q = 2.0, double v = 1.0);
0067
0068 result_type k() const { return k_; }
0069 double q() const { return q_; }
0070 double v() const { return v_; }
0071
0072 friend bool operator==(const param_type& a, const param_type& b) {
0073 return a.k_ == b.k_ && a.q_ == b.q_ && a.v_ == b.v_;
0074 }
0075 friend bool operator!=(const param_type& a, const param_type& b) {
0076 return !(a == b);
0077 }
0078
0079 private:
0080 friend class zipf_distribution;
0081 inline double h(double x) const;
0082 inline double hinv(double x) const;
0083 inline double compute_s() const;
0084 inline double pow_negative_q(double x) const;
0085
0086
0087
0088 IntType k_;
0089 double q_;
0090 double v_;
0091
0092 double one_minus_q_;
0093 double s_;
0094 double one_minus_q_inv_;
0095 double hxm_;
0096 double hx0_minus_hxm_;
0097
0098 static_assert(random_internal::IsIntegral<IntType>::value,
0099 "Class-template absl::zipf_distribution<> must be "
0100 "parameterized using an integral type.");
0101 };
0102
0103 zipf_distribution()
0104 : zipf_distribution((std::numeric_limits<IntType>::max)()) {}
0105
0106 explicit zipf_distribution(result_type k, double q = 2.0, double v = 1.0)
0107 : param_(k, q, v) {}
0108
0109 explicit zipf_distribution(const param_type& p) : param_(p) {}
0110
0111 void reset() {}
0112
0113 template <typename URBG>
0114 result_type operator()(URBG& g) {
0115 return (*this)(g, param_);
0116 }
0117
0118 template <typename URBG>
0119 result_type operator()(URBG& g,
0120 const param_type& p);
0121
0122 result_type k() const { return param_.k(); }
0123 double q() const { return param_.q(); }
0124 double v() const { return param_.v(); }
0125
0126 param_type param() const { return param_; }
0127 void param(const param_type& p) { param_ = p; }
0128
0129 result_type(min)() const { return 0; }
0130 result_type(max)() const { return k(); }
0131
0132 friend bool operator==(const zipf_distribution& a,
0133 const zipf_distribution& b) {
0134 return a.param_ == b.param_;
0135 }
0136 friend bool operator!=(const zipf_distribution& a,
0137 const zipf_distribution& b) {
0138 return a.param_ != b.param_;
0139 }
0140
0141 private:
0142 param_type param_;
0143 };
0144
0145
0146
0147
0148
0149 template <typename IntType>
0150 zipf_distribution<IntType>::param_type::param_type(
0151 typename zipf_distribution<IntType>::result_type k, double q, double v)
0152 : k_(k), q_(q), v_(v), one_minus_q_(1 - q) {
0153 assert(q > 1);
0154 assert(v > 0);
0155 assert(k > 0);
0156 one_minus_q_inv_ = 1 / one_minus_q_;
0157
0158
0159
0160 constexpr double kMax = 18446744073709549568.0;
0161 double kd = static_cast<double>(k);
0162
0163
0164 if (kd > kMax) {
0165
0166
0167 kd = kMax;
0168 }
0169 hxm_ = h(kd + 0.5);
0170
0171
0172 const bool use_precomputed = (v == 1.0 && q == 2.0);
0173 const double h0x5 = use_precomputed ? (-1.0 / 1.5)
0174 : h(0.5);
0175 const double elogv_q = (v_ == 1.0) ? 1 : pow_negative_q(v_);
0176
0177
0178 hx0_minus_hxm_ = (h0x5 - elogv_q) - hxm_;
0179
0180
0181 s_ = use_precomputed ? 0.46153846153846123 : compute_s();
0182 }
0183
0184 template <typename IntType>
0185 double zipf_distribution<IntType>::param_type::h(double x) const {
0186
0187 x += v_;
0188 return (one_minus_q_ == -1.0)
0189 ? (-1.0 / x)
0190 : (std::exp(std::log(x) * one_minus_q_) * one_minus_q_inv_);
0191 }
0192
0193 template <typename IntType>
0194 double zipf_distribution<IntType>::param_type::hinv(double x) const {
0195
0196 return -v_ + ((one_minus_q_ == -1.0)
0197 ? (-1.0 / x)
0198 : std::exp(one_minus_q_inv_ * std::log(one_minus_q_ * x)));
0199 }
0200
0201 template <typename IntType>
0202 double zipf_distribution<IntType>::param_type::compute_s() const {
0203
0204 return 1.0 - hinv(h(1.5) - pow_negative_q(v_ + 1.0));
0205 }
0206
0207 template <typename IntType>
0208 double zipf_distribution<IntType>::param_type::pow_negative_q(double x) const {
0209
0210 return q_ == 2.0 ? (1.0 / (x * x)) : std::exp(std::log(x) * -q_);
0211 }
0212
0213 template <typename IntType>
0214 template <typename URBG>
0215 typename zipf_distribution<IntType>::result_type
0216 zipf_distribution<IntType>::operator()(
0217 URBG& g, const param_type& p) {
0218 absl::uniform_real_distribution<double> uniform_double;
0219 double k;
0220 for (;;) {
0221 const double v = uniform_double(g);
0222 const double u = p.hxm_ + v * p.hx0_minus_hxm_;
0223 const double x = p.hinv(u);
0224 k = rint(x);
0225 if (k > static_cast<double>(p.k())) continue;
0226 if (k - x <= p.s_) break;
0227 const double h = p.h(k + 0.5);
0228 const double r = p.pow_negative_q(p.v_ + k);
0229 if (u >= h - r) break;
0230 }
0231 IntType ki = static_cast<IntType>(k);
0232 assert(ki <= p.k_);
0233 return ki;
0234 }
0235
0236 template <typename CharT, typename Traits, typename IntType>
0237 std::basic_ostream<CharT, Traits>& operator<<(
0238 std::basic_ostream<CharT, Traits>& os,
0239 const zipf_distribution<IntType>& x) {
0240 using stream_type =
0241 typename random_internal::stream_format_type<IntType>::type;
0242 auto saver = random_internal::make_ostream_state_saver(os);
0243 os.precision(random_internal::stream_precision_helper<double>::kPrecision);
0244 os << static_cast<stream_type>(x.k()) << os.fill() << x.q() << os.fill()
0245 << x.v();
0246 return os;
0247 }
0248
0249 template <typename CharT, typename Traits, typename IntType>
0250 std::basic_istream<CharT, Traits>& operator>>(
0251 std::basic_istream<CharT, Traits>& is,
0252 zipf_distribution<IntType>& x) {
0253 using result_type = typename zipf_distribution<IntType>::result_type;
0254 using param_type = typename zipf_distribution<IntType>::param_type;
0255 using stream_type =
0256 typename random_internal::stream_format_type<IntType>::type;
0257 stream_type k;
0258 double q;
0259 double v;
0260
0261 auto saver = random_internal::make_istream_state_saver(is);
0262 is >> k >> q >> v;
0263 if (!is.fail()) {
0264 x.param(param_type(static_cast<result_type>(k), q, v));
0265 }
0266 return is;
0267 }
0268
0269 ABSL_NAMESPACE_END
0270 }
0271
0272 #endif