0

With the following code I am attempting to generate 10 random numbers between 1 and 50 without any duplicates being printed out.

My current code is in the file RandomNum.java:

public class RandomNum
{
    public static void main(String[] args) 
    {
             int counter = 0;
             int num = 0;
             while(counter<=10)
             {
                    num=(int)(1+Math.random()*(50));
                    System.out.println("The number"+" "+num+" "+"was drawn.");
                    ++counter;
            }
    }
}

This code successfully generates and prints out the value of the numbers, but I want to make it so that the program prints out 10 unique numbers between 1 and 50 rather than including any duplicates.

How would I go about doing this?

Thanks!

Zampanò
  • 574
  • 3
  • 11
  • 33

1 Answers1

2

You can use shuffle.

List<Integer> ints = IntStream.range(1, 50).boxed().collect(toList());
Collections.shuffle(ints);
List<Integer> ten = ints.subList(0, 10);

Or you can use a LinkedHashSet. Note: if you use a HashSet the order may not be very random. e.g. if you add 0 to 10 to a HashSet in any order it will happen to be in sorted order.

Set<Integer> ints = new LinkedHashSet<>();
Random rand = new Random();
while(ints.size() < 10)
    ints.add(rand.nextInt(50) + 1);
// copy to a list to taste.

or you can use a Map.

List<Integer> collect = IntStream.range(1, 50).boxed()
        .collect(groupingBy(i -> Math.random()))
        .values().stream().flatMap(Collection::stream)
        .limit(10).collect(toList());

or you can use Random.ints

List<Integer> collect = new Random().ints(1, 50)
        .boxed()
        .collect(Collectors.toCollection(LinkedHashSet::new)) // distinct
        .stream().limit(10)
        .collect(Collectors.toList());

Note: in a previous answer .distinct() was used however, the choice of set used to perform the uniqueness is not defined in in fact happens to use HashSet in Java 8 which is a poor choice as previously mentioned.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • That won't prevent duplicates, right? – fge Nov 27 '15 at 15:53
  • 1
    Plus one for using LinkedHashSet rather than HashSet. – Paul Boddington Nov 27 '15 at 15:55
  • @fge which one might have duplicates? The second one might generate duplicates and it will be really inefficient if you need most of the values. Ok for just 20% of values. – Peter Lawrey Nov 27 '15 at 15:56
  • 2
    I'm not sure I like the third solution. It seems unnecessarily complicated and doesn't it rely on a implementation detail of `groupingBy`? If `groupingBy` used a `LinkedHashMap`, it wouldn't be random. – Paul Boddington Nov 27 '15 at 16:05
  • @PaulBoddington The assumption is that integers from 1 to 50 will have a constant hash value and poor randomness in a HashSet which makes them bad keys/elements to randomly add to a set. However Math.random() which is a random double will have random arrangement which is different each time it is run. – Peter Lawrey Nov 27 '15 at 16:12
  • @PaulBoddington While the current implementation of `groupingBy` happens to use HashMap, you are right that other maps may be poor in which case you would nee to provide a supplier to use `HashMap` or `TreeMap` – Peter Lawrey Nov 27 '15 at 16:15
  • 1
    Hey... What about `Random.ints()`? – fge Nov 27 '15 at 16:35
  • @fge good point however `distinct()` isn't a good choice. I have added a note: – Peter Lawrey Nov 27 '15 at 16:41
  • 1
    I just checked the source code and `IntStream.distinct` is done using a `LinkedHashSet`. Interestingly, the spec for `Stream.distinct` specifies that the order is maintained, but not so for `IntStream.distinct`. Since the order is guaranteed to be maintained, there's no need to do `.boxed().collect(toCollection(LinkedHashSet::new)).stream()` as `.boxed().distinct()` works. – Paul Boddington Nov 27 '15 at 20:15