开发者

Random card generation

I need to randomly generate three cards from array i have the array of 52 cards names from card1 to card52

String rank[]=new Stri开发者_StackOverflow中文版ng[52];
for(int i=0;i<rank.length;i++)
{
rank[i]= "card"+i;
}

Now i need to select three cards from the array and it should not be repetable.

can anybody help me. actually i m doing this bt cards are repeting. pls provide me the sollution.

Thanks in Advance.


You can try Collections.shuffle(List list) method:

String rank[] = new String[52];
for (int i = 0; i < rank.length; i++) {
  rank[i] = "card" + i;
}
List<String> cards = Arrays.asList(rank);
Collections.shuffle(cards);
List<String> selectedCards = cards.subList(0, 3);


If you convert your array to a List you can use Collections.shuffle() to randomise it. Then if you just take the first three entries of the list you'll get three random cards with no repeats.

List<String> ranklist = Arrays.asList(rank);
Collections.shuffle(ranklist);
String rank1 = ranklist.get(0);
String rank2 = ranklist.get(1);
String rank3 = ranklist.get(2);


        Random random = new Random();
        List<Integer> randomList = new ArrayList<Integer>();
        int randomInt;

        do {
            randomInt = random.nextInt(52);
            if (randomList.contains(new Integer(randomInt)))
                continue;
            randomList.add(randomInt);
        } while (randomList.size() < 3);

randomList will contain the index of the three card


Something that might be faster than shuffling the whole array:

java.util.Random r = new java.util.Random();
int c1 = r.nextInt(52),
  c2 = r.nextInt(51),
  c3 = r.nextInt(50);
if(c2 >= c1) c2++;
if(c3 >= Math.min(c1, c2)) c3++;
if(c3 >= Math.max(c1, c2)) c3++;
System.out.println("Card 1: " + rank[c1]);
System.out.println("Card 2: " + rank[c2]);
System.out.println("Card 3: " + rank[c3]);

In fact, some timing tests I did using a generalised (slower) form of this algorithm vs Collections.shuffle (screen shot here) show that this method is about 3.7 times faster for 3 cards, and in practice faster for choosing up to 24 random cards.

To explain the algorithm to those doubting:

Probabilities

We pick random numbers - first, one of 52. If we were to remove that card then pick another, that would be one of 51...so we're ok with the random number range of the indexes we choose.

If we were picking and removing from an array, then we just take out item at index 1, then take out item at index 2, and the same for index 3.

What I'm doing above is adjusting the indexes to have the same effect as taking the items out of the array.

For index two, we have a number that could be any number from 0 to 50. But, lets pretend we took out the card at index 6...

This means that instead of a random number spread over the range 0 to 50, we now want one equally spread over the range 0-5, and 7-51. That is still a total of 51 possibilities. We do that by adding 1 to our second index, if it is >= 6. Now we have a number over the correct range with the correct distribution and equal probability if hitting any of the allowed indexes.

The same slightly more complex logic goes for the third card: there are two spots 'missing' in the deck - so we adjust the third index to cover the available ranges. First, if it's equal to or above the first missing position, shift it up. Then if it's equal to or above the second, shift it up again. This does not bias the selection toward higher values - all we are doing is avoiding the indexes that have already been used.

There is no skew in the probabilities.

Efficiency

It was mentioned in comments, that this algorithm and the one using Collections.shuffle have the same complexity (i.e., O(n)), and this method is slightly less readable. The first point to make is that this algorithm is more complex; i.e. to shuffle the whole array it is O(n^2). For low n, this algorithm is more efficient. And, I think the difference in efficiency warrants the sacrifice in readability. Compare:

Collections.shuffle

  1. pick random number
  2. remove item from list
  3. insert item into list
  4. repeat 51 more times

This method

  1. pick random number
  2. pick random number
  3. pick random number
  4. Up to 3 increments

This ignores the translation from an array to a Container necessary when using the first method. The inevitable conclusion being that this method is significantly more efficient.

BTW - you can visualise the O(n^2) nature when you think about picking 4 cards - which requires up to 6 increments...5 cards, up to 10...6 cards, up 15...for n, (n^2 - n)/2

Generalising

In this exaple I've used Math.min and Math.max as a simple way to sort a list of 2 items. In the generic case (i.e. selecting 'n' [1-52 inclusive] non-repeating random cards), we need to sort a list of up to 52 items. This can be done in O(n) worst case using an insertion sort, by keeping an ordered list of selected indexes. Selecting a new random index is done by

  1. Select a new random index, using Random.nextInt(52 - selectedIndexListSize)
  2. Iterate over the sorted list, at each node incrementing our selected index if it is >= node value, stopping if it is less
  3. output card at the selected index in the array
  4. insert our selected index into the sorted list, at the point where we stopped

It's O(n) for each selection - but given up to 52 selections, that makes it O(n^2).

In terms of efficiency however, this algorithm is more efficient whenever m^2 < n, (or to be more precise, whenever m + (m^2-m)/2 < n) where n is 52 and m is the number of cards chosen. So this method is more efficient for picking 7 or less cards (or using the more precise calc, 9 or less), and therefore and obviously, more efficient for picking 3.


Have an array (or even a string) and add to it whenever you select a card. That way whenever you select the second or third card you can select another one if it is in the array (or string).

Here is one way to do it... I didn't check if it works.

String pickedCards = ""
int totalPickedCards = 0
boolean continue = true;
for(continue) 
{
  tempVariable = pickedCard
  if (pickedCard.contains("/"+tempVariable+"/") == false) { // if you didn't already pick it then
    pickedCards = pickedCards + "/" + tempVariable + "/";
    // save the card you picked - stored in tempVariable
    ++totalPickedCards
  }
  if (totalPickedCards >= 3) { // exit loop if you have 3 cards picked
    continue = false;
  }
}


Basically there are two mathematically valid ways:

  1. As others have suggested, shuffle the entire array, then pick sequentially from the beginning.

  2. Pick the first one at random. Then pick the second one at random. If the second is the same as the first, pick at random again. i.e. put the pick into a loop that keeps trying until it gets a value that hasn't been chosen before. For the third you have to check against the first two, etc. You may want to create a set of flags parallel to the list of cards to indicated which have been chosen, or maybe you can change a card to null after it's chosen, etc.

#1 is more efficient if you're going to use a large percentage of the cards. #2 is more efficient if you're going to use a small percentage of the cards.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