1

This is how i am generating a unique no in between 1 to 6 and getting appropriate images from the drawable folder.

Random rand = new Random();
// n = the number of images, that start at idx 1
rndInt = rand.nextInt(6) + 1; 
String imgName = "card" + rndInt;
int id = getResources().getIdentifier(imgName, "drawable", getPackageName());
imgView.setImageResource(id);

What i want is, I have to call this method 7 times, and each time this method should return a unique random no. so that none of the already chosen numbers will come up again.

Janusz
  • 187,060
  • 113
  • 301
  • 369
Shishir.bobby
  • 10,994
  • 21
  • 71
  • 100
  • 1
    If there are only 6 possible values, how are you going to get 7 unique numbers? – Dan Dyer Aug 03 '10 at 11:14
  • 10
    "Unique" and "Random" are mutually exclusive. You can't have both. – Greg D Aug 03 '10 at 11:18
  • @Dan dyer sry my bad. i changed it to 7. @greg D.. dude, plz c the accepted answr..that code is creating a unique and a ransom no. – Shishir.bobby Aug 03 '10 at 12:05
  • 2
    @Greg D I guess he wants a random permutation of the numbers 1 to N – Lasse Espeholt Aug 03 '10 at 12:12
  • 3
    You're not looking for a unique random number, you're looking for the numbers 1 to 6 in a random order. – Douglas Aug 03 '10 at 12:19
  • He's looking for random numbers drawn from a hypergeometric distribution. http://en.wikipedia.org/wiki/Hypergeometric_distribution unique and random are not mutually exclusive – emory Aug 03 '10 at 12:23
  • @lassesspeholt has it, I think, in which case a classic Fisher-Yates shuffle or one of its derivatives ought to be sufficient. – Greg D Aug 03 '10 at 12:37
  • @emory: How do you draw numbers from a probability distribution? – Douglas Aug 03 '10 at 13:12

7 Answers7

38

The usual approach to this kind of problem is to create a list containing each of the possible values and shuffle it (use Collections.shuffle). You then consume one item from the list each time you need a value. This will ensure that you don't use the same value more than once but still allows for a random order.

Dan Dyer
  • 53,737
  • 19
  • 129
  • 165
  • It looks like you're using this to shuffle cards. Wouldn't it be better to load all of the cards, and then shuffle them? The approach is similar to this answer. – Erick Robertson Aug 03 '10 at 12:03
  • 3
    +1 Much better than the accepted answer. I wonder if yours had been accepted if it had code. – MAK Aug 03 '10 at 12:26
8

Here is an example class which creates a random permutation using the approach Dan Dyer suggested. It ensures that each .next() calls gives a new number up to the number given in the constructor. After that it wraps around and gives the same sequence again. This can be useful for shuffling a playlist.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class RandomPermutation{
    private List<Integer> list;
    private int index;

    /**
     * Create a random permutation the sequence of numbers 0, 1, ... , n - 1.
     * @param n Range specifier, must be positive
     */
    public RandomPermutation(int n) {
        if (n <= 1) {
            throw new IllegalArgumentException(
                    "Positive number expected, got: " + n);
        }
        list = new ArrayList<Integer>();
        newList(n);
    }

    /**
     * Creates a new list
     */
    public void newList(int n) {
        index = -1;
        list.clear();
        for (int i = 0; i < n; i++) {
            list.add(i);
        }
        Collections.shuffle(list);
    }

    /**
     * Retrieve the next integer in sequence. 
     */
    public int next() {
        index = (index + 1) % list.size();
        return list.get(index);
    }
}

Btw. do not use the approach Snake used. It is not only because it will freeze once all numbers have been used. That can be fixed. The problem is more that the procedure runs slower and slower as more and more numbers are in the listIdontWantAnymore. With just 6 numbers it's not a problem, but it can cause quite a significant slowdown if the range is large. Consider choosing between 10000 numbers. After 9900 numbers have been chosen there is a 1% chance of hitting a good number. after 9990 numbers there is a 0.1% chance of hitting a good number and etc.

