i will provide a plan to solve this problem as follows:
The whole point is about encoding the (successor) index in order to produce the random order.
As mentioned (reversible) encryption is such a scheme.
Lets see the general outline of such schemes:
Assume the iterator uses a successor function to get the next index/item. For the usual case the successor function would be:
def succ(index):
return index+1
This returns the next index. If seen as a binary operation, the index+1
code creates a new index-pattern (for lack of better word) out of the current index pattern.
Presumably one can generalise this pattern (in binary operations, i.e logN) to create a succesor function that returns the next index but traversed in a shuffled or random unique order.
For example encryption routines can be seen as such a scheme.
An even simpler scheme would be to use modular arithmetic, similar to a pseudo-random number generator, with suitable parameters i.e:
def succ_rand_modulo(index):
return (seed*index+step) mod numItems
For suitable choices of seed and step (e.g relatively prime and large enough) numbers so as to produce random orderings with the uniform property (i.e unbiased)
A linear mapping as above may not produce all orderings, one can assume polynomial mappings with more free parameters to adjust, that can produce all permutations/orderings (see: Permutation Polynomials (over finite fields))
Effectively all these methods are ways of re-encoding the indices (ie encryption, modulo arithmetic, pseudo-random-generation, binary manipulation and so on).
i would say that the most efficient way would be a (polynomial) modulo (i.e pseudo-random number generation) provided the parameters are suitably chosen to be able to produce a uniform/unbiased distribution over all random orderings (see above).
UPDATE:
Another way that needs only numItems
bits to be stored in memory (but requires more time) is a rejection-based method. One can randomly generate or fetch one index/item and set the associated bit on the bit vector to 1 (item fetched), then produce another random index and if associated bit is set (index has been already used) reject it and re-produce another random index and so on. On average this will have a running time of O(numItems)
, but the worst case may be long.
UPDATE2:
Still another promising approach, and also optimal in various cases, is the method by I. STOJMENOVIC to rank/unrank combinarotial objects uniformly and efficiently without computing large integers.
The method works for this case as follows, a random ordering is just a permutation among all orderings. Thus to generate any random ordering, one tries to generate a random permutation of N!
items. Being able to generate a random permutation uniformly (i.e unbiasedly) and without dealing with large integers is a plus. The method of STOJMENOVIC, uses only one random number in [0,1]
and takes this as the probability that a particular permutation has been generated. To adapt the method in this case, one changes the code to return one element of the permutation at a time (keeping some state of where the generator is at any given time) using only an initial seed of a single random number in [0,1]
.
Lecture notes on Lexicographic generation (touching upon random generation as well).
References on same problem / similar approaches:
Unique random number generation based schemes
- How to Generate a Sequence of Unique Random Integers
- List of random number generators (wikipedia)
- GNU, Random number generator algorithms
- Generating combinatorial objects without computing large integers
Cryptography/Cipher based schemes
- Hasty-Pudding cipher
- Block cipher
Encoding-based schemes / Number-theoretic schemes
- Coding theory
- Linear Code
- Permutation Polynomials (over finite fields)