@@ -28,7 +28,9 @@ use core::fmt;
28
28
#[ derive( Copy , Clone , Debug ) ]
29
29
pub struct Geometric
30
30
{
31
- p : f64
31
+ p : f64 ,
32
+ pi : f64 ,
33
+ k : u64
32
34
}
33
35
34
36
/// Error type returned from `Geometric::new`.
@@ -55,8 +57,21 @@ impl Geometric {
55
57
pub fn new ( p : f64 ) -> Result < Self , Error > {
56
58
if !p. is_finite ( ) || p < 0.0 || p > 1.0 {
57
59
Err ( Error :: InvalidProbability )
60
+ } else if p == 0.0 || p >= 2.0 / 3.0 {
61
+ Ok ( Geometric { p, pi : p, k : 0 } )
58
62
} else {
59
- Ok ( Geometric { p } )
63
+ let ( pi, k) = {
64
+ // choose smallest k such that pi = (1 - p)^(2^k) <= 0.5
65
+ let mut k = 1 ;
66
+ let mut pi = ( 1.0 - p) . powi ( 2 ) ;
67
+ while pi > 0.5 {
68
+ k += 1 ;
69
+ pi = pi * pi;
70
+ }
71
+ ( pi, k)
72
+ } ;
73
+
74
+ Ok ( Geometric { p, pi, k } )
60
75
}
61
76
}
62
77
}
@@ -77,21 +92,14 @@ impl Distribution<u64> for Geometric
77
92
78
93
if self . p == 0.0 { return core:: u64:: MAX ; }
79
94
95
+ let Geometric { p, pi, k } = * self ;
96
+
80
97
// Based on the algorithm presented in section 3 of
81
98
// Karl Bringmann and Tobias Friedrich (July 2013) - Exact and Efficient
82
99
// Generation of Geometric Random Variates and Random Graphs, published
83
100
// in International Colloquium on Automata, Languages and Programming
84
101
// (pp.267-278)
85
- let ( pi, k) = {
86
- // choose smallest k such that pi = (1 - p)^(2^k) <= 0.5
87
- let mut k = 1 ;
88
- let mut pi = ( 1.0 - self . p ) . powi ( 2 ) ;
89
- while pi > 0.5 {
90
- k += 1 ;
91
- pi = pi * pi;
92
- }
93
- ( pi, k)
94
- } ;
102
+ // https://people.mpi-inf.mpg.de/~kbringma/paper/2013ICALP-1.pdf
95
103
96
104
// Use the trivial algorithm to sample D from Geo(pi) = Geo(p) / 2^k:
97
105
let d = {
@@ -104,12 +112,15 @@ impl Distribution<u64> for Geometric
104
112
105
113
// Use rejection sampling for the remainder M from Geo(p) % 2^k:
106
114
// choose M uniformly from [0, 2^k), but reject with probability (1 - p)^M
115
+ // NOTE: The paper suggests using bitwise sampling here, which is
116
+ // currently unsupported, but should improve performance by requiring
117
+ // fewer iterations on average. ~ October 28, 2020
107
118
let m = loop {
108
119
let m = rng. gen :: < u64 > ( ) & ( ( 1 << k) - 1 ) ;
109
120
let p_reject = if m <= core:: i32:: MAX as u64 {
110
- ( 1.0 - self . p ) . powi ( m as i32 )
121
+ ( 1.0 - p) . powi ( m as i32 )
111
122
} else {
112
- ( 1.0 - self . p ) . powf ( m as f64 )
123
+ ( 1.0 - p) . powf ( m as f64 )
113
124
} ;
114
125
115
126
let u = rng. gen :: < f64 > ( ) ;
0 commit comments