A technique for selection sampling (sampling without replacement)
Tags: programming, howtos
Many applications require the selection of a subset of objects from a larger set. For example, suppose you are down-sampling a large data set by choosing the indices that you want to keep at random. If you are satisfied with obtaining duplicate indices (in essence, if you are sampling with replacement), this is a trivial matter in most programming language. Python certainly makes this absurdly easy:
import random
def sample(k,n):
return [random.choice( [x for x in range(n)] ) for _ in range(k)]
But what about sampling without replacement? More precisely, what if
you require the selection of k
indices from an array of n
values
without choosing duplicate values but still want the probability of
choosing a certain element to be uniform? Well, if you use Python, you
are in luck again:
import random
def sample_without_replacement(k,n):
return random.sample([x for x in range(n)], k)
But what about other programming languages that may not have similar builtin functionality, such as C++? In this case, a very simple and elegant technique is given by none other than the great Donald E. Knuth. The following algorithm originally was given in The Art of Computer Programming, Volume 2, Section 3.4.2 on p. 137. Briefly put, it works like this:
-
Set
t = 0
andm = 0
.m
represents the number of items selected so far, whilet
is the total number of items that have already been traversed. -
Generate a random number
U
that is uniformly distributed between zero and one. -
If
(N-t)*U >= n-m
, skip the current item by increasingt
by 1 and going back to step 2. Else, select the current item by increasing botht
andm
by 1. Afterwards, either go back to step 2 or stop the algorithm (if sufficiently many items have been sampled already).
A very basic implementation of this algorithm (in C++) is given in the gist below:
I do not know about you, but my gut reaction upon seeing this algorithm
for the first time was How can this possibly work? So, let us briefly
prove the algorithm to be correct. Basically, we have to show that every
item has the same probability of being chosen. The key insight is to
realize that the probability has to change depending on the number of
elements that have already been selected. More precisely, we need to
determine the probability of choosing item t+1
if m
items have
already been selected. This requires some combinatorics. There are
If you are interested or require a more efficient algorithm, please refer to the paper An Efficient Algorithm for Sequential Random Sampling by Jeffrey Scott Vitter in ACM Transactions on Mathematical Software, Volume 13, Issue 1, pp. 58–67, for more details. The paper should be available free-of-charge from the link provided above, but there is also a mirror available, thanks to the good folks at INRIA, who require their publications to be kept in an open archive. Time permitting, I might provide a second blog post and implementation about the more efficient algorithm.
That is all for now, until next time!