why does this simple shuffle algorithm produce biased results? what is a simple reason?

前端 未结 12 1442
旧时难觅i
旧时难觅i 2020-11-27 03:17

it seems that this simple shuffle algorithm will produce biased results:

# suppose $arr is filled with 1 to 52

for ($i < 0; $i < 52; $i++) { 
  $j = r         


        
相关标签:
12条回答
  • 2020-11-27 03:35

    Here's the complete probability tree for these replacements.

    Let's assume that you start with the sequence 123, and then we'll enumerate all the various ways to produce random results with the code in question.

    123
     +- 123          - swap 1 and 1 (these are positions,
     |   +- 213      - swap 2 and 1  not numbers)
     |   |   +- 312  - swap 3 and 1
     |   |   +- 231  - swap 3 and 2
     |   |   +- 213  - swap 3 and 3
     |   +- 123      - swap 2 and 2
     |   |   +- 321  - swap 3 and 1
     |   |   +- 132  - swap 3 and 2
     |   |   +- 123  - swap 3 and 3
     |   +- 132      - swap 2 and 3
     |       +- 231  - swap 3 and 1
     |       +- 123  - swap 3 and 2
     |       +- 132  - swap 3 and 3
     +- 213          - swap 1 and 2
     |   +- 123      - swap 2 and 1
     |   |   +- 321  - swap 3 and 1
     |   |   +- 132  - swap 3 and 2
     |   |   +- 123  - swap 3 and 3
     |   +- 213      - swap 2 and 2
     |   |   +- 312  - swap 3 and 1
     |   |   +- 231  - swap 3 and 2
     |   |   +- 213  - swap 3 and 3
     |   +- 231      - swap 2 and 3
     |       +- 132  - swap 3 and 1
     |       +- 213  - swap 3 and 2
     |       +- 231  - swap 3 and 3
     +- 321          - swap 1 and 3
         +- 231      - swap 2 and 1
         |   +- 132  - swap 3 and 1
         |   +- 213  - swap 3 and 2
         |   +- 231  - swap 3 and 3
         +- 321      - swap 2 and 2
         |   +- 123  - swap 3 and 1
         |   +- 312  - swap 3 and 2
         |   +- 321  - swap 3 and 3
         +- 312      - swap 2 and 3
             +- 213  - swap 3 and 1
             +- 321  - swap 3 and 2
             +- 312  - swap 3 and 3
    

    Now, the fourth column of numbers, the one before the swap information, contains the final outcome, with 27 possible outcomes.

    Let's count how many times each pattern occurs:

    123 - 4 times
    132 - 5 times
    213 - 5 times
    231 - 5 times
    312 - 4 times
    321 - 4 times
    =============
         27 times total
    

    If you run the code that swaps at random for an infinite number of times, the patterns 132, 213 and 231 will occur more often than the patterns 123, 312, and 321, simply because the way the code swaps makes that more likely to occur.

    Now, of course, you can say that if you run the code 30 times (27 + 3), you could end up with all the patterns occuring 5 times, but when dealing with statistics you have to look at the long term trend.

    Here's C# code that explores the randomness for one of each possible pattern:

    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<String, Int32> occurances = new Dictionary<String, Int32>
            {
                { "123", 0 },
                { "132", 0 },
                { "213", 0 },
                { "231", 0 },
                { "312", 0 },
                { "321", 0 }
            };
    
            Char[] digits = new[] { '1', '2', '3' };
            Func<Char[], Int32, Int32, Char[]> swap = delegate(Char[] input, Int32 pos1, Int32 pos2)
            {
                Char[] result = new Char[] { input[0], input[1], input[2] };
                Char temp = result[pos1];
                result[pos1] = result[pos2];
                result[pos2] = temp;
                return result;
            };
    
            for (Int32 index1 = 0; index1 < 3; index1++)
            {
                Char[] level1 = swap(digits, 0, index1);
                for (Int32 index2 = 0; index2 < 3; index2++)
                {
                    Char[] level2 = swap(level1, 1, index2);
                    for (Int32 index3 = 0; index3 < 3; index3++)
                    {
                        Char[] level3 = swap(level2, 2, index3);
                        String output = new String(level3);
                        occurances[output]++;
                    }
                }
            }
    
            foreach (var kvp in occurances)
            {
                Console.Out.WriteLine(kvp.Key + ": " + kvp.Value);
            }
        }
    }
    

    This outputs:

    123: 4
    132: 5
    213: 5
    231: 5
    312: 4
    321: 4
    

    So while this answer does in fact count, it's not a purely mathematical answer, you just have to evaluate all possible ways the random function can go, and look at the final outputs.

    0 讨论(0)
  • 2020-11-27 03:35

    See the Coding Horror post The Danger of Naïveté.

    Basically (suposing 3 cards):

    The naive shuffle results in 33 (27) possible deck combinations. That's odd, because the mathematics tell us that there are really only 3! or 6 possible combinations of a 3 card deck. In the KFY shuffle, we start with an initial order, swap from the third position with any of the three cards, then swap again from the second position with the remaining two cards.

    0 讨论(0)
  • 2020-11-27 03:37

    The Naive algorithm picks the values of n like so:

    n = rand(3)

    n = rand(3)

    n = rand(3)

    3^3 possible combinations of n

    1,1,1, 1,1,2....3,3,2 3,3,3 (27 combinations) lassevk's answer shows the distribution among the cards of these combinations.

    the better algorithm does:

    n = rand(3)

    n = rand(2)

    n! possible combinations of n

    1,1, 1,2, 2,1 2,2 3,1 3,2 (6 combinations, all of them giving a different result)

    As mentioned in the other answers, if you take 27 attempts to get 6 results, you cannot possibly attain the 6 results with even distribution, since 27 is not divisible by 6. Put 27 marbles into 6 buckets and no matter what you do, some buckets will have more marbles than others, the best you can do is 4,4,4,5,5,5 marbles for buckets 1 through 6.

    the fundamental problem with the naive shuffle is that swaps too many times, to shuffle 3 cards completely, you need only do 2 swaps, and the second swap need only be among the first two cards, since the 3rd card already had a 1/3 chance of being swapped. to continue to swap cards will impart more chances that a given card will be swapped, and these chances will only even out to 1/3, 1/3, 1/3 if your total swap combinations is divisible by 6.

    0 讨论(0)
  • 2020-11-27 03:38

    Here's a great analysis of a card shuffling Markov chains. Oh wait, that's all math. Sorry. :)

    0 讨论(0)
  • 2020-11-27 03:46

    Not that another answer is needed, but I found it worthwhile to try to work out exactly why Fisher-Yates is uniform.

    If we are talking about a deck with N items, then this question is: how can we show that

    Pr(Item i ends up in slot j) = 1/N?
    

    Breaking it down with conditional probabilities, Pr(item i ends up at slot j) is equal to

    Pr(item i ends up at slot j | item i was not chosen in the first j-1 draws)
    * Pr(item i was not chosen in the first j-1 draws).
    

    and from there it expands recursively back to the first draw.

    Now, the probability that element i was not drawn on the first draw is N-1 / N. And the probability that it was not drawn on the second draw conditional on the fact that it was not drawn on the first draw is N-2 / N-1 and so on.

    So, we get for the probability that element i was not drawn in the first j-1 draws:

    (N-1 / N) * (N-2 / N-1) * ... * (N-j / N-j+1)
    

    and of course we know that the probability that it is drawn at round j conditional on not having been drawn earlier is just 1 / N-j.

    Notice that in the first term, the numerators all cancel the subsequent denominators (i.e. N-1 cancels, N-2 cancels, all the way to N-j+1 cancels, leaving just N-j / N).

    So the overall probability of element i appearing in slot j is:

    [(N-1 / N) * (N-2 / N-1) * ... * (N-j / N-j+1)] * (1 / N-j)
    = 1/N
    

    as expected.

    To get more general about the "simple shuffle", the particular property that it is lacking is called exchangeability. Because of the "path dependence" of the way the shuffle is created (i.e. which of the 27 paths is followed to create the output), you are not able to treat the different component-wise random variables as though they can appear in any order. In fact, this is perhaps the motivating example for why exchangeability matters in random sampling.

    0 讨论(0)
  • 2020-11-27 03:48

    See this:
    The Danger of Naïveté (Coding Horror)

    Let's look at your three card deck as an example. Using a 3 card deck, there are only 6 possible orders for the deck after a shuffle: 123, 132, 213, 231, 312, 321.

    With your 1st algorithm there are 27 possible paths (outcomes) for the code, depending on the results of the rand() function at different points. Each of these outcomes are equally likely (unbiased). Each of these outcomes will map to the same single result from the list of 6 possible "real" shuffle results above. We now have 27 items and 6 buckets to put them in. Since 27 is not evenly divisible by 6, some of those 6 combinations must be over-represented.

    With the 2nd algorithm there are 6 possible outcomes that map exactly to the 6 possible "real" shuffle results, and they should all be represented equally over time.

    This is important because the buckets that are over-represented in the first algorithm are not random. The buckets selected for the bias are repeatable and predictable. So if you're building an online poker game and use the 1st algorithm a hacker could figure out you used the naive sort and from that work out that certain deck arrangements are much more likely to occur than others. Then they can place bets accordingly. They'll lose some, but they'll win much more than they lose and quickly put you out of business.

    0 讨论(0)
提交回复
热议问题