简体   繁体   中英

Explanation of card shuffling algorithm (java)

I was trying to better understand in a common Java shuffle deck of cards algorithm, this piece of code:

// Random for remaining positions. 
int r = i + rand.nextInt(52 - i); 

Why is it necessary to "pad" or add i index to the resultant random number? It looks like as you iterate and add i , by subtracting i , you keep the max possible range of random numbers consistent from 0 to 51, but why not just do:

int r = rand.nextInt(52);

Full code:

      
    // Function which shuffle and print the array 
    public static void shuffle(int card[], int n) 
    { 
          
        Random rand = new Random(); 
          
        for (int i = 0; i < n; i++) 
        { 
            // Random for remaining positions. 
            int r = i + rand.nextInt(52 - i); 
              
             //swapping the elements 
             int temp = card[r]; 
             card[r] = card[i]; 
             card[i] = temp; 
               
        } 
    } 

The Fisher–Yates shuffle works as follows:

  • Take a random element from the array, and swap it into first place
  • Take a random element from remaining values, and swap it into second place
  • Take a random element from remaining values, and swap it into third place
  • and so on

It is the " remaining values" part you're asking about.

Eg after 10 iterations, you have swapped 10 random values into the first 10 positions of the array, so for the next iteration you need a random position in range 10-end, hence the offset of 10 from a random range of 10 less than full range, aka i + rand.nextInt(52 - i) .

Other answers haven't addressed your question of " why not just do: int r = rand.nextInt(52); ". This is sometimes referred to as a naive shuffle, and the answer is because that would result in a biased shuffle.

Ideally, you want the number of outcomes to mirror the probability calculations involved for equally likely outcomes. That means the first card in the shuffled deck can be any one of the 52 cards, the second can be any one of the 51 remaining cards, the third can be any of the remaining 50 cards,... In other words, there are A = 52*51*50*...*3*2*1 (ie, 52 factorial) possible arrangements of the cards. This is what the Fisher-Yates shuffle and its variants do. However, if you choose any of the cards to move on the i th iteration as you proposed, C = 52 52 cases are generated. The result is a biased shuffle.

To illustrate how and why the bias arises in a naive shuffle, consider a much smaller deck with 3 cards. In that case, A = 3*2*1 = 6 arrangements, while C = 3 3 = 27 is the number of paths to get to a final arrangement. Why is this a problem? Because of the pigeon hole principle. C is not an integer multiple of A, so if we think of C as pigeons and A as pigeonholes, some of the holes must get more pigeons than others. In shuffling terms, there are more paths to get to some arrangements than others. Consequently, not all arrangements will occur equally often so the result is not a "fair" shuffle.

If you work it out analytically and start from an array initialized to ['a','b','c'] , you'll find the following probabilities when a naive shuffle is used:

outcome         probability
-------         -----------
['a','b','c']       4/27
['a','c','b']       5/27
['b','a','c']       5/27
['b','c','a']       5/27
['c','a','b']       4/27
['c','b','a']       4/27

whereas with an unbiased shuffle the probabilities for each of the 6 possible permutations is 1/6. This is why the Fisher-Yates algorithm was created.

What your code does is create a shuffle of the deck. In each iteration it takes a random card and puts it back in the front of the deck, from i=0 to i=n.

If you would've used int r = rand.nextInt(52); it means that in each iteration you can get any card back, even those at the beginning of the deck which are already part of the new order of the deck.

By subtracting i you choose a card only of the remaining 52-i cards left, and then you need to add +i in order to get it's actual position, because you already set the first i cards at the beginning of the deck as the new shuffle.

For example let's say you already got the first 10 cards in their new place. Now you need to get the 11th card so you take a random card from the 52-10=42 cards left. Let's say we got back the number 5, it's not the card at card[4] but the card at card[10+4]

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