Here is an example of how you can use the class:

static RandomPermutation randomPerm = new RandomPermutation(7)

int NextRandomNumber() {
    return randomPerm.next() + 1;
}
Zeriab
  • 128
  • 4
  • thanks zeriab, i implemeneted ur approach, and its working f9. can u plz tell me one more thing, like u must hv seen a timer, countdown timer, showing values from 10 to 0 , visible on view. how can i show random number keeps on changing on my view. thanks again mate – Shishir.bobby Aug 06 '10 at 11:33
2

Here's the easiest code to do it and store into an array without any repetition:

Random rand = new Random();
int[] ar;
ar = new int[5];

int random = 0;
int [] result_arr=new int[5];
boolean flag=false;

for(int i=0;i<5;i++)
{  
    ar[i]=0;

    do{
        random =rand.nextInt(10);
        flag=false;
        for(int j=0;j<i;j++)
        {
            if(ar[j]==random)
            flag=true;
        }
        if(!flag)
        {
            ar[i]=random;
            break;
        }
     }
     while(true) ;
}

this will create unique numbers in the array

Bohemian
  • 412,405
  • 93
  • 575
  • 722
arslan haktic
  • 4,348
  • 4
  • 22
  • 35
1

For your particular use-case, this should do the trick.

Random rand = new Random();
// n = the number of images
List<String> imgNames = new ArrayList<String>(n);
for (int i = 0; i < n; i++) { 
    imgNames.add("card" + (i + 1)) 
}
while (!imageNames.isEmpty()) {
    String imgName = imgNames.remove(rand.next(imageNames.size());
    int id = getResources().getIdentifier(imgName, "drawable", getPackageName());
    imgView.setImageResource(id);
}

Beware that this does not scale well as n gets large. The remove operation is O(n) for an ArrayList or a LinkedList. But for n in the hundreds or thousands, this is probably insignificant compared with loading and displaying the images.

Also, as the comments noted "unique random numbers" is a contradiction in terms. What you are after is a random permutation of the set of numbers from 1 to n. My solution gives you this without an explicit "shuffling" step, and that's sufficient for your use-case.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • I would say it's quite clear that "unique random numbers" means that the same sample should not be picked more than once. On a side-note: The removeFirst() operation on a LinkedList is O(1). – Zeriab Aug 03 '10 at 13:00
  • @Zeriab - *"The removeFirst() operation on a LinkedList is O(1)"*. How is that relevant? The algorithm doesn't use that method. – Stephen C Aug 03 '10 at 13:35
  • @Zeriab - "unique random number" might be clear to you, but it is nonsense from a mathematical perspective. Sort of like talking about sets that allow duplicates. – Stephen C Aug 03 '10 at 13:39
  • A number that is unique (and) random. Unique in respect to other calls. Random in that the number should be picked randomly among 1,2,...,6,7. I do not see why it is nonsense from a mathematical perspective to consider the conjunction of the two constraints. Am I missing some ambiguity? – Zeriab Aug 03 '10 at 14:48
  • You are right that my mention of removeFirst() is irrelevant for your example. You can use a Red-Black tree (TreeSet) whose remove operations runs in O(lg). – Zeriab Aug 03 '10 at 14:51
  • Sets don't have a `remove(int)` method. – Stephen C Aug 03 '10 at 15:09
  • @Zeriab - yes you are missing something. Simply, a real random number does not depend on previous numbers in the sequence. But the uniqueness constraint means that there has to be a dependency; i.e. no number can be repeated. – Stephen C Aug 03 '10 at 15:13
1

Generate a list of numbers containing every number you will use. (Which is fine, given that we are talking about a small range, where "N" is "somewhere less than a thousand")

When you choose a number, select a random index between 0 and sizeof(list), that number becomes the index of that list.

Delete that list index and return the number.

(It is an exercise to the reader to determine what kind of "list" is appropriate here.)

Arafangion
  • 11,517
  • 1
  • 40
  • 72
1

Use a linear congruential generator with appropriately chosen parameters.

tc.
  • 33,468
  • 5
  • 78
  • 96
-4

Create a static list of possibilities you've already gotten.

static ArrayList<int> listIdontWantAnymore = new ArrayList<int>();

int NextRandomNumber() {
    Random random = new Random();
    int myRandomInt;

    do {
        myRandomInt = random.NextInt(6) + 1;
    } while(listIdontWantAnymore.Contains(myRandomInt));

    listIdontWantAnymore.Add(myRandomInt);

    // now your 'myRandomInt' is what you want it to be.
    return myRandomInt;
}
Janusz
  • 187,060
  • 113
  • 301
  • 369
Anemoia
  • 7,928
  • 7
  • 46
  • 71
  • 4
    I think any solution like this should have failsafe-system to avoid nasty deadlocks. It could end bad ;) – monoceres Aug 03 '10 at 11:27
  • That was perfect, man.. dat wat i waz looking for, now i am able to create a unique random number thanks a lot. – Shishir.bobby Aug 03 '10 at 12:04
  • 6
    @shishir.bobby: no, that was not perfect. That was absolutely horrible. Please, please do not use this "solution". – Michael Borgwardt Aug 03 '10 at 12:20
  • 1
    Try running it on... lets say 1000 limit and you will notice that it is slow. After it generates lets say 999 numbers, it will statistically need 1000 more tries to get the last one. That means that the complexity of this algorithm will be O(n^2) - and that's horrible for such a simple task. – bezmax Aug 03 '10 at 12:30
  • 2
    @Max: That assumes a perfect distribution. I fear that it would be much worse than than O(n^2). – Arafangion Aug 03 '10 at 12:40
  • @Arafangion No, it will be exactly n^2 (statistically at least). I've just calculated it. For n-th number you need n tries to get it. So you will need 1+2+3+...n tries to get full set. And that's approximately O(n*n/2) -> O(n^2). – bezmax Aug 03 '10 at 12:49
  • -1 for (hopefully) a joke gone horribly wrong the moment it was accepted. – Greg D Aug 03 '10 at 13:06
  • 1
    @shishir.bobby: Scroll down and choose ANY other answer. – bezmax Aug 03 '10 at 13:27
  • @Max: For the sake of argument... Suppose you have a random number generator that always returns 4. Now, how slow will that be for N=0? (Pretty instantanious, I hope). What about N=1? Pretty dang fast, actually. N=2? You might be waiting for the sun to cool down! The better the random number distribution, the closer to O(n^2) you will get. The more "spiky" the random number generation, the worse it will get. – Arafangion Aug 03 '10 at 13:39
  • @shishir.bobby: The wonder thing about this site is that you can see what we suggest without even asking us! Just check out the answers with the most upvotes. – Greg D Aug 03 '10 at 16:14
  • @Arafangion: Knowing that someone will start arguing about the randomness of random generators, I explicitly specified "*statistically at least*". Did you notice that part? So *statistically* this algorithm is O(n^2), that is true and you cant prove it is wrong. In reality though, it may vary from O(n) to (infinity). – bezmax Aug 04 '10 at 07:00
  • @Max: Sorry, I'd missed that. I'm used to seeing "Amortized" in that context, but even so, using the mathematical definition of 'statistically' is correct. – Arafangion Aug 04 '10 at 12:51
  • @Max: "For n-th number you need n tries to get it." <- Nope. Assuming a perfectly uniform generator, the expected number of tries to get the kth number (1 <= k <= n) from a sample of size n will be n/(n + 1 - k). So the expected total number of random samples needed is O(n log n), which isn't actually too bad. – Mark Dickinson Aug 04 '10 at 12:54
  • @Mark Dickinson: Yeah, you are right. I've always been bad in statistics xD – bezmax Aug 04 '10 at 14:20