简体   繁体   中英

Removing a number from a Java RNG's range?

I'm currently using Java's RNG Random r = new Random() , and having it generate a new integer between 0 and 5 in a while loop.

while (someBoolean == false) {
        int i = r.nextInt(6);
           ....
 }

What I would like to do, is to remove a number from the range (for instance, 4) so that the RNG still generates a new number between 0 and 5, excluding one of the values.

My current best bet is the following:

 while (someBoolean == false) {
        int i = r.nextInt(6);
           if (i == removedInt) { continue; }
          ....
 }

However I'm worried this could cause long runs in my code where the RNG is constantly returning a number that I don't want.

[ For clarity; the number that is being returned is a column in a Connect4 grid, or 2D int array. The method is randomly placing moves in columns until a column fills up, at which point I no longer want to be able to play in that column. ]

Any help appreciated :)

Although you could use a List enumerating the numbers you want to generate and exclude/remove the one you want to exclude, this is only efficient for small ranges. If you want to generate a random number in a large range, this solution becomes quite inefficient and unfeasable .

Solution using only 1 Random.nextInt() call

If you want to generate random numbers in the range of 0..5 both inclusive, you can do that with r.nextInt(6) .

If you want to exlude a number, eg 4 , that means the range is smaller by 1, so use r.nextInt(5) and if the result is the excluded number, then return the max allowed which is 5 (because it will never be generated because you used max - 1).

It looks like this:

// Returns a random number in the range 0..5 (0 and 5 included), 4 excluded
public int nextRand() {
    int i = r.nextInt(5);
    return i == 4 ? 5 : i;
}

General solution

Here is a general solution which takes the min , max and the excludable numbers as parameters:

/**
 * Returns a random number in the range min..max both included, but never the excluded.
 */
public int nextRand(int min, int max, int excluded) {
    if (max <= min || excluded < min || excluded > max)
        throw new IllegalArgumentException(
                "Must be: min <= excluded <= max AND min < max");

    int i = min + r.nextInt(max - min);  // r is a java.util.Random instance
    return i == excluded ? max : i;
}

So for example if you call nextRand(0, 5, 3) , it will only return a random number being one of 0, 1, 2, 4, 5 .

ArrayList<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
myInts.add(3);
myInts.add(4);
int i = myInts.get(r.nextInt(myInts.size())); // 1,2,3,4
myInts.remove(3);
int j = myInts.get(r.nextInt(myInts.size())); // 1,2,4

The code selects random entry from allowed list of integers.

PS Please note, that if you have a big range of numbers, then creating an ArrayList of thousands upon thousands of integers might not be the best idea.

Hope this helps you with your situation. It isn't really excluding any numbers but instead making a decision on what spot to make a move in based off an initial value given to it from a random number.

    //Change these to what you want
    int numberColumns = 7;
    int numberRows = 6;
    /**
    * A way for my snippet to determine what column has a piece in its very top spot.
    */
    boolean[][] hasPiece = new boolean[numberRows][numberColumns];
    /**
    * Get a random number between 0 and numberColumns - 1 and set it equal 
    * to the column you want the computer to place it in.
    */
    int columnToPlace = (int) (0 +Math.random()*numberColumns);
    /**
    * A boolean check to determine what side of the chosen column to check first.
    * This way it doesn't always check to the left or right first.
    */
    if(Math.random() < 0.5){
        //Forwards for loop to start at the initial random number given.
        for(int runner = columnToPlace; runner < numberColumns; runner++){
            if(!hasPiece[0][runner]){/**Check First Row And Next Column To The Right For A Piece.*/
                columnToPlace = runner;/**If there is no piece their set it equal to that number/empty column.*/
                break;/**Break out of the loop*/
            }
        }
    }else{
        //Reversed for loop to start at the initial random number given.
        for(int backwardsRunner = columnToPlace; backwardsRunner > 0; backwardsRunner--){
            if(!hasPiece[0][backwardsRunner]){/**Check First Row And Next Column To The Left For A Piece.*/
                columnToPlace = backwardsRunner;/**If there is no piece their set it equal to that number/empty column.*/
                break;/**Break out of the loop*/
            }
        }
    }
    //The final number/empty column the computer will make it's move in.
    System.out.println(columnToPlace);

Using this method you could probably get a decent AI that doesn't just make random moves. It's a good place to start notice the "runner" loops. If you have a way to check if it's a computers piece to the left or right of the random column you could add logic that would make the wiser decision.

Your brain is fixed on the idea that the numbers out of random() have to be the same numbers that you actually use for rows or whatever. Separate the mere ordinal from what it represents by using small array:

Vector validColumns = new Vector(6);
for (int i = 0; i < 6; i += 1) { validColumns.add(i); }
. . .
c = validColumns[r.nextInt(validColumns.size())]

In the ... you remove and add columns as needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM