Generate non repeating random number within range

2020-07-14 07:02发布

问题:

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.

回答1:

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());
  }
}


回答2:

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 :)



回答3:

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.



回答4:

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).