java.util.Random NextDouble() is biased towards higher uniform number - java

I am using NextDouble() to generate uniform random number. I generated random number with 500 different seeds and found that the generated uniform random number is biased towards higher uniform number (mean was 0.93). Which is very absurd. I then divided the seed by 10000 and the random number generated was perfect (mean was 0.51).
I found that the seed should not be more than 48 bits and so made sure that the seed I provide (seed for NextDouble() is generated randomly using nextLong() with seed = 12345) is less 48 bits. Then I re-generated the random number 500 times and found the same problem. Does anyone had similar issues with NextDouble()?
Please see the code below:
Random randObject = new Random();
randObject.setSeed(12345);
long[] seed = new long[501];
for (int i = 1; i<=500; i++)
seed[i] = (long)( ( randObject.nextLong() / (double)Long.MAX_VALUE ) * Math.pow( 2, 48 ) ); // to make sure seed is not more than 48 bits
for (int i = 1; i<=500; i++)
{
randObject.setSeed(seed[i]);
randObject.nextDouble();
}
Thanks

"...I generated [a] random number with 500 different seeds..."
That's your problem. A random number generator generates a sequence of numbers from a given seed, that, taken as a sequence, are random and uniformly distributed. If you only take the first number from the sequence, there are no such guarantees. In fact, an RNG might decide to return the seed itself as its first value--that would be perfectly valid. In practice, they generally hash the seed somehow.
So your 500 numbers are not a random sequence at all. They are simply a hash function of your input seeds, and there's no reason whatsoever to expect them to be random.

The Java-8 version seems to offer a different picture:
public void test() {
Random r1 = new Random();
Random r2 = new Random();
r1.setSeed(12345);
double average = IntStream.range(0, 5000)
// 5000 random longs.
.mapToLong(i -> r1.nextLong())
// Used as seeds to a Random
.mapToDouble(l -> {
r2.setSeed(l);
return r2.nextDouble();
})
// Gather stats.
.summaryStatistics()
// Average,
.getAverage();
System.out.println("Average: " + average);
}
Prints 0.5000... fairly consistently.

Congratulations
I found that the seed should not be more than 48 bits and so made sure that the seed I provide (seed for NextDouble() is generated randomly using nextLong() with seed = 12345) is less 48 bits.
You proved experimentally that java.util.Random is 48bit LCG generator
From its sources
48: * The algorithm is described in <em>The Art of Computer Programming,
49: * Volume 2</em> by Donald Knuth in Section 3.2.1. It is a 48-bit seed,
50: * linear congruential formula.
51: *
link http://developer.classpath.org/doc/java/util/Random-source.html

Java's Random is a linear congruential generator (LCG), which propagates the next state from the current state. You're using the output of an LCG to seed itself, so each of your seeds is going to be related to the outputs produced by the other seeds. From that point on, all bets are off as to the distributional behavior of the results.
Seed your 500 streams from /dev/random, or with the output of an entirely different class of generator such as a feedback shift register or Mersenne twister. Then, if you're still observing bias, write up a journal paper.

Related

Best way to generate unique Random number in Java

I have to generate unique serial numbers for users consisting of 12 to 13 digits. I want to use random number generator in Java giving the system. Time in milliseconds as a seed to it. Please let me know about the best practice for doing so. What I did was like this
Random serialNo = new Random(System.currentTimeMillis());
System.out.println("serial number is "+serialNo);
Output came out as: serial number is java.util.Random#13d8cc98
For a bit better algorithm pick SecureRandom.
You passed a seed to the random constructor. This will pick a fixed sequence with that number. A hacker knowing the approximate time of calling, might restrict the number of attempts. So another measure is not using the constructor and nextLong at the same spot.
SecureRandom random = new SecureRandom​();
long n = random.nextLong();
A symmetric bit operation might help:
n ^= System.currentMillis();
However there is a unique number generation, the UUID, a unique 128 bits number, two longs. If you xor them (^) the number no longer is that unique, but might still be better having mentioned the circumstantial usage of random numbers.
UUID id = UUID.randomUUID();
long n = id.getLeastSignificantBits() ^ id.getMostSignificantBits();
Create a random number generator using the current time as seed (as you did)
long seed = System.currentTimeMillis();
Random rng = new Random​(seed);
Now, to get a number, you have to use the generator, rng is NOT a number.
long number = rng.nextLong();
According to the documentation, this will give you a pseudorandom number with 281.474.976.710.656 different possible values.
Now, to get a number with a maximum of 13 digits:
long number = rng.nextLong() % 10000000000000;
And to get a number with exactly 13 digits:
long number = (rng.nextLong() % 9000000000000) + 1000000000000;
First, import the Random class:
import java.util.Random;
Then create an instance of this class, with the current milliseconds as its seed:
Random rng = new Random(System.currentTimeMillis());
This line would generate an integer that can have up to 13 digits:
long result = rng.nextLong() % 10000000000000;
This line would generate an integer that always have 13 digits:
long result = rng.nextLong() % 9000000000000 + 1000000000000;
There are three ways to generate Random numbers in java
java.util.Random class
We can generate random numbers of types integers, float, double, long, booleans using this class.
Example :
//Random rand = new Random();
// rand_int1 = rand.nextInt(1000)
Math.random method : Can Generate Random Numbers of double type.
random(), this method returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0.
Example :
Math.random());
//Gives output 0.15089348615777683
ThreadLocalRandom class
This class is introduced in java 1.7 to generate random numbers of type integers, doubles, booleans etc
Example :
//int random_int1 = ThreadLocalRandom.current().nextInt();
// Print random integers
//System.out.println("Random Integers: " + random_int1);

