Package squidpony.squidmath
Class LowStorageShuffler
java.lang.Object
squidpony.squidmath.LowStorageShuffler
- All Implemented Interfaces:
Serializable
- Direct Known Subclasses:
ShuffledIntSequence
public class LowStorageShuffler extends Object implements Serializable
Gets a sequence of distinct pseudo-random ints (typically used as indices) from 0 to some bound, without storing all
of the sequence in memory. Uses a Feistel network, as described in
this post by Alan Wolfe.
The API is very simple; you construct a LowStorageShuffler by specifying how many items it can shuffle, and you can
optionally use a seed (it will be random if you don't specify a seed). Call
This differs from the version in Alan Wolfe's example code and blog post; it uses a very different round function, and it only uses 2 rounds of it (instead of 4). Wolfe's round function is MurmurHash2, but as far as I can tell the version he uses doesn't have anything like MurmurHash3's fmix32() to adequately avalanche bits, and since all keys are small keys with the usage of MurmurHash2 in his code, avalanche is the most important thing. It's also perfectly fine to use irreversible operations in a Feistel network round function, and I do that since it seems to improve randomness slightly. The
This class is similar to
Created by Tommy Ettinger on 9/22/2018.
next()
on a LowStorageShuffler
to get the next distinct int in the shuffled ordering; next() will return -1 if there are no more distinct ints (if
bound
items have already been returned). You can go back to the previous item with previous()
,
which similarly returns -1 if it can't go earlier in the sequence. You can restart the sequence with
restart()
to use the same sequence over again, or restart(int)
to use a different seed (the bound
is fixed).
This differs from the version in Alan Wolfe's example code and blog post; it uses a very different round function, and it only uses 2 rounds of it (instead of 4). Wolfe's round function is MurmurHash2, but as far as I can tell the version he uses doesn't have anything like MurmurHash3's fmix32() to adequately avalanche bits, and since all keys are small keys with the usage of MurmurHash2 in his code, avalanche is the most important thing. It's also perfectly fine to use irreversible operations in a Feistel network round function, and I do that since it seems to improve randomness slightly. The
round(int, int)
method used here acts like an unbalanced, irreversible PRNG with
two states, and that turns out to be just fine for a Feistel network. Using 4 rounds turns out to be overkill in this
case. This also uses a different seed for each round.
This class is similar to
ShuffledIntSequence
, which extends this class and uses different
behavior so it "re-shuffles" the results when all results have been produced.
Created by Tommy Ettinger on 9/22/2018.
- Author:
- Alan Wolfe, Tommy Ettinger
- See Also:
- Serialized Form
-
Field Summary
-
Constructor Summary
Constructors Constructor Description LowStorageShuffler()
Constructs a LowStorageShuffler with a random seed and a bound of 10.LowStorageShuffler(int bound)
Constructs a LowStorageShuffler with the given exclusive upper bound and a random seed.LowStorageShuffler(int bound, int seed)
Constructs a LowStorageShuffler with the given exclusive upper bound and int seed. -
Method Summary
Modifier and Type Method Description static int
determine(int z)
Used to rearrange the bits of seeds this is given in a way that should partly randomize them.int
encode(int index)
Encodes an index with a 2-round Feistel network.int
next()
Gets the next distinct int in the sequence, or -1 if all distinct ints have been returned that are non-negative and less thanbound
.int
previous()
Gets the previous returned int from the sequence (as yielded bynext()
), or -1 if next() has never been called (or the LowStorageShuffler has reached the beginning from repeated calls to this method).void
restart()
Starts the same sequence over from the beginning.void
restart(int seed)
Starts the sequence over, but can change the seed (completely changing the sequence).static int
round(int data, int seed)
An irreversible mixing function that seems to give good results; GWT-compatible.
-
Field Details
-
Constructor Details
-
LowStorageShuffler
public LowStorageShuffler()Constructs a LowStorageShuffler with a random seed and a bound of 10. -
LowStorageShuffler
Constructs a LowStorageShuffler with the given exclusive upper bound and a random seed.- Parameters:
bound
- how many distinct ints this can return
-
LowStorageShuffler
Constructs a LowStorageShuffler with the given exclusive upper bound and int seed.- Parameters:
bound
- how many distinct ints this can returnseed
- any int; will be used to get several seeds used internally
-
-
Method Details
-
next
Gets the next distinct int in the sequence, or -1 if all distinct ints have been returned that are non-negative and less thanbound
.- Returns:
- the next item in the sequence, or -1 if the sequence has been exhausted
-
previous
Gets the previous returned int from the sequence (as yielded bynext()
), or -1 if next() has never been called (or the LowStorageShuffler has reached the beginning from repeated calls to this method).- Returns:
- the previously-given item in the sequence, or -1 if this can't go earlier
-
restart
Starts the same sequence over from the beginning. -
determine
Used to rearrange the bits of seeds this is given in a way that should partly randomize them. A high-quality 32-bit input, 32-bit output unary hash is pretty hard to find.- Parameters:
z
- any int- Returns:
- a pseudo-random but deterministic change of z
-
restart
Starts the sequence over, but can change the seed (completely changing the sequence). Ifseed
is the same as the seed given in the constructor, this will use the same sequence, acting likerestart()
.- Parameters:
seed
- any int; will be used to get several seeds used internally
-
round
An irreversible mixing function that seems to give good results; GWT-compatible. This is similar toSilkRNG
's way of combining two states, but because this doesn't need to be reversible or even evenly distributed, it has been significantly simplified. It uses one int multiplication, two additions, two subtractions, two XORs, two unsigned right shifts, and one signed right shift.- Parameters:
data
- the data being cipheredseed
- the current seed- Returns:
- the ciphered data
-
encode
Encodes an index with a 2-round Feistel network. It is possible that someone would want to override this method to use more or less rounds, but there must always be an even number.
-