4

I want to generate 6 different random numbers by using Math.random and store them into an array. How can I make sure that they are different? I know I need to use for-loop to check the array but how...

This is the range. I only need numbers between 1 and 49. ( 1 + (int) (Math.random() * 49) )

coding
  • 141
  • 2
  • 4
  • 11
  • 1
    Just keep generating numbers and adding them to the array as long as they are unique; generate a number and check it against the others in the array. – Brendan Lesniak Mar 22 '14 at 22:09
  • Exactly the same way you would if you were doing it by hand. Roll dice to get a number, check if it's a number you don't already have, record it, repeat until you have as many as you need. -- Or implement your own shuffle, which is the more general solution. – keshlam Mar 22 '14 at 22:10

10 Answers10

27

In Java 8:

final int[] ints = new Random().ints(1, 50).distinct().limit(6).toArray();

In Java 7:

public static void main(final String[] args) throws Exception {
    final Random random = new Random();
    final Set<Integer> intSet = new HashSet<>();
    while (intSet.size() < 6) {
        intSet.add(random.nextInt(49) + 1);
    }
    final int[] ints = new int[intSet.size()];
    final Iterator<Integer> iter = intSet.iterator();
    for (int i = 0; iter.hasNext(); ++i) {
        ints[i] = iter.next();
    }
    System.out.println(Arrays.toString(ints));
}

Just a little messier. Not helped by the fact that it's pretty tedious to unbox the Set<Integer> into an int[].

It should be noted that this solution should be fine of the number of required values is significantly smaller than the range. As 1..49 is quite a lot larger than 6 you're fine. Otherwise performance rapidly degrades.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • It won't matter to OP's requirement, but still... I can't help but disappreciate such cute one-liners with bad performance characteristics. I've seen my share of them in Clojure. – Marko Topolnik Mar 22 '14 at 22:17
  • I agree with @MarkoTopolnik. While this is a nice one-liner, the solution of JBNizet is much more efficient. +1 Though. – Alexis C. Mar 22 '14 at 22:19
  • In both cases HashSet is used which is a poor choice. e.g. add 0 to 10 to a HashSet in any order and it will happen to place them in sorted order. i.e. HashSet isn't so random. – Peter Lawrey Nov 27 '15 at 16:02
  • 1
    @PeterLawrey I hadn't thought about the natural order if items in a `Set` - that's a very good point. I suppose `LinkedHashSet` would work in the second case. In the first case I'm not sure how to fix that... – Boris the Spider Nov 27 '15 at 16:04
12

Create a list containing the numbers 1 to 49.

Create a random number x between 0 and the size of the list, take the number being at index x in the list, and remove it from the list.

Repeat the previous step 5 times. And you're done. Note that java.util.Random has a nextInt(int max) method that you should use instead of Math.random().

Note regarding performance: this solution has an advantage compared to the "try until you get 6 different numbers" various solutions: it runs in a O(n) time. It doesn't matter much for 6 unique numbers out of 50, but if you want to get 48 or 49 unique random numbers out of 50, you'll start seeing a difference, because you might have to generate many random numbers before getting one that isn't already in the set.

EDIT:

to reduce the cost induced by the removal of the elements in the list, you could instead simply replace the element at index x with the last element of the list (and at the second iteration, with the element at size - 2, etc.)

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Just *replacing* the used slot with the number from the end would be enough, unless you use the end of the array as your output. You could easily do that with `subList`, though. – Marko Topolnik Mar 22 '14 at 23:17
  • Doesnt that change the distribution of the picked numbers? At least for that replaced number the odds are twice as high as for other numbers to be picked. Addionally, for replacing at size-2 and so on, you would need to check that the number you currently picked, is not at index = size (or size-2...). – SerialSensor Apr 18 '20 at 08:51
2

Generate any 6 numbers (not necessarily different). Order them.

a1 <= a2 <= a3 <= a4 <= a5 <= a6

Now take these 6 numbers

a1 < a2 + 1 < a3 + 2 < a4 + 3 < a5 + 4 < a6 + 5

These 6 are different and random.

The idea of this construct comes from some combinatorial proofs.

Its advantage is that it's simple, fast, and deterministic.
I think the time complexity is O(count*log(count)).
I wonder if it can be improved.

import java.util.TreeMap;

public class Test005 {

    public static void main(String[] args) {
        int count = 6;
        int min = 1;
        int max = 49;

        // random number mapped to the count of its occurrences
        TreeMap<Integer, Integer> mp = new TreeMap<Integer, Integer>();
        for (int i=0; i<count; i++){
             int d = ( min + (int) (Math.random() * (max-count+1)) );
             if (!mp.containsKey(d)){
                 mp.put(d, 0);
             }
             mp.put(d, mp.get(d) + 1);
        }

        // now ensure the output numbers are different
        int j = 0;
        for (int num : mp.keySet()){
            int cnt = mp.get(num);
            for (int i=0; i<cnt; i++){
                System.out.println(num + j);
                j++;
            }
        }
    }

}
peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • 1
    I like the idea. But it seems like you forgot to handle the case where the increased number exceeds the bounds – prom85 Mar 23 '18 at 07:48
  • Yeah, seems I should have done `int max = 44;` Then the increased ones cannot go over 49. – peter.petrov Mar 23 '18 at 13:01
  • doesn't this reduce the probability to get numbers in the range of 0 to 5? It's still a good scaleable solution, that's faster than the others – prom85 Mar 24 '18 at 17:02
