Skip to content
Back to the collection

Fast Random Integer Generation in an Interval

Daniel Lemire

2018 · arXiv

Read the paper
HN score
0
Mentions
4
Readers
4
Months
3
What’s in a score?

From the abstract

In simulations, probabilistic algorithms and statistical tests, we often generate random integers in an interval (e.g., [0,s)). For example, random integers in an interval are essential to the Fisher-Yates random shuffle. Consequently, popular languages like Java, Python, C++, Swift and Go include ranged random integer generation functions as part of their runtime libraries. Pseudo-random values are usually generated in words of a fixed number of bits (e.g., 32 bits, 64 bits) using algorithms such as a linear congruential generator. We need functions to convert such random words to random integers in an interval ([0,s)) without introducing statistical biases. The standard functions in programming languages such as Java involve integer divisions. Unfortunately, division instructions are relatively expensive. We review an unbiased function to generate ranged integers from a source of random words that avoids integer divisions with high probability. To establish the practical usefulness of the approach, we show that this algorithm can multiply the speed of unbiased random shuffling on x64 processors. Our proposed approach has been adopted by the Go language for its implementation of the shuffle function.

Metadata: arXiv. Abstracts retain their original authors’ rights.

In the conversation

Original HN comments, with labels from simple text rules. A label describes the whole comment and may miss context or irony. Read the discussion to make up your own mind. About the labels

4 selected excerpts · 4 total mentions

NWDD
Neutral mention · rule
For many use cases biased is good enough. The topic of unbiased bounded generation is pretty interesting itself and both lemire and M.E. O'Neill (author of pcg) wrote blog posts and an academic paper comparing different approaches (some using as building block the fixed point trick) https://www.pcg-random.org/posts/bounded-rands.html https://lemire.me/blog/2019/06/06/nearly-divisionless-random... https://arxiv.org/pdf/1805.10941.pdf
Reelin
Neutral mention · rule
I'm a bit confused. Looking at the original paper (https://arxiv.org/pdf/1805.10941.pdf) this doesn't seem to be a RNG itself but rather a method to efficiently transform the output of a RNG onto an arbitrary interval while maintaining a uniform distribution. (This is at odds with the current title.) See in particular "Algorithm 5" which specifies retrieving a random integer on lines 1 and 7. In section 5, the authors specify that they use a linear congruential generator for their experiments. If my understanding is correct, this is an important distinction because it means you can plug different RNGs into this algorithm depending on your needs (in particular the…
Read the full comment
capabilities of the underlying platform). Related question: For the ultimate in minimal state, would a CBPRNG such as Philox or Threefry (http://www.thesalmons.org/john/random123/papers/random123sc1...) be safe to use here or would the fact that it can be invoked multiple times for a single call (and thus state sequences might end up overlapping between calls) be likely to introduce subtle statistical issues? Slightly off topic - from Lemire's paper: > may not be applicable to specialized processors such as Graphics Processing Units (GPUs) that lack support for the computation of the full multiplication This statement seems to be at odds with the fact that implementations of Philox are provided for both AMD and Nvidia GPUs but Philox relies on efficient mulhi and mullo being available. I didn't bother to look into it further yet though.
MauranKilom
Neutral mention · rule
After reading the intro of the article, I wanted to see whether I could get the gist of the algorithm just from looking at the first code of it that I come upon. I thus took the first such link from the article (https://arxiv.org/pdf/1805.10941.pdf) while trying to avoid any spoilers. There was no way to unsee the comment at the top of the article, but I don't think it helped much. I did skim the paper to find which of the listed algorithms is the one in question (Algorithm 5), which inevitable primed me a bit (primarily by having the context of the two other rejection sampling algorithms). I also looked at the pseudo-code instead of the real code, so I don't think this…
Read the full comment
is a fair comparison with whatever colleagues of the author had to face, but I had no chance to make it rigorous anyway. The algorithm in question, copied straight from the paper (and ASCII-fied): ALGORITHM 5: An efficient algorithm to generate unbiased random integers in an interval. Require: source of uniformly-distributed random integers in [0, 2^L) Require: target interval [0, s) with s in [0,2^L) x := random integer in [0, 2^L) m := x * s l := m % 2^L if (l < s) then // Application of the rejection method t := (2^L−s) mod s // 2^L mod s = (2^L − s) mod s while (l < t) do x := random integer in [0, 2^L) m := x * s l := m mod 2^L end while end if return m / 2^L Looking only at the part outside the if, I felt the overall idea quite intuitive - it's essentially the equivalent of return floor(s * rand(0, 1)) except you're working with L bit wide fixed point numbers. The remaining question is "when do you have to actually rejection sample". This part is not as obvious. Thinking about the ranges of the variables involved, it becomes clear that m can take values in [0, s * 2^L) that are steps of s apart, and we want to bucket this space into segments of length 2^L. Some of these segments might contain one more of those possible m values than the others. What the `if` therefore has to do is "shorten" the segments so that each one covers the same number of possible values (at least that is my assumption for now - see below). The dead space in between is then where we have to actually do the "rejection" part of rejection sampling. This seems to be the core of the idea - trim the fat between the segments, so that they can all end at a 2^L boundary, thereby eliminating the non-power-of-two division for figuring out which segment m fell in. At this point I'm slightly stumped why the condition is different in the while. We compare with t instead of s, where t < s, but is this required for equiprobability or just for performance reasons? I am left to wonder whether the results for all "not taking the if branch" rolls are equiprobable. If they are, why not rejection-sample on the if directly, completely avoiding any non-trivial remainder calculation in the first place? If they are not equiprobable, that means the results of taking the if branch have to exactly undo the bias of the if-not-taken cases. I assume this is the case, since the goal was to eliminate as many "hard" remainder calculations as possible. On the other hand, it would be very odd for the probabilities involved to work out just right. I mean, if rejection sampling on the if alone has bias, and you only enter the if with a certain probability, and then need to correct that bias while influenced by the conditional probability - that just seems odd. In other words: Does this algorithm have bias? If yes, it's indeed freaky how the part within the if above compensates that. x := random integer in [0, 2^L) m := x * s l := m % 2^L if (l < s) then start over end if return m / 2^L I'll investigate this more deeply (probably by actually reading the full article and paper), but only after some sleep.
igg
Neutral mention · rule
It is Julia all the way down. julia> @which randn(1000) randn(dims::Integer...) in Random at ~/Software/Julia/share/julia/stdlib/v1.5/Random/src/normal.jl:210 julia> @edit randn(1000) # Opens the normal.jl file in your editor julia> @run randn(1000) # Starts the debugger in VSCode Digging deeper, at least one of the PRs [1] is based on a 2018 paper [2] [1] https://github.com/JuliaLang/julia/pull/29240 [2] https://arxiv.org/abs/1805.10941

Keep following the thread.

More in Statistics & probability

More from the same topics.

2018arXiv

Talent vs Luck: the role of randomness in success and failure

A. Pluchino. A. E. Biondo, A. Rapisarda

The largely dominant meritocratic paradigm of highly competitive Western cultures is rooted on the belief that success is due mainly, if not exclusively, to personal qualities such as talent, intelligence, skills, efforts or risk taking.

16 mentions14 months
2.5HN score