How to generate a SecureRandom number with a specific bit range [duplicate]

This question already has answers here:
How do I generate random integers within a specific range in Java?
(72 answers)
Closed 1 year ago.
I have maximum number of bits that is not bit aligned E.g. 35 and required to generate unique random number from 1 - 68719476734 (max number for 35 bits).
Could use SecureRandom but will have to extract 5 bytes out of it and convert to Long maybe, but chances of collision seem to a concern. What are some options to generate a random for this range.
Could i seed this random with nanoTime maybe if there is a collision and regenerate in this range.
First, a few comments:
The max value for 35 bits is 34359738367, not 68719476734.
68719476734 is not even the max value for 36 bits, 68719476735 is.
Do not seed a SecureRandom. That reduces the security of it.
To generate a 35-bit random number, excluding value zero, just generate a long random value, take the last 35 bits, and redo if the value is zero.
SecureRandom r = new SecureRandom();
long value;
do {
value = r.nextLong() & ((1L << 35) - 1);
} while (value == 0);
There may be a better way, but perhaps this can help. The first simply prints the single number generated from a stream. The second masks off the required bits. Interesting that the values for the same seed are different (which I can't explain). But the first method which allows a range might be sufficient for what you want. I am clearly not an expert on this but provided it to foster some ideas.
SecureRandom r = new SecureRandom();
r.setSeed(23);
// generate a sum of 1 element to get the element from the stream.
long v = 0;
while (v == 0) {
v = r.longs(1,1L<<34, (1L<<35)-1).sum();
}
System.out.println(v);
System.out.println(64-Long.numberOfLeadingZeros(v));
r.setSeed(23);
long vv = 0;
while (vv == 0) {
vv = r.nextLong()&((1L<<35)-1);
}
System.out.println(vv);
System.out.println(64-Long.numberOfLeadingZeros(vv));
prints
31237208166
35
9741674490
34
My assumption here was that the stream version above would not be provided if the random numbers did not meet the secure requirements.

How to scale pseudo random numbers to a specific range?

How would I scale random numbers to say 1-10
whithout loosing the randomness?
I know, just using % 10 is not a good idea.
And in addition, I need a seed value, so I can test on
determinsitic numbers,
but I have not understood what value this seed should have
and I how it influences the sequence.
Is there a difference if it is high or low ?
thanks in advance.
import java.util.Random;
Random random = new Random( yourSeed );
int randomInt = random.nextInt(10) + 1 ;
The seed has no other purpose than to give the PRNG a starting point. It is purely arbitrary and will not influence the distribution of the random numbers.
The nextGaussian() is different in that it returns floating point numbers distributed around a bell curve.
usually when trying to get a random value you'll use something like this:
public int getRandom( int min, int max )
{
return (int) Math.round( Math.random() % (max - min) + min );
}
Or, as I just remembered from Stavr00's answer, use one of the built in functions from java.util.Random

Random number array with lazy initialization

For a distributed application project I want to have two instances share the same/know (pseudo-)random numbers.
This can be achieved by using the same seed for the random number generator (RNG). But this only works if both applications use the RNG output in the same order. In my case this is hard or even impossible.
Another way of doing this is would be (psedocode):
rng.setSeed(42);
int[] rndArray;
for(...) {
rndArray[i] = rng.nextInt();
}
Now both applications would have the same array of random numbers and my problem would be solved.
BUT the array would have to be large, very large. This is where the lazy initialization part comes in: How can I write a class that where rndArray.get(i) is always the same random number (depending on the seed) without generating all values between 0 and i-1?
I am using JAVA or C++, but this problem should be solvable in most programming languages.
You can use a formula based on a random seed.
e.g.
public static int generate(long seed, int index) {
Random rand = new Random(seed + index * SOME_PRIME);
return rand.nextInt();
}
This will produce the same value for a given seed and index combination. Don't expect it to be very fast however. Another approach is to use a formula like.
public static int generate(long seed, int index) {
double num = seed * 1123529253211.0 + index * 10123457689.0;
long num2 = Double.doubleToRawLongBits(num);
return (int) ((num2 >> 42) ^ (num2 >> 21) ^ num2);
}
If it's large and sparse you can use a hash table (downside: the numbers you get depend on your access pattern).
Otherwise you could recycle the solution to a problem from the Programming Pearls (search for something like "programming pearls initialize array"), but it wastes memory iirc.
Last solution I can think of, you could use a random generator which can efficiently jump to a specified position - the one at http://mathforum.org/kb/message.jspa?messageID=1519417 is decently fast, but it generates 16 numbers at a time; anything better?

Generate random double from random long

in Java I have a random generator that generates random number from -2^63 to 2^63 and that is NOT the java.util.Random.
I need to generate random double in (0,1), this is what I've done so far:
return (seed/(double)(9223372036854775807L))/2+0.5;//seed is a random long
Is this right? Are there any numerical problem (underflow?)?
Could be better/faster?
Thank you.
I would use Math.scalb as the most efficient and ensures there is no funny behaviour due to rounding or representation error
double d = Math.scalb(seed >>> 1, -63);
You can only use 53 bits in a double so some will be discarded.
If you run
long seed = Long.MAX_VALUE;
System.out.println(Math.scalb(seed >>> 1, -63));
prints
0.5
With a seed of 0 you get 0.0
With a seed of -1 you get 1.0
I would prefer to see just a single division.
0.5+(seed/1.84467440737096E+19);
That said, you are going to run up against issues with floating point accuracy since you have 64 random integer bits which you then try to squeeze into 53 bits of double precision. You may be better off making a dedicated generator for floating point values, but I could not say for sure since I don't know your motivation.
The fastest way would probably just be to set the first three bits in your long to 0 and then use those bits to make a double.:
double rand = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
This works by forcing the sign to positive, and exponent to be less than 0, which will cause the mantissa to be shifted right at least once. It gives an even distribution assuming all the ints in the long are completely random. Here is a full Java program that uses Random to generate random longs, and then this method to convert them to double's between 0 and 1:
import java.util.Random;
class Main{
public static void main(String[] args){
Random rand = new Random();
long seed = rand.nextLong();
double x = Double.longBitsToDouble(seed & 0x1FFFFFFFFFFFFFFFL);
System.out.println(x);
}
}
This is the output of 10 executions:
1.1211565592484309E-247
8.84224349357039E-242
6.956043405745214E-271
3.747746366809532E-232
9.302628573486166E-158
1.1440116527034282E-166
1.2574577719255876E-198
5.104999671234867E-269
3.360619724894072E-213
1.5654452507283312E-220
Edit
This gives a uniform distribution of all possible doubles between 0 and 1. Since there are many more small doubles you will likely never see a number close to 1. You can fix this by generating a new exponent based on the bits of the existing one, but you need a loop to do it, so it probably isn't the fastest method after factoring this in:
long exponent = 0;
for(int i = 52; (seed >>> i & 1) > 0; i++) exponent++;
double x = Double.longBitsToDouble(seed & 0x000FFFFFFFFFFFFFL | ((1022 - exponent) << 52));
0.4773960377161338
0.929045618651037
0.7183096209363845
0.33962049395497845
0.45568660174922454
0.11670190555677815
0.09371618427480996
0.8192870898479095
0.9365016017283178
0.11311614413193898
Not exactly. I think that easier way is to do the following:
new Random(seed).nextDouble()
Unless I'm misreading your need to have a random double from 0 to 1, Java's built in Math.random does just that. So you could avoid all the conversion you are currently doing.

Categories