2

You can use a Set.

Set<Integer> s = new HashSet<>();
while(s.size() != 6){
   s.add(1 + (int) (Math.random() * 49));
}

Integer[] arr = s.toArray(new Integer[s.size()]);

This is enough to do this in your case because the number of distinct random numbers is relatively small compared to the size of the range you generate them.

Otherwise I would go with @JBNizet approach.

Alexis C.
  • 91,686
  • 21
  • 171
  • 177
2

I've just came up with a small idea for Java 8-.

Set<Integer> set = new LinkedHashSet<>();
while(set.size() != 6)
    set.add(rnd.nextInt(49) + 1);
ar4ers
  • 740
  • 5
  • 19
1

Instead of checking that the array has no duplicates, you can use a bit more smartness while generating the numbers, such that uniqueness is enforced at the outset.

  1. Create a boolean[] as long as your range (49 entries);
  2. generate a random number from the full range;
  3. put that number into your output array;
  4. "cross out" the corresponding index in the boolean[];
  5. now generate another random number, but curtail the range by one (now 48);
  6. instead of directly using that number as output, scan your boolean[], counting all the non-crossed entries. Stop when you reach the count equal to the random number generated in step 5. The number corresponding to that entry is your output number;
  7. go to step 4.
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • I don't get it (read it 2-3 times). I wonder if it has anything to do with the approach suggested in my answer. Can you post some link to a description of this algorithm? – peter.petrov Mar 22 '14 at 22:41
  • Maybe this? http://stackoverflow.com/questions/196017/unique-random-numbers-in-o1 – peter.petrov Mar 22 '14 at 22:46
  • @peter.petrov: it's basically the same idea as in my answer, except that instead of removing numbers from the list you mark them as deleted in the boolean array and don't count the logically deleted numbers when iterating. Now that I think about it, I'm not sure it would be more efficient than my algorithm, which becomes more and more efficient as long as numbers are removed from the list. – JB Nizet Mar 22 '14 at 22:47
  • Oh ... OK, not fully convinced but OK, I believe you on this one ;) – peter.petrov Mar 22 '14 at 22:49
  • @JBNizet Not fully sure but seems to me none of the answers here is good enough. The one from the link I posted claims to be O(count)?! Never mind, I should read up a bit. Seems your and Marko's answers are basically similar to this: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle – peter.petrov Mar 22 '14 at 22:51
  • 1
    @peter.petrov: swapping numbers instead of removing them is indeed a good idea. There's is a good reason I'm not named Knuth nor Fisher-Yates :-) – JB Nizet Mar 22 '14 at 22:55
  • @JBNizet The reason why I think your approach would be less efficient is because it involves copying (bulk writing) vs. only reading and changing one index each time in my case. Also, array initialization is more involved in your case. – Marko Topolnik Mar 22 '14 at 23:13
1

in your case n=6

     public static int[] chooseAny(int n){
        int[] lottery = new int[n];
        int[] chooseFrom = new int[49];
        for(int i=1 ; i <= 49 ; i++)
            chooseFrom[i-1] = i;
        Random rand = new Random();
        int N = 49;
        int index;
        for(int i=0 ; i < n ; i++){
            //pick random index
            index = rand.nextInt(N);
            lottery[i] = chooseFrom[index];
            chooseFrom[index] = chooseFrom[N-1];
            N--;
        }
        return lottery;
    }
0

Just keep generating numbers and adding them to the array as long as they are unique; psuedocode:

num = genNextRand()

For (array length)
    If (num not in array)
        addToArray()

Repeat while length not equal 6
Brendan Lesniak
  • 2,271
  • 4
  • 24
  • 48
0

Create a variable last; initialize it to 0.

Next, in a loop x from 0 to 5, create a random number between last+1 and 49-6+x. Store this number in a list, and set last to the number generated this way.

You will end up with an ordered list of 6 random numbers in the range of 1..49 with no repeats.

Jongware
  • 22,200
  • 8
  • 54
  • 100
0

That code generate numbers from 6 to 0 and save in ArrayList.

If generated number was duplicated the program generate numbers again.

If generated number is different that number is added.

Code:

private ArrayList<Integer> arraylist = new ArrayList<Integer>();

private Random rand = new Random();

public void insertNumber() {
    while (true) {
        int i = generateNumber();
        if(!isGenerateNumberExists(i)){
            addNumber(i);
            break;
        }
    }
}
//Generate numbers
private int generateNumber() {
    return rand.nextInt(6);
}
//Confirm if that number exists
private boolean isGenerateNumberExists(int y) {
    for (int num : arraylist) {
        if (num == y) {
            return true;
        }
    }
    return false;
}
//Add number to arrayList
private void addNumber(int x) {
    arraylist.add(x);
}
Ilya Budu
  • 119
  • 1
  • 9
  • 1
    Welcome to SO! Although this might answer the question, try adding information as to how and why this is a suitable answer. Additionally, adding links to support your claim will add more weight! – Rick M. Oct 02 '18 at 09:28