5

I want to generate random numbers within the range 1 to 4, 4 including.
Here is my code:

int num = r.nextInt(4) + 1;  //r is instance of Random.

However, I am running the above code in a loop and don't want repeating random number. What happens now is often I am getting:
1,1,1,2,3,1,4,2,2,1,4,2,4,4,2,1,4,3,3,1,4,2,4,1 as my output.

Here, though the numbers are random within the range(1-4), but often repeated like the number "1"in the first 3 iterations.

What I am looking for is a way to get non repeating random number within the loop. One simple way I know is of keeping the last random number before current iteration and compare, but I am sure there must be better solution to this.
Thanks in advance.

ashish.gd
  • 1,713
  • 1
  • 14
  • 25

4 Answers4

7

Use random.nextInt(range-1) and then map that number to the output number with a function that excludes the previous number:

public class Test {
  private final Random random = new Random();
  private final int range;
  private int previous;

  Test(int range) { this.range = range; }

  int nextRnd() {
    if (previous == 0) return previous = random.nextInt(range) + 1;
    final int rnd = random.nextInt(range-1) + 1;
    return previous = (rnd < previous? rnd : rnd + 1);
  }


  public static void main(String[] args) {
    final Test t = new Test(4);
    for (int i = 0; i < 100; i++) System.out.println(t.nextRnd());
  }
}
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • @MarkoTopolnik Would keeping a previous value for compare would be much lighter in comparison to Collection.shuffle ? If yes, then I think this solution suits me the best. – ashish.gd Dec 13 '12 at 15:18
  • 1
    This could produce a number which is max+1 – Peter Lawrey Dec 13 '12 at 15:19
  • 1
    @PeterLawrey But I start with a random number chosen from a set that is one less in size than the number of allowed integers. If in any doubt, please run it to convince yourself. – Marko Topolnik Dec 13 '12 at 15:19
  • 1
    @ashish.gd This is definitely preferred to shuffling a whole array of numbers at each step. – Marko Topolnik Dec 13 '12 at 15:20
  • 1
    @PeterLawrey It actually can't, he shifts the whole top of the range, i.e. If previous is 2, he takes the range `1 2 3` and shifts the top to be `1 3 4` by adding one to the higher numbers, if that makes sense – durron597 Dec 13 '12 at 15:20
1

As you have more numbers than you have to choice from you have to repeat some numbers. All you can do is minimise the number of immediate repeats.

One way to do this is to use Collections.shuffle which allow you to have numbers in a random order, without repeats and do this each time. You could prevent the last N value being repeated.

To stop consecutive repeating numbers you can reduce the range and use modulus.

int n = 0, max = 4;
Random rand = new Random();

for(int i = 0; i < numbers; i++) {
   n = (n + rand.nextInt(max-1)) % max;
   int numToUse = n + 1;
   // use this number.
}

This work as there is really only max-1 possible values as you are excluding the last value used.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • I think he just wants to prevent `3,2,2,3`, `2,3,2,3` is ok, shuffle is overkill – durron597 Dec 13 '12 at 15:04
  • So adding 1 - 4 in a collection and then calling Collections.shuffle in every iteration, to pick the first element should solve my problem ? – ashish.gd Dec 13 '12 at 15:04
  • @durron597 Yes, I just don't want same numbers side by side and trying my best to avoid shuffle :( – ashish.gd Dec 13 '12 at 15:06
  • @ashish.gd Calling shuffle every time you have used all the values in the list – Peter Lawrey Dec 13 '12 at 15:08
  • @PeterLawrey Using up all values is fine since I am going to use this in a loop. All I want is say if there are 10 iterations in my loop, I will obviously have 10 random numbers between 1 - 4 range. Agreed that they will get repeated. But I just don't want then repeating in sequence i.e. `2,3,2,3` is OK but not `2,2,3,3`. – ashish.gd Dec 13 '12 at 15:15
  • PeterLawrey It's actually not, now that I'm not misreading it anymore, @MarcoTopolnik's answer is far better than mine as it doesn't need to loop. That's why I deleted it – durron597 Dec 13 '12 at 15:18
1

There is no "better" answer. You are getting a random number. Check this line:

1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

this could be perfectly random. So I propose you describe a better requirement. Do you want always a next number that is different from the previous? Do you want a maximum of duplicates in a special rang? lets say within 6 consecutive numbers, every number is allowed to occur twice?

If you bring such a requirement we might be able to help you. otherwise we can just say: what you seeing is really random :)

Fabian Lange
  • 1,786
  • 1
  • 14
  • 18
0

Here is an algorithm:

initialize an array A[4] with the numbers 1-4
set a counter Acnt, the effective size of A. Initialize to 4
for i in 1 to length(output sequence)
   choose a random integer X from 0 to Acnt -1
   save A[X] to your output sequence
   swap(A[X],A[Acnt - 1])
   Acnt--
   if(Acnt == 0) Acnt = lengh(A)

Imagine A is a bag of balls with the numbers 1-4. Each iteration of your loop, you remove a ball. Rather than actually deleting from the array, which is expenisve, you simply hide the ball at the end of the array. When you decrement the number of balls in the bag (Acnt), the next ball you select comes from the non-hidden balls.

When you have no more balls to select, you unhide the balls by resetting the count of balls in your bag back to the full count.

This is basically the standard shuffle algorithm.

Edit: Rereading the question, I see now he allows repeats after only 1 number instead of the whole sequence, in which case it all you need to do to modify this is change if (Acnt == 0) to if(Acnt == length(A) - 1).

frankc
  • 11,290
  • 4
  • 32
  • 49