| // Copyright 2009 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| // W.Hormann, G.Derflinger: | |
| // "Rejection-Inversion to Generate Variates | |
| // from Monotone Discrete Distributions" | |
| // http://eeyore.wu-wien.ac.at/papers/96-04-04.wh-der.ps.gz | |
| package rand | |
| import "math" | |
| // A Zipf generates Zipf distributed variates. | |
| type Zipf struct { | |
| r *Rand | |
| imax float64 | |
| v float64 | |
| q float64 | |
| s float64 | |
| oneminusQ float64 | |
| oneminusQinv float64 | |
| hxm float64 | |
| hx0minusHxm float64 | |
| } | |
| func (z *Zipf) h(x float64) float64 { | |
| return math.Exp(z.oneminusQ*math.Log(z.v+x)) * z.oneminusQinv | |
| } | |
| func (z *Zipf) hinv(x float64) float64 { | |
| return math.Exp(z.oneminusQinv*math.Log(z.oneminusQ*x)) - z.v | |
| } | |
| // NewZipf returns a [Zipf] variate generator. | |
| // The generator generates values k ∈ [0, imax] | |
| // such that P(k) is proportional to (v + k) ** (-s). | |
| // Requirements: s > 1 and v >= 1. | |
| func NewZipf(r *Rand, s float64, v float64, imax uint64) *Zipf { | |
| z := new(Zipf) | |
| if s <= 1.0 || v < 1 { | |
| return nil | |
| } | |
| z.r = r | |
| z.imax = float64(imax) | |
| z.v = v | |
| z.q = s | |
| z.oneminusQ = 1.0 - z.q | |
| z.oneminusQinv = 1.0 / z.oneminusQ | |
| z.hxm = z.h(z.imax + 0.5) | |
| z.hx0minusHxm = z.h(0.5) - math.Exp(math.Log(z.v)*(-z.q)) - z.hxm | |
| z.s = 1 - z.hinv(z.h(1.5)-math.Exp(-z.q*math.Log(z.v+1.0))) | |
| return z | |
| } | |
| // Uint64 returns a value drawn from the [Zipf] distribution described | |
| // by the [Zipf] object. | |
| func (z *Zipf) Uint64() uint64 { | |
| if z == nil { | |
| panic("rand: nil Zipf") | |
| } | |
| k := 0.0 | |
| for { | |
| r := z.r.Float64() // r on [0,1] | |
| ur := z.hxm + r*z.hx0minusHxm | |
| x := z.hinv(ur) | |
| k = math.Floor(x + 0.5) | |
| if k-x <= z.s { | |
| break | |
| } | |
| if ur >= z.h(k+0.5)-math.Exp(-math.Log(k+z.v)*z.q) { | |
| break | |
| } | |
| } | |
| return uint64(k) | |
| } | |