What's up with this broken Java Random.nextInt(long) behavior? - java

I discovered the hard way that this method seems to fail miserably when you feed it a power of 2. Given two Random objects of different seeds, it seems that the first integer they return when asked to return an integer between 0 (inclusive) and and a power of two (exclusive) is always the same; the seeds don't matter. For example:
public static void main(String[] args) {
Random mRandom;
for (int i = 0; i < 10; i++) {
mRandom = new Random(i);
System.out.println(mRandom.nextInt((int) Math.pow(2, 4)));
}
}
Console:
11
11
11
11
11
11
11
11
11
11
I chose 2^4 arbitrarily, but it seems to work for any power of two. What's going on? Furthermore, how can I avoid this?

This problem occurs due to 2 reasons.
Same seed for Random class.
At nextInt(int n), if n is power of 2
1. Same seed for Random class.
Because, you have initiated new Random instance with new seed value which has influence on the nextInt value generation. According to Java docs of Random(long seed).
Creates a new random number generator using a single long seed. The seed is the initial value
of the internal state of the
pseudorandom number generator which is maintained by method next(int).
The invocation new Random(seed) is equivalent to:
Random rnd = new Random();
rnd.setSeed(seed);
If you try to generate random value, without new seed than it will generate real random value, even though new instance of Random class.
for (int i = 0; i < 10; i++) {
mRandom = new Random(); // Without seed
System.out.println(mRandom.nextInt((int) Math.pow(2, 4)));
}
Output: 2 1 12 4 3 9 9 8 2 9
2. At nextInt(int n), if n is power of 2
Besides this, Random#nextInt has effect of power of 2. If n is a power of 2 it will return (int)((n * (long)next(31)) >> 31) which will always same for same n. According to nextInt Algorithm,
public int nextInt(int n) {
if (n <= 0)
throw new IllegalArgumentException("n must be positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do {
bits = next(31);
val = bits % n;
} while (bits - val + (n-1) < 0);
return val;
}

You could also use Math.random and Math.pow together for more simplicity if you wanted.
for (int i = 0; i < 10; i++) {
int pows = (int) Math.pow(2, 4);
int random = (int)(Math.random()*pows);
System.out.println(""+random);
}

Related

Bad Operand types - First type - java.util.Random, Second type - int

I am trying to create a program where a slot machine takes a random int, process it with some if statements for a range of numbers, and returning an amount of cash based on what range of numbers the random integer fits into.
The problem derives from the if statement, if(s >= 0 && s < 6 ), where I am comparing a random object with an int.
/* This method determines the amount of pay off when there is a winner
* # return the amount of payoff
*/
private int getPayOff()
{
Random s = new Random();
s.nextInt(11);
Random rr = new Random();
rr.nextInt(10 + 1);
Random rrr = new Random();
rrr.nextInt(90 + 11);
if(s >= 0 && s < 6 )
return rr;
else if(s >= 6 && s < 9)
return rrr;
return 10000;
}
If I understand your question, you want to generate two values in the range1 1 to 10 and one in the range 11 to 100. You only need one Random (a generator for random values), and then you can use it to generate three random values (actually, two random values depending on the code path). Also, you can simplify your if chain to remove impossible conditions. Something like,
private final Random rand = new Random(); // <-- one.
private int getPayOff() {
int s = 1 + rand.nextInt(10); // <-- [1,10]
if (s < 6) {
return 1 + rand.nextInt(10); // <-- [1,10]
} else if (s < 9) {
return 11 + rand.nextInt(90); // <-- [11,100]
}
return 10000;
}
1We add one because Random.nextInt(int) returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive).

Is Math.random() safe to use?

