5

I have this method that generates a number (1-10) for each array value listed in this method. I want the whole set of numbers to be displayed as a set of unique numbers. How to do this?

public static int generateNumbers(int[] lotteryNumbers) {

    Random randNum = new Random();

    lotteryNumbers[0] = randNum.nextInt(10);
    lotteryNumbers[1] = randNum.nextInt(10);
    lotteryNumbers[2] = randNum.nextInt(10);
    lotteryNumbers[3] = randNum.nextInt(10);
    lotteryNumbers[4] = randNum.nextInt(10);

    return lotteryNumbers[4];
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
N.Cre
  • 167
  • 1
  • 2
  • 13
  • You should not use random. –  Sep 06 '15 at 12:22
  • 2
    Duplicate of http://stackoverflow.com/questions/8115722/generating-unique-random-numbers-in-java – SatyaTNV Sep 06 '15 at 12:23
  • 1
    @nikpon what's wrong with `Random` ? It's perfectly fine – Dici Sep 06 '15 at 12:29
  • @Dici I didn't get what you asking. If you have a question, ask it in the section of questions. –  Sep 06 '15 at 12:37
  • @nikpon you told him not to use `Random`, but there is nothing wrong with this class – Dici Sep 06 '15 at 12:38
  • @Dici I don't understand what do you say. yes, nothing wrong with it, but the code is wrong. –  Sep 06 '15 at 12:42
  • @nipkon Just look at what you told him : *You should not use random*. Nevermind... – Dici Sep 06 '15 at 12:45
  • @dici yes, He thought that using random he can generate a set of unique numbers. But it's wrong. –  Sep 06 '15 at 12:54
  • By number 1-10, you mean 1 and 10 are both included? In that case, use 1+nextInt(10), because your code nextInt(10) would return range of 0-9 only. – voho Sep 15 '15 at 08:06

4 Answers4

5

An easy solution is to generate a list of the 10 digits, shuffle that list and get the first five elements:

List<Integer> list = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
    list.add(i);
}
Collections.shuffle(list);
Integer[] lotteryNumbers = list.subList(0, 5).toArray(new Integer[10]);

Collections.shuffle(list) is an utility method that randomly permutes the given list in place.

If you are using Java 8, this can be written as:

List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
Collections.shuffle(list);
int[] loterryNumbers = list.subList(0, 5).stream().mapToInt(i -> i).toArray();
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • Thank @Tunkai. But is there a way how I can generate unique numbers using Random? cause I'm only allowed to use random since it's a school assignment. – N.Cre Sep 06 '15 at 12:41
  • 1
    There is no built-in way. What you can do is generate an int using Random in a loop until the number you have obtained has not been found before. – Tunaki Sep 06 '15 at 12:44
  • @N.Cre see my answer, it is basically the same algorithm as `Collections.shuffle` but without using a library method. It is good to know and also good for your assignment, but keep in mind in real-life Tunaki answer is better. I would not mind if you upvoted mine though :D – Dici Sep 06 '15 at 12:46
3

A naive technique is to pick randomly in the set you want to "shuffle" :

public static int[] generateNumbers(int exclusiveMaxValue) {
    List<Integer> values = new ArrayList<>(exclusiveMaxValue);
    for (int i=0 ; i<values.size() ; i++) values.add(i);

    int[] result = new int[exclusiveMaxValue];
    Random rd = new Random();
    for (int i=0 ; i<result.length ; i++) {
       result[i] = values.remove(rd.nextInt(values.size()));
    }
    return result;
}

However, List.remove is usually O(n), so the whole method is quadratic, which is very expensive. You can perform a shuffle in O(n) by simply swapping elements in place (that is what Collections.shuffle does) :

public static int[] generateNumbers(int exclusiveMaxValue) {
    int[] result = new int[exclusiveMaxValue];
    for (int i=0 ; i<result.length ; i++) result[i] = i;

    Random rd = new Random();
    for (int i=result.length - 1 ; i>=0 ; i--) {
       swap(result, i, rd.nextInt(i + 1));
    }
    return result;
}

private static swap(int[] arr, int i, int j) {
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
Dici
  • 25,226
  • 7
  • 41
  • 82
1

This method generates the sequence of length N of unique numbers in range [0, N -1].

public static int[] generateNumbers(int length) {
    final int[] array = new int[length];
    for (int i = 0; i < length; ++i) {
        array[i] = i;
    }
    shuffle(array);
    return array;
}

For shuffling Fisher–Yates algorithm was used:

public static void shuffle(final int[] array) {
    final Random random = new Random();
    for (int i = array.length - 1; i > 0; --i) {
        final int randomIdx = random.nextInt(i + 1);
        final int temp = array[i];
        array[i] = array[randomIdx];
        array[randomIdx] = temp;
    }
}
  • Thanks of (Ronald Fisher and Frank Yates) the algorithm's time complexity is O(n)
  • This implementation works on arrays (with primitives) not on collections (with instances of Integer class that wraps a value of the primitive type int in an object) - it matters if array size is big enough
Karol Król
  • 3,320
  • 1
  • 34
  • 37
0

Here is an alternative way which uses a set and fills it until it grows to the size required. It generates numbersToDraw distinct random numbers in range from min to max (inclusive). It also preserves the order in which numbers were drawn (that is what LinkedHashSet is for).

private static Set<Integer> drawNumbers(int min, int max, int numbersToDraw) {
    if (max < min) {
        throw new IllegalArgumentException("Minimum must be less than maximum.");
    }
    if (max < 0 || min < 0) {
        throw new IllegalArgumentException("Both range numbers must be positive.");
    }
    final int countOfNumbers = max - min + 1;
    if (countOfNumbers < numbersToDraw) {
        throw new IllegalArgumentException("Range is not big enough.");
    }
    final Random randomizer = new SecureRandom();
    final Set<Integer> numbersDrawn = new LinkedHashSet<>();
    while (numbersDrawn.size() < numbersToDraw) {
        final int randomNumber = min + randomizer.nextInt(countOfNumbers);
        numbersDrawn.add(randomNumber);
    }
    return numbersDrawn;
}

If you do not require numbers to be unique, you can use this in Java 8:

final Random randomizer = new SecureRandom();

final List<Integer> numbersDrawn = IntStream
        .range(0, numbersToDraw)
        .mapToObj(i -> min + randomizer.nextInt(max - min + 1))
        .collect(Collectors.toList());

If you do not require numbers to be unique, BUT you want to print their distinct values (is that your original question?):

final Random randomizer = new SecureRandom();

final Set<Integer> numbersDrawn = IntStream
        .range(0, numbersToDraw)
        .mapToObj(i -> min + randomizer.nextInt(max - min + 1))
        .collect(Collectors.toSet());

And one more alternative for your concrete case:

final Set<Integer> distinctNumbers = Arrays
  .stream(lotteryNumbers)
  .distinct() // you can leave this as the set is distinct automatically
  .boxed()
  .collect(Collectors.toSet());
voho
  • 2,805
  • 1
  • 21
  • 26