Package squidpony.squidmath
Class VanDerCorputQRNG
java.lang.Object
squidpony.squidmath.VanDerCorputQRNG
- All Implemented Interfaces:
Serializable
,RandomnessSource
,StatefulRandomness
public class VanDerCorputQRNG extends Object implements StatefulRandomness, RandomnessSource, Serializable
A quasi-random number generator that goes through one of many sub-random sequences found by J.G. van der Corput.
More specifically, this offers the standard family of van der Corput sequences, each defined by a (almost always
prime) base number. Each sequence only changes the state by incrementing it (this works better in the normal usage as
part of a 2D or 3D point generator). The state is internally stored in a 64-bit long that is incremented once per
generated number. The important things to know about this class are: size of state affects speed (prefer smaller
seeds, but quality is sometimes a bit poor at first if you start at 0); the base (when given) should be prime
(smaller bases tend to yield better quality, but oddly, larger bases perform better); this doesn't generate very
random numbers, and is classified as a quasi-random number generator (which can be good for making points that
should not overlap); this is a StatefulRandomness with an additional method for generating quasi-random doubles,
This generator allows a base (also called a radix) that changes the sequence significantly; a base should be prime, and this performs better in terms of time used with larger primes, though quality is also improved by preferring primes that aren't very large relative to the quantity of numbers expected to be generated. Unfortunately, performance is not especially similar to conventional PRNGs; smaller (positive) state values are processed more quickly than larger ones, or most negative states. At least one 64-bit integer modulus and one 64-bit integer division are required for any number to be produced, and the amount of both of these relatively-non-cheap operations increases linearly with the number of digits (in the specified base, which is usually not 10) needed to represent the current state. Since performance is not optimal, and the results are rather strange anyway relative to PRNGs, this should not be used as a direct substitute for a typical RandomnessSource (however, it is similar to, but simpler and faster than,
A VanDerCorputSequence can be a nice building block for more complicated quasi-randomness, especially for points in 2D or 3D. There's a simple way that should almost always "just work" as the static method
Created by Tommy Ettinger on 11/18/2016. Uses code adapted from Alan Wolfe's blog, which turned out to be a lot faster than the previous way I had it implemented.
nextDouble()
; and there are several static methods offered for convenient generation of points on the
related Halton sequence (as well as faster generation of doubles in the base-2 van der Corput sequence, and a special
method that switches which base it uses depending on the index to seem even less clearly-patterned). There are also,
for lack of a better place to put such small code, methods to generate points in the R2 sequence found by Martin
Roberts, which is very similar in 2D to some Halton sequences but has better separation between points.
This generator allows a base (also called a radix) that changes the sequence significantly; a base should be prime, and this performs better in terms of time used with larger primes, though quality is also improved by preferring primes that aren't very large relative to the quantity of numbers expected to be generated. Unfortunately, performance is not especially similar to conventional PRNGs; smaller (positive) state values are processed more quickly than larger ones, or most negative states. At least one 64-bit integer modulus and one 64-bit integer division are required for any number to be produced, and the amount of both of these relatively-non-cheap operations increases linearly with the number of digits (in the specified base, which is usually not 10) needed to represent the current state. Since performance is not optimal, and the results are rather strange anyway relative to PRNGs, this should not be used as a direct substitute for a typical RandomnessSource (however, it is similar to, but simpler and faster than,
SobolQRNG
). So what's it good for?
A VanDerCorputSequence can be a nice building block for more complicated quasi-randomness, especially for points in 2D or 3D. There's a simple way that should almost always "just work" as the static method
halton(int, int, int, int[])
here. If it doesn't meet your needs, there's a little more complexity involved.
Using a VanDerCorputQRNG with base 3 for the x-axis and another VanDerCorputQRNG with base 5 for the y-axis,
requesting a double from each to make points between (0.0, 0.0) and (1.0, 1.0), has an interesting trait that can be
desirable for many kinds of positioning in 2D: once a point has been generated, an identical point will never be
generated until floating-point precision runs out, but more than that, nearby points will almost never be generated
for many generations, and most points will stay at a comfortable distance from each other if the bases are different
and both prime (or, more technically, if they share no common denominators). This is also known as a Halton sequence,
which is a group of sequences of points instead of simply numbers. The choices of 3 and 5 are examples; any two
different primes will technically work for 2D (as well as any two numbers that share no common factor other than 1,
that is, they are relatively coprime), but patterns can be noticeable with primes larger than about 7, with 11 and 13
sometimes acceptable. Three VanDerCorputQRNG sequences could be used for a Halton sequence of 3D
points, using three different prime bases, four for 4D, etc. SobolQRNG can be used for the same purpose, but the
points it generates are typically more closely-aligned to a specific pattern, the pattern is symmetrical between all
four quadrants of the square between (0.0, 0.0) and (1.0, 1.0) in 2D, and it probably extends into higher dimensions.
Using one of the possible Halton sequences gives some more flexibility in the kinds of random-like points produced.
Oddly, using a base-2 van der Corput sequence as the x axis in a Halton sequence and a base-39 van der Corput
sequence as the y axis results in the greatest minimum distance between points found so far while still involving a
base-2 sequence (which is preferential for performance). There, the minimum distance between the first 65536 points
in the [0,1) range is 0.001147395; a runner-up is a y base of 13, which has a minimum distance of 0.000871727 . The
(2,39) Halton sequence is used by halton(int, int, int)
and halton(int, int, int, int[])
.
Created by Tommy Ettinger on 11/18/2016. Uses code adapted from Alan Wolfe's blog, which turned out to be a lot faster than the previous way I had it implemented.
- See Also:
- Serialized Form
-
Field Summary
-
Constructor Summary
Constructors Constructor Description VanDerCorputQRNG()
Constructs a new van der Corput sequence generator with base 7, starting point 37, and scrambling disabled.VanDerCorputQRNG(int base, long seed)
Constructs a new van der Corput sequence generator with the given base (a.k.a.VanDerCorputQRNG(long seed)
Constructs a new van der Corput sequence generator with the given starting point in the sequence as a seed. -
Method Summary
Modifier and Type Method Description static double
altDetermine(long base, int index)
Similar todetermine(int, int)
, but can take bases that aren't prime and can sometimes produce a Halton-like sequence with almost-as-good distance between points.VanDerCorputQRNG
copy()
Produces a copy of this StatefulRandomness that, if next() and/or nextLong() are called on this object and the copy, both will generate the same sequence of random numbers from the point copy() was called.static double
determine(int base, int index)
Convenience method to get a double from the van der Corput sequence with the givenbase
at the requestedindex
without needing to construct a VanDerCorputQRNG.static double
determine2(int index)
Convenience method to get a double from the van der Corput sequence with the base 2 at the requestedindex
without needing to construct a VanDerCorputQRNG.static double
determine2_scrambled(int index)
Method to get a double from the van der Corput sequence with the base 2 at a scrambling of the requestedindex
without needing to construct a VanDerCorputQRNG.static double
determineMixed(int index)
Chooses one sequence from the van der Corput sequences with bases 2, 3, and 5, where 5 is used 1/8 of the time, 3 is used 3/8 of the time, and 2 is used 1/2 of the time, and returns a double from the chosen sequence at the specifiedindex
.boolean
equals(Object o)
long
getState()
Get the current internal state of the StatefulRandomness as a long.static Coord
haltoid(int seed, int width, int height, int xOffset, int yOffset, int index)
Samples a quasi-random Coord sequence that is almost unique to the given seed, getting a Coord with x between xOffset (inclusive) and width + xOffset (exclusive), y between yOffset (inclusive) and height + yOffset (exclusive).static Coord
halton(int width, int height, int index)
Convenience method that gets a quasi-random Coord between integer (0,0) inclusive and (width,height) exclusive and gets the corresponding Coord from the Coord pool.static Coord3D
halton(int width, int height, int depth, int index)
Convenience method that gets a quasi-random Coord3D between integer (0,0,0) inclusive and (width,height,depth) exclusive.static int[]
halton(int width, int height, int index, int[] point)
Convenience method that gets a quasi-random 2D point between integer (0,0) inclusive and (width,height) exclusive and fills it into point.static Coord
halton(int width, int height, int xOffset, int yOffset, int index)
Convenience method that gets a quasi-random Coord between integer (0,0) inclusive and (width,height) exclusive and gets the corresponding Coord from the Coord pool.static int[]
halton(int width, int height, int depth, int index, int[] point)
Convenience method that gets a quasi-random 3D point between integer (0,0,0) inclusive and (width,height,depth) exclusive.int
hashCode()
int
next(int bits)
Using this method, any algorithm that might use the built-in Java Random can interface with this randomness source.double
nextDouble()
Gets the next quasi-random double from between 0.0 and 1.0 (normally both exclusive; only if state is negative or has wrapped around to a negative value can 0.0 ever be produced).long
nextLong()
Gets the next quasi-random long as a fraction ofLong.MAX_VALUE
; this can never produce a negative value.static double
planarDetermine(long base, int index)
A quasi-random number generator of doubles between 0.0 inclusive and 1.0 exclusive, but that has issues when it would be used like a Halton sequence.static int
roberts(int span, int offset, int index)
Martin Roberts' "unreasonably effective" quasi-random int sequence based on the golden ratio.static Coord
roberts(int width, int height, int xOffset, int yOffset, int index)
Martin Roberts' "unreasonably effective" quasi-random point sequence based on a 2D analogue to the golden ratio.void
setState(long state)
Set the current internal state of this StatefulRandomness with a long.String
toString()
static float
weakDetermine(int index)
Given any int (0 is allowed), this gets a somewhat-sub-random float from 0.0 (inclusive) to 1.0 (exclusive) using the same implementation asNumberTools.randomFloat(long)
but with index alterations.static float
weakSignedDetermine(int index)
LikeweakDetermine(int)
, but returns a float between -1.0f and 1.0f, exclusive on both.
-
Field Details
-
Constructor Details
-
VanDerCorputQRNG
public VanDerCorputQRNG()Constructs a new van der Corput sequence generator with base 7, starting point 37, and scrambling disabled. -
VanDerCorputQRNG
Constructs a new van der Corput sequence generator with the given starting point in the sequence as a seed. Usually seed should be at least 20 with this constructor, but not drastically larger; 2000 is probably too much. This will use a base 7 van der Corput sequence and have scrambling disabled.- Parameters:
seed
- the seed as a long that will be used as the starting point in the sequence; ideally positive but low
-
VanDerCorputQRNG
Constructs a new van der Corput sequence generator with the given base (a.k.a. radix; if given a base less than 2, this will use base 2 instead) and starting point in the sequence as a seed. Good choices for base are between 10 and 60 or so, and should usually be prime. Good choices for seed are larger than base but not by very much, and should generally be positive at construction time.- Parameters:
base
- the base or radix used for this VanDerCorputQRNG; for most uses this should be prime but small-ishseed
- the seed as a long that will be used as the starting point in the sequence; ideally positive but low
-
-
Method Details
-
nextLong
Gets the next quasi-random long as a fraction ofLong.MAX_VALUE
; this can never produce a negative value. It is extremely unlikely to produce two identical values unless the state is very high or is negative; state increases by exactly 1 each time this,next(int)
, ornextDouble()
is called and can potentially wrap around to negative values after many generations.- Specified by:
nextLong
in interfaceRandomnessSource
- Returns:
- a quasi-random non-negative long; may return 0 rarely, probably can't return
Long.MAX_VALUE
-
next
Description copied from interface:RandomnessSource
Using this method, any algorithm that might use the built-in Java Random can interface with this randomness source.- Specified by:
next
in interfaceRandomnessSource
- Parameters:
bits
- the number of bits to be returned- Returns:
- the integer containing the appropriate number of bits
-
nextDouble
Gets the next quasi-random double from between 0.0 and 1.0 (normally both exclusive; only if state is negative or has wrapped around to a negative value can 0.0 ever be produced). It should be nearly impossible for this to return the same number twice unless floating-point precision has been exhausted or a very large amount of numbers have already been generated. Certain unusual bases may make this more likely.- Returns:
- a quasi-random double that will always be less than 1.0 and will be no lower than 0.0
-
copy
Description copied from interface:StatefulRandomness
Produces a copy of this StatefulRandomness that, if next() and/or nextLong() are called on this object and the copy, both will generate the same sequence of random numbers from the point copy() was called. This just needs to copy the state so it isn't shared, usually, and produce a new value with the same exact state.- Specified by:
copy
in interfaceRandomnessSource
- Specified by:
copy
in interfaceStatefulRandomness
- Returns:
- a copy of this StatefulRandomness
-
getState
Description copied from interface:StatefulRandomness
Get the current internal state of the StatefulRandomness as a long.- Specified by:
getState
in interfaceStatefulRandomness
- Returns:
- the current internal state of this object.
-
setState
Description copied from interface:StatefulRandomness
Set the current internal state of this StatefulRandomness with a long.- Specified by:
setState
in interfaceStatefulRandomness
- Parameters:
state
- a 64-bit long. You should avoid passing 0, even though some implementations can handle that.
-
toString
-
equals
-
hashCode
-
halton
Convenience method that gets a quasi-random 2D point between integer (0,0) inclusive and (width,height) exclusive and fills it into point. This is roughly equivalent to creating two VanDerCorputQRNG generators, one withnew VanDerCorputQRNG(2, index)
and the other withnew VanDerCorputQRNG(39, index)
, then getting an x-coordinate from the first with(int)(nextDouble() * width)
and similarly for y with the other generator. The advantage here is you don't actually create any objects using this static method, only assigning to point, if valid. You might find an advantage in using values for index that start higher than 20 or so, but you can pass sequential values for index and generally get points that won't be near each other; this is not true for all parameters to Halton sequences, but it is true for this one.- Parameters:
width
- the maximum exclusive bound for the x-positions (index 0) of points this can returnheight
- the maximum exclusive bound for the y-positions (index 1) of points this can returnindex
- an int that, if unique, positive, and not too large, will usually result in unique pointspoint
- an int array that will be modified; should have length 2; if null or too small, a new array will be created- Returns:
- point after modifications to the first two items, or a new array if point is null or too small
-
halton
Convenience method that gets a quasi-random Coord between integer (0,0) inclusive and (width,height) exclusive and gets the corresponding Coord from the Coord pool. This is roughly equivalent to creating two VanDerCorputQRNG generators, one withnew VanDerCorputQRNG(2, index)
and the other withnew VanDerCorputQRNG(39, index)
, then getting an x-coordinate from the first with(int)(nextDouble() * width)
and similarly for y with the other generator. You might find an advantage in using values for index that start higher than 20 or so, but you can pass sequential values for index and generally get points that won't be near each other; this is not true for all parameters to Halton sequences, but it is true for this one.- Parameters:
width
- the maximum exclusive bound for the x-positions (index 0) of points this can returnheight
- the maximum exclusive bound for the y-positions (index 1) of points this can returnindex
- an int that, if unique, positive, and not too large, will usually result in unique points- Returns:
- point after modifications to the first two items, or a new array if point is null or too small
-
halton
Convenience method that gets a quasi-random Coord between integer (0,0) inclusive and (width,height) exclusive and gets the corresponding Coord from the Coord pool. This is roughly equivalent to creating two VanDerCorputQRNG generators, one withnew VanDerCorputQRNG(2, index)
and the other withnew VanDerCorputQRNG(39, index)
, then getting an x-coordinate from the first with(int)(nextDouble() * width)
and similarly for y with the other generator. You might find an advantage in using values for index that start higher than 20 or so, but you can pass sequential values for index and generally get points that won't be near each other; this is not true for all parameters to Halton sequences, but it is true for this one.- Parameters:
width
- the width of the area this can coverheight
- the height of the area this can coverxOffset
- the lowest x-coordinate this can produce, and also added to width to get the upper bound on xyOffset
- the lowest y-coordinate this can produce, and also added to height to get the upper bound on yindex
- an int that, if unique, positive, and not too large, will usually result in unique points- Returns:
- point after modifications to the first two items, or a new array if point is null or too small
-
halton
Convenience method that gets a quasi-random 3D point between integer (0,0,0) inclusive and (width,height,depth) exclusive. This is roughly equivalent to creating three VanDerCorputQRNG generators, one withnew VanDerCorputQRNG(2, index)
another withnew VanDerCorputQRNG(3, index)
, and another withnew VanDerCorputQRNG(5, index)
, then getting an x-coordinate from the first with(int)(nextDouble() * width)
and similarly for y and z with the other generators. The advantage here is you don't actually create any objects using this static method, only assigning to point, if valid. You might find an advantage in using values for index that start higher than 20 or so, but you can pass sequential values for index and generally get points that won't be near each other; this is not true for all parameters to Halton sequences, but it is true for this one.- Parameters:
width
- the maximum exclusive bound for the x-positions (index 0) of points this can returnheight
- the maximum exclusive bound for the y-positions (index 1) of points this can returndepth
- the maximum exclusive bound for the z-positions (index 2) of points this can returnindex
- an int that, if unique, positive, and not too large, will usually result in unique pointspoint
- an int array that will be modified; should have length 3; if null or too small, a new array will be created- Returns:
- point after modifications to the first two items, or a new array if point is null or too small
-
halton
Convenience method that gets a quasi-random Coord3D between integer (0,0,0) inclusive and (width,height,depth) exclusive. This is roughly equivalent to creating three VanDerCorputQRNG generators, one withnew VanDerCorputQRNG(2, index)
another withnew VanDerCorputQRNG(3, index)
, and another withnew VanDerCorputQRNG(5, index)
, then getting an x-coordinate from the first with(int)(nextDouble() * width)
and similarly for y and z with the other generators. This overload always creates a new Coord3D object, so you might preferhalton(int, int, int, int, int[])
, which can reuse an int array. You might find an advantage in using values for index that start higher than 20 or so, but you can pass sequential values for index and generally get points that won't be near each other; this is not true for all parameters to Halton sequences, but it is true for this one.- Parameters:
width
- the maximum exclusive bound for the x-positions (index 0) of points this can returnheight
- the maximum exclusive bound for the y-positions (index 1) of points this can returndepth
- the maximum exclusive bound for the z-positions (index 2) of points this can returnindex
- an int that, if unique, positive, and not too large, will usually result in unique points- Returns:
- a new Coord3D with x,y,z between 0,0,0 (inclusive) and width,height,depth (exclusive)
-
determine
Convenience method to get a double from the van der Corput sequence with the givenbase
at the requestedindex
without needing to construct a VanDerCorputQRNG. You should use a prime number for base; 2, 3, 5, and 7 should be among the first choices to ensure optimal quality unless you are scrambling the index yourself. If speed is the priority, then larger prime bases counter-intuitively perform better than small ones; 0x1337, 0xDE4D, 0x510B and 0xACED are all probable primes (usingBigInteger.isProbablePrime(int)
) that may do well here for speed but will likely require some basic scrambling of the index order. This method on its own does not perform any scrambling on index other than incrementing it and ensuring it is positive (by discarding the sign bit; for all positive index values other than 0x7FFFFFFF, this has no effect). If you want to quickly scramble an int indexi
for this purpose, try(i ^ (i << 7 | i >>> 25) ^ (i << 19 | i >>> 13))
, which may compile to SSE instructions on recent desktop processors and won't risk losing precision on GWT.
Uses the same algorithm asdetermine2(int)
when base is 2, which should offer some speed improvement. The other bases use code adapted from Alan Wolfe's blog, which turned out to be a lot faster than the previous way I had it implemented.- Parameters:
base
- a prime number to use as the base/radix of the van der Corput sequenceindex
- the position in the sequence of the requested base, as a non-negative int- Returns:
- a quasi-random double between 0.0 (inclusive) and 1.0 (exclusive).
-
determine2
Convenience method to get a double from the van der Corput sequence with the base 2 at the requestedindex
without needing to construct a VanDerCorputQRNG. This does not perform any scrambling on index other than incrementing it and ensuring it is positive (by discarding the sign bit; for all positive index values other than 0x7FFFFFFF (Integer.MAX_VALUE
), this has no effect).
Because binary manipulation of numbers is easier and more efficient, the technique used by this method is also used bydetermine(int, int)
when base is 2, and should be faster than other bases.- Parameters:
index
- the position in the base-2 van der Corput sequence, as a non-negative int- Returns:
- a quasi-random double between 0.0 (inclusive) and 1.0 (exclusive).
-
determine2_scrambled
Method to get a double from the van der Corput sequence with the base 2 at a scrambling of the requestedindex
without needing to construct a VanDerCorputQRNG. This performs different scrambling on index than the instance methods on this class perform, and it seems to do well enough while being a little simpler. This is meant to be usable as an alternative to a different base for a van der Corput sequence when you need two different sequences, and are already using base 2 viadetermine2(int)
.
Because binary manipulation of numbers is easier and more efficient, this method should be somewhat faster than the alternatives, likedetermine(int, int)
with base 2. It should take only slightly longer to run thandetermine2(int)
, due to the brief amount of time needed to scramble the index.- Parameters:
index
- the position in the sequence of the requested base- Returns:
- a quasi-random double between 0.0 (inclusive) and 1.0 (exclusive).
-
determineMixed
Chooses one sequence from the van der Corput sequences with bases 2, 3, and 5, where 5 is used 1/8 of the time, 3 is used 3/8 of the time, and 2 is used 1/2 of the time, and returns a double from the chosen sequence at the specifiedindex
. The exact setup used for the choice this makes is potentially fragile, but in certain circumstances this does better thanSobolQRNG
at avoiding extremely close values (the kind that overlap on actual maps). Speed is not a concern here; this should be very much fast enough for the expected usage in map generation (it's used inGreasedRegion.quasiRandomSeparated(double)
.- Parameters:
index
- the index to use from one of the sequences; will also be used to select sequence- Returns:
- a double from 0.0 (inclusive, but extremely rare) to 1.0 (exclusive); values will tend to spread apart
-
weakDetermine
Given any int (0 is allowed), this gets a somewhat-sub-random float from 0.0 (inclusive) to 1.0 (exclusive) using the same implementation asNumberTools.randomFloat(long)
but with index alterations. Only "weak" because it lacks the stronger certainty of subsequent numbers being separated that the Van der Corput sequence has. Not actually sub-random, but should be distributed fairly well (internally usesThrustAltRNG
's algorithm, which does not guarantee that its outputs are unique).
Not all int values for index will produce unique results, since this produces a float and there are less distinct floats between 0.0 and 1.0 than there are all ints (1/512 as many floats in that range as ints, specifically). It should take a while calling this method before you hit an actual collision.- Parameters:
index
- any int- Returns:
- a float from 0.0 (inclusive) to 1.0 (exclusive) that should not be closely correlated to index
-
weakSignedDetermine
LikeweakDetermine(int)
, but returns a float between -1.0f and 1.0f, exclusive on both. UsesNumberTools.randomSignedFloat(long)
internally but alters the index parameter so calls with nearby values for index are less likely to have nearby results.- Parameters:
index
- any int- Returns:
- a sub-random float between -1.0f and 1.0f (both exclusive, unlike some other methods)
-
altDetermine
Similar todetermine(int, int)
, but can take bases that aren't prime and can sometimes produce a Halton-like sequence with almost-as-good distance between points. The base is allowed to be any odd long, (negative bases are allowed). The index can technically also be negative, and if this is given 0 it will not return any specific number (it will vary with the base). This returns a double between 0.0 inclusive and 1.0 exclusive. Better results have been found with larger bases (points tend to be more spread out). It is never as good at spreading out 2D points as a 2,3 Halton sequence, at least for any bases tried so far.
Earlier versions of this method wound up only producing points on parallel lines in 2D, never placing points in between those lines. This sometimes formed a hex-like grid that, as hexagons do, has optimal packing properties, which made the optimal distance seem very good despite the points having a clear pattern. This can still sometimes be useful; when you want optimal distance and don't have a case where a clear pattern on a grid is an issue, it can have high performance. The code for the old way is small, though not simple:((base * Integer.reverse(index) << 21) & 0x1fffffffffffffL) * 0x1p-53
, where base is an odd long and index is any int. It works best in one dimension.- Parameters:
base
- any odd longindex
- any int- Returns:
- a double between 0.0 inclusive and 1.0 exclusive
-
planarDetermine
A quasi-random number generator of doubles between 0.0 inclusive and 1.0 exclusive, but that has issues when it would be used like a Halton sequence. Only ideal in 1D, this produces well-separated points that are aligned to parallel hyperplanes when called with different bases and each base used as an axis for more than 1 dimension. This can produce points with more separation in 2D than a Halton sequence can, but not often. Two bases that do this when used together are the ints 0xDE4DBEEF and 0x1337D00D (or in decimal, -565330193 and 322424845; note that 0xDE4DBEEF is a negative integer); they were tried as a gimmick but nothing else turned out better. They do still produce points on parallel lines, and like all bases, never points between those lines.
Note, the source of this method is one line, and you may see benefits from copying that code into the call-site with minor modifications. This returns(((base * Integer.reverse(index)) << 21) & 0x1fffffffffffffL) * 0x1p-53;
, where base is a long and index is an int (as in the method signature). The multiplier 0x1p-53 is a very small hexadecimal double literal, using the same syntax as other parts of SquidLib use for packed floats; using this helps avoid precision loss. If you want a range larger than 0.0 to 1.0, you can change the multiplier0x1p-53
to some other constant, like declaringfinal double upTo100 = 0x1p-53 * 100.0
before some code that wants quasi-random numbers between 0.0 inclusive and 100.0 exclusive, then using(((base * Integer.reverse(index)) << 21) & 0x1fffffffffffffL) * upTo100;
to get those numbers. It isn't precise to use this technique to get numbers with an upper bound less than 1.0, because 0x1p-53 is about as small as a double can get with precision intact. In that case, you can use(((base * Integer.reverse(index)) << 8) & 0xffffffffffL) * smallBound;
where smallBound has been declared asfinal double smallBound = 0x1p-40 * 0.05;
(where 0.05 can be switched out for any double between 1.0/8192.0 and 1.0).- Parameters:
base
- any odd long; the most significant 21 bits (except the sign bit) are effectively ignoredindex
- any int; if 0 this will return 0- Returns:
- a double between 0.0 inclusive and 1.0 exclusive
-
haltoid
Samples a quasi-random Coord sequence that is almost unique to the given seed, getting a Coord with x between xOffset (inclusive) and width + xOffset (exclusive), y between yOffset (inclusive) and height + yOffset (exclusive). The seed is "almost" unique because even seeds are discouraged; there is an identical sequence for every even seed produced by some odd seed. This generates a not-very random number, reverses its bits as other methods in this class do, then treats that single 32-bit int as two coordinates on a Z-order curve to get separate x and y from them. In practice these Coords are very well-dispersed if only a small amount are sampled and all the index values are close-by, and get closer together as more are sampled. Unlike the Halton sequence, this has very different results with different seeds (Halton only allows bases to be changed), and doesn't involve any division, modulus, conditionals, or loops.- Parameters:
seed
- an int seed that should be an odd numberwidth
- the width of the area this can coverheight
- the height of the area this can coverxOffset
- the lowest x-coordinate this can produce, and also added to width to get the upper bound on xyOffset
- the lowest y-coordinate this can produce, and also added to height to get the upper bound on yindex
- the index in the sequence, almost always a positive int that increases by 1 with each call- Returns:
- a Coord between (xOffset, yOffset) inclusive and (width+xOffset, height+yOffset) exclusive
-
roberts
Martin Roberts' "unreasonably effective" quasi-random int sequence based on the golden ratio. See his blog for more detailed info, but this can be summarized as being extremely good at separating outputs at the expense of seeming less random. Produces an int between offset (inclusive) and offset + span (exclusive), with the int at eachindex
likely to be different for at leastspan / 4
indices (very low spans may offer less of a guarantee).
Note, useroberts(int, int, int, int, int)
for 2D points and this method for 1D values; the same properties won't be preserved if you use a 1D Roberts sequence in 2D.- Parameters:
span
- the size of the range this can returnoffset
- the minimum value this can returnindex
- the index of the int in the 1D Roberts sequence; should be greater than 0, but not required to be- Returns:
- an int between offset inclusive and offset+span exclusive
-
roberts
Martin Roberts' "unreasonably effective" quasi-random point sequence based on a 2D analogue to the golden ratio. See his blog for more detailed info, but this can be summarized as being extremely good at separating points at the expense of seeming less random. Produces a Coord with x between xOffset (inclusive) and xOffset + width (exclusive), and y between yOffset (inclusive) and yOffset + height (exclusive), with the Coord at eachindex
likely to be different for at leastwidth * height / 4
indices (very low sizes may offer less of a guarantee).- Parameters:
width
- the x-size of the space this can place a Coordheight
- the y-size of the space this can place a CoordxOffset
- the minimum x-position of a CoordyOffset
- the minimum y-position of a Coordindex
- the index of the Coord in the 2D Roberts sequence; should be greater than 0, but not required to be- Returns:
- a Coord with x,y between xOffset,yOffset inclusive and xOffset+width,yOffset+height exclusive
-