I have a method which is generating random integers. I don't want integers to be repeated, so I created this code -
int prevInt = 0;
private int randomInt() {
int random = (int) (Math.random() * 3);
//generate random numbers between 0 to 3 inclusive
if(random == prevInt)
return randomInt();
//if previous random number is equal to currently generated
//random number, then call this method again for a different
//random number
prevInt = random;
return random;
//else return the generated random number
}
Is the above code safe to use? At worst case scenario, can it be possible that all random integers generated by Math.random() * 3 are same?
private final Random random = new Random();
private int randomInt(final int prev, final int max) {
final int next = random.nextInt(max - 1);
if(next >= prev){
return next + 1;
}else{
return next;
}
}
This will return an int between 0 and max without the need to repeat.
public static double random()
Returns a double value with a positive sign, greater than or equal to 0.0 and less than 1.0. Returned values are chosen pseudorandomly with (approximately) uniform distribution from that range.
There are two principal means of generating random (really pseudo-random) numbers:
the Random class generates random integers, doubles, longs and so on,
in various ranges.
the static method Math.random generates doubles between 0 (inclusive)
and 1 (exclusive).
To generate random integers:
do not use Math.random (it produces doubles, not integers)
use the Random class to generate random integers between 0 and N.
To generate a series of random numbers as a unit, you need to use a single Random object - do not create a new Random object for each new random number.
Other alternatives are:
SecureRandom, a cryptographically strong subclass of Random
ThreadLocalRandom, intended for multi-threaded cases
please have a look at this post.
SO Reference 1,Reference 2
From the javadoc, Math.random() return a pseudo random and (approximately) uniform distributed number within the range.
So when you don't strict accuracy you may use it. Otherwise search for better solution.
try this trivial code and see for yourself
for (int ran = 0; ran < 10; ran++) {
int random = (int) (Math.random() * 3);
System.out.println(random);
}
Output in my case
2 2 0 2 1 0 0 0 2 1
First, I must point that your question title is not that clear. Safe can mean various thing. In this case I think you mean safety of algorithm of your code, not security nor only Math.random().
Unfortunately, your code is not algorithmically safe. Even if Math.random is safe, Your code has always positive possibility on running at any time: roughly speaking it means there are no guarantee that your code ends in finite time.
Assume you are taking random number among 4 numbers. If you are drawing random number excluding right before number, you're actually not drawing from 4 numbers: it's 3. I suggest another method:
int prevInt = -1;
private int randomInt() {
if (prevInt == -1) {
int random = (int) (Math.random() * 4);
//generate random numbers between 0 to 3 inclusive
} else if
int random = (int) (Math.random() * 3);
random = (random >= prevint) ? (random + 1) % 4 : random;
}
prevInt = random;
return random;
}
private void resetRandom() {
prevInt = -1;
//use when you want to reset information that you have 'before' result.
}
This algorithm ends within finite time. Concern about whether Math.Random() itself is dangerous or whatever will be explained by other nice guys.
Yes, it is safe but we never know what is going to happen, so we can simply multiply Math.random two times to maintain a good safety
int random = (int) (Math.random() * Math.random() * 3);

making a 13 digit random number generator in Java

a friend of mine at Uni, was wanting to generate a bunch of 13 digit numbers so he can test his sorting algorithms on, but was doing it a very long way around, so i've tried to use the following code to generate a settable number of 13 digit numbers.
public class random {
public static void main(String[] args) {
long intArray[] = new long[20]; // to generate more than 20 random numbers increase this and the 'i < 20' to the same number ie. 75
for(int i = 0; i < 20; i++) { // here
intArray[i] = numbGen();
}
for(int j = 0; j < intArray.length; j++) {
System.out.println(intArray[j]);
}
}
public static long numbGen() {
long numb = (long)(Math.random() * 10000000 * 1000000); // had to use this as int's are to small for a 13 digit number.
return numb;
}
}
my issue is now sometimes it will generate a couple of 12 digit numbers in the group of 20 and i want to find a way not to add the number to the array if it is not 13 digits. I've tried if statement but getting stuck on not being able to determine the length (individual characters) of the Long.
Thanks in Advance.
A simple solution:
while(test < 10000) {
long num = (long) (Math.random() * 100000000 * 1000000);
if(Long.toString(num).length() == 13) {
return num;
}
test++;
}
However, a better solution is this:
long number = (long) Math.floor(Math.random() * 9000000000000L) + 1000000000000L;
This will only generate random 13 digit numbers, and you don't need to check if there are more or less digits.
Note that this solution may not scale to a higher number of digits and may not return a perfect distribution of random numbers.
long min = 1000000000000L; //13 digits inclusive
long max = 10000000000000L; //14 digits exclusive
Random random = new Random()
long number = min+((long)(random.nextDouble()*(max-min)));
A generic integer based implementation would be:
public static long randomDigits(int digits) {
if (digits <= 0 || digits > 18) {
throw new IllegalArgumentException("A long can store the random of 18 full digits, you required: " + digits);
}
// use SecureRandom instead for truly random values
final Random r = new Random();
long randomNumber = r.nextInt(9) + 1;
for (int i = 1; i < digits; i++) {
randomNumber = randomNumber * 10L + (long) r.nextInt(10);
}
return randomNumber;
}
or use a shorter version for 13 digits that does not tax the RNG as much:
public static long thirteenRandomDigits() {
final Random r = new Random();
return 1_000_000_000L * (r.nextInt(9_000) + 1_000)
+ r.nextInt(1_000_000_000);
}
These solutions are better to using Math.random() because they don't rely on multiplication with a large number to generate the random values. A double only has 15-17 digits precision, which is very close to the 13 digits number it is multiplied with. This leads to unequal distributions of random numbers. Solutions based on Math.random() won't scale past 13 digits either.
The simple solution for the problem that you described:
public static long numbGen() {
while (true) {
long numb = (long)(Math.random() * 100000000 * 1000000); // had to use this as int's are to small for a 13 digit number.
if (String.valueOf(numb).length() == 13)
return numb;
}
}
This is not the most efficient or most random implementation of generating a 13-digit number but it answers your specific question.
ThreadLocalRandom is a good thing, introduced in Java 1.7
java.util.concurrent.ThreadLocalRandom
.current()
.nextLong(1000000000000L, 10000000000000L);
long randomNumber = 0;
long power = 1;
for(int i = 0; i < 12; i++) { // up to 12 not 13
Random r = new Random();
int randomInt = r.nextInt(10);
randomNumber += (power * randomInt);
power *= 10;
}
// here, the most stupid way to provide last digit to be not zero
Random r = new Random();
int randomInt = r.nextInt(9);
randomInt++;
randomNumber += (power * randomInt);
power *= 10;
First off, a Long doesn't have characters.
If you want to see if it has 13 digits, compare it to 999999999999L.
If you want to insure you have a value w/ 13 digits, get a random number between 0 and 8999999999999L (inclusive) (using the technique you already have to generate a random number in a range) and add it to 1000000000000L.
Why not we try with Unix timestamp in milliseconds.Because that will work for next 200 years and after that we need to only eliminate the trailing digit from the number.
Calendar.getInstance().get(Calendar.MILLISECOND);
and by using this method we don't need any loop or any condition and it will give me a unique number every time.

