[tor-commits] [stegotorus/master] Add geometric distribution generator to rng.
zwol at torproject.org
zwol at torproject.org
Fri Jul 20 23:17:06 UTC 2012
commit b6de45bcd5c535a16c8bf18ae8f5e7e1ea27e5aa
Author: Zack Weinberg <zackw at cmu.edu>
Date: Tue Jan 10 01:09:59 2012 +0000
Add geometric distribution generator to rng.
git-svn-id: svn+ssh://spartan.csl.sri.com/svn/private/DEFIANCE@212 a58ff0ac-194c-e011-a152-003048836090
---
src/rng.cc | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/rng.h | 9 +++++++++
2 files changed, 68 insertions(+), 0 deletions(-)
diff --git a/src/rng.cc b/src/rng.cc
index 9bde6cc..95bea3a 100644
--- a/src/rng.cc
+++ b/src/rng.cc
@@ -5,6 +5,8 @@
#include "util.h"
#include "rng.h"
+#include <limits>
+#include <math.h>
#include <cryptopp/osrng.h>
/* Note: this file wraps a C++ library into a C-style program and must
@@ -86,3 +88,60 @@ rng_range(unsigned int min, unsigned int max)
}
CATCH_ALL_EXCEPTIONS(-1);
}
+
+/** Internal use only (can be externalized if someone has a good use
+ * for it): generate a random double-precision floating-point number
+ * in the range [0.0, 1.0). Implementation tactic from "Common Lisp
+ * the Language, 2nd Edition", section 12.9. Assumes IEEE754.
+ */
+static double
+rng_double()
+{
+ union ieee754_double {
+ double d;
+ uint64_t i;
+ };
+
+ union ieee754_double n;
+
+ /* This may waste up to 12 bits of randomness on each call,
+ depending on how clever GenerateWord32 is internally; but the
+ implementation is much simpler than if we used GenerateBlock. */
+ try {
+ rng_init();
+ n.i = (0x3FF0000000000000ULL |
+ (uint64_t(rng->GenerateWord32(0, 0x000FFFFFu)) << 32) |
+ uint64_t(rng->GenerateWord32()));
+ } CATCH_ALL_EXCEPTIONS(std::numeric_limits<double>::quiet_NaN());
+
+ return n.d - 1.0;
+}
+
+/** Return a random integer in the range [0, hi), geometrically
+ * distributed over that range, with expected value 'xv'.
+ * (The rate parameter 'lambda' that's usually used to characterize
+ * the geometric/exponential distribution is equal to 1/xv.)
+ * 'hi' must be no more than INT_MAX+1, as for 'rng_range'.
+ * 'xv' must be greater than 0 and less than 'hi'.
+ */
+int
+rng_range_geom(unsigned int hi, unsigned int xv)
+{
+ log_assert(hi <= ((unsigned int)INT_MAX)+1);
+ log_assert(0 < xv && xv < hi);
+
+ double U = rng_double();
+ if (isnan(U))
+ return -1;
+
+ /* Inverse transform sampling:
+ T = (-ln U)/lambda; lambda=1/(xv-lo); therefore T = (xv-lo) * -ln(U).
+ Minor wrinkle: rng_double() produces [0, 1) but we want (0, 1] to
+ avoid hitting the undefined log(0). This is what nextafter() is for. */
+
+ double T = -log(nextafter(U, 2.0)) * xv;
+
+ /* Technically we should rejection-sample here instead of clamping, but
+ that would make this not a constant-time operation. */
+ return std::min(hi-1, std::max(0U, (unsigned int)floor(T)));
+}
diff --git a/src/rng.h b/src/rng.h
index 43955bc..02b0947 100644
--- a/src/rng.h
+++ b/src/rng.h
@@ -19,4 +19,13 @@ int rng_int(unsigned int max);
*/
int rng_range(unsigned int min, unsigned int max);
+/** Return a random integer in the range [0, hi), geometrically
+ * distributed over that range, with expected value 'xv'.
+ * (The rate parameter 'lambda' that's usually used to characterize
+ * the geometric/exponential distribution is equal to 1/xv.)
+ * 'hi' must be no more than INT_MAX+1, as for 'rng_range'.
+ * 'xv' must be greater than 0 and less than 'hi'.
+ */
+int rng_range_geom(unsigned int hi, unsigned int xv);
+
#endif
More information about the tor-commits
mailing list