Tuesday, April 8, 2014

Non repeating random numbers in Java (Android)

Recently on a app I was working on, I had to select an element from either of two lists randomly. I tried using the normal new Random().nextInt( arrayLength - 1 ); but id didn't work on short lists.

Say a given length of 4, I'm more interested in getting an output like 4, 1, 3, 2, 1, 4, 2, 3, 4 but instead what I got was 1, 1, 1, 1, 2, 3, 2, 2, 4, 3, 2, 1, 1.

So as usual, SO to the rescue. How can I generate a random number within a range but exclude some?

This code, can do magic.

public int getRandomWithExclusion(Random rnd, int start, int end, int... exclude) {
    int random = start + rnd.nextInt(end - start + 1 - exclude.length);
    for (int ex : exclude) {
        if (random < ex) {
            break;
        }
        random++;
    }
    return random;
}

I did a few changes to better suit to my needs and following is the code which I'm using to access this method. There I have 2 variables to store the previous index from either of the lists.

int _prevArrayId = 0, _prevDBId = 0;

public void doSomething(){
    boolean arrayOrDB = rand.nextInt() % 10 >= 5;

    int id;

    if (arrayOrDB) {
        int len = Utils.SomeArray.length;

        // Array indices start with 0 and ends with (length - 1)
        id = Utils.getRandomWithExclusion(rand, 0, len, len,_prevArrayId);

        _prevArrayId = id;

        // Do stuff!
    } else { 

        // Database table rows start with 1 and ends in MaxID (count)
        id = Utils.getRandomWithExclusion(rand, 1, count,_prevDBId);

        _prevDBId = id;

        SomeItemFromDB item = ContentProviderHelpers.getItem(contentResolver, id);
        // Do stuff!
    }
}