Random numbers in Java when working with Android

I need to make a random number between 1 and 20, and based on that number (using "If - Then" statements), I need to set the image of an ImageView.
I know that in Objective-C, it goes like this:
int aNumber = arc4Random() % 20;
if (aNumber == 1) {
[theImageView setImage:theImage];
}
How can I do this in Java? I have seen it done this way, but I do not see how I can set the range of numbers (1-20, 2-7, ect).
int aNumber = (int) Math.random()
Docs are your friends
Random rand = new Random();
int n = rand.nextInt(20); // Gives n such that 0 <= n < 20
Documentation:
Returns a pseudorandom, uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random number generator's sequence.
Thus, from this example, we'll have a number between 0 and 19
Math.random() returns an double from [0,1[.
Random.nextInt(int) returns an int from [0, int[.
You can try:
int aNumber = (int) (20 * Math.random()) + 1;
or
Random rand = new Random();
int n = rand.nextInt(20) + 1;
You can use Math.random() to generate a double between 0 and 1 non-inclusive. Android Javadoc here.

Java n must be positive

I receive the 'n must be positive error' on this for loop:
for(int i = 0; i < 8; i++){
array2[i] = CS2004.numbers.get(rand.nextInt(randomNumbers.size()));
}
Whenever I change it to the following, however, it seems to work well.
for(int i = 0; i < 8; i++){
array2[i] = CS2004.numbers.get(rand.nextInt(1000 + randomNumbers.size()));
}
Brief background on the method: it reads in a file containing the first 1000 prime numbers and then randomly adds them to an array of size 8.
Also, if I add the number 1 in place of 1000, it provides me with an answer of 2.0 for every index in the array. If I change it to 10, then the following is the output: [29.0, 29.0, 17.0, 11.0, 5.0, 19.0, 29.0, 2.0]. For the sake of completing the example, when 100 is entered, the following is the result: [61.0, 107.0, 433.0, 193.0, 257.0, 29.0, 463.0, 127.0].
Does the number (10, 100, 1000, ..., n) 'tell' the result that it can add numbers which are up to the length of n? Or is it another explanation altogether?
Can anybody tell me why this error comes up?
Thank you.
The first time rand.nextInt() is called I assume randomNumbers.size() is 0. You are saying you want a random number from 0, which is less than 0 (the number you gave) which is non-sense. You have to give it a positive number so it can give you a sensible result.
My guess is the line should read
array2[i] = CS2004.numbers.get(rand.nextInt(CS2004.numbers.size()));
Here is what JavaDoc has to say about Random#nextInt(int)
Note that the pseudo code has if (n <= 0) throw new IllegalArgumentException("n must be positive");
quoting the excerpts here:
Returns a pseudorandom, uniformly
distributed int value between 0
(inclusive) and the specified value
(exclusive), drawn from this random
number generator's sequence. The
general contract of nextInt is that
one int value in the specified range
is pseudorandomly generated and
returned. All n possible int values
are produced with (approximately)
equal probability. The method
nextInt(int n) is implemented by class
Random as if by:
public int nextInt(int n) {
if (n <= 0)
throw new IllegalArgumentException("n must be
positive");
if ((n & -n) == n) // i.e., n is a power of 2
return (int)((n * (long)next(31)) >> 31);
int bits, val;
do {
bits = next(31);
val = bits % n;
} while (bits - val + (n-1) < 0);
return val;
}

Categories

Resources