0

Possible Duplicate:
Generating Unique Random Numbers in Java

How can I generate random number between 0 and 1000 and keep on passing unique random number that got generated between 0 and 1000 to a particular method. So for that I generated the number between 0 and 1000 and inserted unique random number between 0 and 1000 in List so that we can compare whether the random number we are generating is already present in the list or not. If it is present then generate it again. But somehow I believe the below code will fail sometimes.

public class Testing4 {
    private static List<Integer> randomNumber;
    private static Random r = new Random();
    private static int rand;
    private static int endRange = 1000;

    public static void main(String args[]) throws IOException {

        randomNumber = new ArrayList<Integer>();

        for (int i = 1; i<= endRange; i++) {
            rand = r.nextInt(endRange);

            if(randomNumber.contains(rand)) {
                rand = r.nextInt(endRange);
            } else {
                randomNumber.add(rand);
            }

        // Pass the unique random number between 0 and 1000 to this method      
                randomNumberMethod(rand);

        }
    }
}
Community
  • 1
  • 1
AKIWEB
  • 19,008
  • 67
  • 180
  • 294
  • 7
    I don't have the code for it off the top of my head, but basically: generate a list of ints from 0 to 1000 and shuffle it, then return each of those values one at a time. This is much less work than trying to keep track of what you've already seen. – Kirk Strauser May 21 '12 at 21:38
  • Are you even serious? Took me 10 minutes to write a great answer. And it got locked. I'm so frustrated. – Andrius Naruševičius May 21 '12 at 21:48
  • @Andrius, Can you copy paste here so that I can see what is the most efficient way of doing this? Or I can provide my email address. – AKIWEB May 21 '12 at 21:50
  • Well, my answer got destroyed already. Just use the answer they linked you to :) Thanks for being very polite :) – Andrius Naruševičius May 21 '12 at 21:57

4 Answers4

3

You are essentially generating a List of the numbers from 0-1000 in a random order. You could achieve this more efficiently in the following way:

public class Testing4 {
    private static List<Integer> randomNumber;
    private static int endRange = 1000;

    public static void main(String args[]) throws IOException {

        randomNumber = new ArrayList<Integer>(endRange);

        for (int i = 0; i<= endRange; i++) {                
            randomNumber.add(i);
        }

        Collections.shuffle(randomNumber);

        for (int i = 0; i<= endRange; i++) {                
            // Pass the unique random number between 0 and 1000 to this method      
            randomNumberMethod(randomNumber.get(i));
        }
    }
}

Think about what will be happening by the time you get to 999 - you will have a 1 in 999 chance of 'guessing' the remaining available number each time round the loop.

Malcolm Smith
  • 3,540
  • 25
  • 29
  • This gives me the IndexOutOfBoundException at some point. – AKIWEB May 22 '12 at 19:22
  • Answer updated to correct. Your original code looped from 1-1000, but I see from your text you actually want 0-1000. My code incorrectly started from 1 in the second loop, causing the IndexOutOfBoundException, but if both are changed to start at 0 you will get the behaviour you are looking for. – Malcolm Smith May 22 '12 at 20:00
  • Thanks for the update Malcolm, so If I need random number between 0 and 1001, I can change in the for loop as `i – AKIWEB May 22 '12 at 20:26
  • yes, just alter the `endRange`. BTW constants in java are usually declared `final` and a written in upper case, i.e. `private static final int END_RANGE = 1000;` – Malcolm Smith May 22 '12 at 20:45
2

To generate a unique list of numbers between 0 and 1000, do the following:

  • create a list containing all the numbers from 0 to 1000
  • shuffle the list using Collections.shuffle()
  • take the first however many numbers you need from the list.

There are cases where a more complex algorithm is called for, but if the range of possible numbers and count of items you need to select are both in the order of 1000 you really may as well just take the first n numbers from a randomly shuffled list of all the possibilities.

If you do want to do something like you suggest, then:

  • use a while loop as another poster has suggested
  • use a Set, not a list, to store the already-chosen values.

However, this becomes inefficient as the number of items to select tends in magnitude towards the number of possible items (so e.g. if you're selecting 10 numbers out of a possible 1000, then it'll be OK; it you're selecting 900 out of a possible 1000 it will become inefficient as more and more numbers need to be rejected each time before finding one not previously chosen).

Neil Coffey
  • 21,615
  • 7
  • 62
  • 83
1

From code inspection, I cannot see anything in this method that will stop it from WORKING, it is just very inefficient.

For one thing, checking to see if the number is one that you have seen randomNumber.contains(rand) will take more and more time the more numbers you generate since each time you do it, you will have to compare it against every number in the list until either you find one that matches or you have tried every number in the list. A better way to do this is to use a HashSet instead of a list which should make every membership test take the same amount of time regardless of how many things you have put in it.

A second, and more significant optimization can be done by noting that you may be asking the wrong question. Are you trying to generate unique, random numbers or are you trying to generate every number between 1 and endRange in random order? If you are going to want every number (or even a non-trivial portion of them), it is going to be a great deal faster to just put every number from 1 to 1000 into a list and then shuffle them using Collections.shuffle. So your generation code is going to be:

java.util.List<Integer> nums = new java.util.ArrayList<Integer>(1001);
for (int i = 0; i <= 1000; i++)
{
   nums.add(new Integer(i));
}
java.util.Collections.shuffle(nums);
0

Your code will fail if 2 numbers that already exist in your ArrayList are generated consecutively. It will use the second number whether it is a duplicate or not. The if statement should be a while loop instead (to keep trying until it generates a unique one).

public class Testing4 {
    private static HashSet<Integer> randomNumber;
    private static Random r = new Random();
    private static int rand;
    private static int endRange = 1000;

    public static void main(String args[]) throws IOException {

        randomNumber = new HashSet<Integer>();

        for (int i = 1; i<= endRange; i++) {    
            do
            {
               rand = r.nextInt(endRange);
            }
            while(randomNumber.contains(rand));

            randomNumber.add(rand);

            // Pass the unique random number between 0 and 1000 to this method      
            randomNumberMethod(rand);

        }
    }
}

edit: as per the comments, you should use a do/while and a hashing data structure rather than an arraylist to speed up the duplicate lookups. The final edits are in the code above.

Dima
  • 23,484
  • 6
  • 56
  • 83
  • A "do while" loop seems more appropriate here. – Nican May 21 '12 at 21:39
  • That's true (for neatness), I'll edit my answer. – Dima May 21 '12 at 21:43
  • The major problem with this approach is that it's going to slow down dramatically as `randomNumber.length()` approaches `endRange`. When `randomNumber` is empty, addition will require exactly one iteration of the while loop. When it's nearly full, it will take approximately `endRange` iterations before succeeding. – Kirk Strauser May 21 '12 at 21:45
  • agreed actually, as far as optimization. a HashSet instead of an ArrayList would speed this up a lot. My initial answer was more for correctness than efficiency. – Dima May 21 '12 at 21:47