8

I want to generate four random numbers in the range of 0 to 9. It's easy to generate four random numbers with Java Random class.

    Random random = new Random();

    int numbers[] = new int[4];

    for(int i=0;i<4;i++){

        numbers[i] = random.nextInt(10);

    }

With this, I can get an array of four numbers easily like, 9369, 4702 etc. In this case there may be possibility of a number to be repeated in four numbers and I don't want such repeat in numbers.

Here, I want to get all four digit in above array to be unique so that I can get output like 9543, 1234 etc.

For this, I have thought following way.

  1. Generate a random number and assigned as first number.
  2. Generate a random number and check with first number if different assigned as second number else generate random number again and repeat and so on.

Is there any better way than above method so that I can get four unique random numbers easily and quickly ?

Any kind of suggestion is appreciated.

Sagar Gautam
  • 9,049
  • 6
  • 53
  • 84
  • Is the order of random numbers important? Can they always be ascending or does that need to be random too? – chux - Reinstate Monica Aug 27 '17 at 14:20
  • 1
    In fact, the answers given there belong here - shuffling is good if you want a significant fraction of the domain (here: 40%), not if it's a small part. The reason is that in the latter case, you're shuffling many numbers you didn't want anyway (20x more, even). – MSalters Aug 27 '17 at 23:41
  • https://stackoverflow.com/questions/8116872/generate-random-numbers-in-array/8116947#8116947 – paxdiablo Aug 28 '17 at 01:16

5 Answers5

32

You can use Collections.shuffle:

// generate a List that contains the numbers 0 to 9
List<Integer> digits = IntStream.range(0,10).boxed().collect(Collectors.toList());
// shuffle the List
Collections.shuffle (digits);
// take the first 4 elements of the List
int numbers[] = new int[4];
for(int i=0;i<4;i++){
    numbers[i] = digits.get(i);
}
Eran
  • 387,369
  • 54
  • 702
  • 768
  • 4
    @SagarGautam I think you should accept this answer instead. It is clearly a better answer than the one you accepted. – lexicore Aug 27 '17 at 09:54
  • 7
    @lexicore not necessarily, because this answer has a runtime of `n` with n being the number of possible elements to choose from where the accepted answer has an expected runtime of something that is much lower than `n`. – SpaceTrucker Aug 27 '17 at 09:56
  • @lexicore But I don't understand logic behind above code. Upvoted though ! – Sagar Gautam Aug 27 '17 at 09:56
  • 1
    @SagarGautam what didn't you understand? The first line may be confusing if you are not familiar with Java 8 Streams, but you can replace it with a regular for loop : `List digits = new ArrayList<>(); for (int i = 0; i < 10; i++) digits.add(i);` – Eran Aug 27 '17 at 09:58
  • 1
    @SagarGautam The code creates a list of digits 0..9 then randomly shuffles it and takes 4 first elements giving you 4 random digits. – lexicore Aug 27 '17 at 09:59
  • @SpaceTrucker This is a complete and working solution whereas the other answer is just very rough sketch of code. – lexicore Aug 27 '17 at 10:05
  • 3
    @lexicore That doesn't make it "clearly a better answer" - that makes it a more complete answer. Amer's algorithm is superior to Eran's in a lot of ways (although Eran's lends itself more to reusabilty). – corsiKa Aug 27 '17 at 16:40
  • 1
    @lexicore - The question specifically asks for efficiency. For selecting n items out of m possibilities, the algorithm in this answer has predictable run time of O(m log m) while Amer's has *expected* run time of O(f(n,m) * n * g(n)) where f(n,m) = the mean for i=0 to n of (m+i)/m, which is approximately 1 for small values of n, and g(n) is the time required for insertion of each of n items into your set implementation, which is 1 for hashsets. For small numbers of selections, Amer's is faster (although unpredictable so not useful in realtime applications). – Jules Aug 28 '17 at 06:25
  • (although, of course the solution provided by paxdiablo at https://stackoverflow.com/questions/8116872/generate-random-numbers-in-array gives O(n+m) which is somewhat better than either, but is more complex and harder to implement) – Jules Aug 28 '17 at 06:32
9

You can use Set for that, the idea is to generate your random number then put it in a set and keep doing this until you have 4 elements in your set, when done you will have 4 unique random numbers stored in your set

Set<Integer> randomSet = new HashSet<>();

while(randomSet.size() <4) 
   randomSet.add //add your generated random number
Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
Amer Qarabsa
  • 6,412
  • 3
  • 20
  • 43
7

If you can create a fast function f that maps the natural numbers to the set of numbers which fulfill your requirement, you can generate just one random number. Your run time is then bounded by f. Provided you can create a reasonably fast f, this is the most efficient way to do it.

The simplest solution would be to put all the numbers which satisfy your criterion in an array and create a random number as an index into that array. -> O(1)

escitalopram
  • 3,750
  • 2
  • 23
  • 24
  • 1
    This would time-optimal, not space-optimal. And we don't even know what "optimal" was supposed to mean. – lexicore Aug 27 '17 at 10:13
  • 1
    @lexicore "Easily and quickly" suggests time-optimal. – David K Aug 27 '17 at 13:06
  • There are only 5040 such possible random sequences, simply storing them as a `byte[]`, and using indexing for your function `f` should work, and not waste too much space. On Oracle HotSpot JVM, it would be around 20176 bytes, for example, including header and reference overhead. – Jörg W Mittag Aug 27 '17 at 14:19
  • @JörgWMittag: Theoretical minimum for such a table is 8359 bytes. If you can store each "set of numbers" as a short, that only takes 10080 bytes. – Mooing Duck Aug 27 '17 at 18:32
7

As you see, there are a many ways to reach your goal. Here is my proposal

Random random = new Random();

// prepare all valid digits
List<Integer> from = new ArrayList<Integer>(Arrays.asList(0,1,2,3,4,5,6,7,8,9));

// take set in an random order
int numbers[] = new int[4];
for(int i = 0; i < numbers.length; i++){
    numbers[i] = from.remove (random.nextInt (from.size()));
}

for (int num : numbers) {
   System.out.println(num); // when you prefer this
}
Shaido
  • 27,497
  • 23
  • 70
  • 73
stefan bachert
  • 9,413
  • 4
  • 33
  • 40
0

EDIT

Since Collections.shuffle also use fisher-yates algorithm. But this variant is choosing the starting point of the sequence radomly. It's like shuffling a deck of card and choose 4 cards from middle vs shuffling a deck of card and choose 4 from top.

Here is a Variant Of Fisher-Yeats shuffling algorithm mention in here https://softwareengineering.stackexchange.com/questions/199644/efficient-way-to-shuffle-objects

    public int[] shuffle() {
        int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        Random r = new Random();
        for (int i = a.length; i > 1; i--) {
            swap(a, i - 1, r.nextInt(i));
        }

        int[] result = new int[4];
        // Variant :: Randomly choosing the starting point of the 
        // sequence, since we need only four number.
        System.arraycopy(a, r.nextInt(a.length - 4), result, 0, 4);
        return result;
    }

    private void swap(int[] a, int i, int i1) {
        int temp = a[i];
        a[i] = a[i1];
        a[i1] = temp;
    }

Reference:https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

mirmdasif
  • 6,014
  • 2
  • 22
  • 28