I believe the only thing it does is shuffle your deck twice and try to give you the better starting hand of the two.
I believe the only thing it does is shuffle your deck twice and try to give you the better starting hand of the two.
That said people do sometimes unintentionally write something like this (terrible and also buggy) algo
for idx in size(deck):
new_spot = int(random() * size(deck))
swap(deck[idx], deck[new_spot])
...which won't give an even distribution of all possible outcomes. If shuffling, look up a real algo (like Fisher-Yates for instance) and implement that. Or better yet, use a library implementation. -- To shuffle an array a of n elements (indices 0..n-1):
for i from 0 to n−2 do
j ← random integer such that i ≤ j < n
exchange a[i] and a[j]Nerd sidenote: Because generating random numbers tends to be expensive, I had an idea once where you generate the full permutation with only a single random sample. Basically you could use something which fully enumerates a space like Gray codes[1] (you want one with a closed form solution which can arbitrarily you the nth code) then you generate one random number, multiply by the number of combinations, look up that code and decode that into the sequence of cards. I never actually got around to implementing it, but I suspect if your code lookup was fast it would be much faster than Fisher-Yates.
The use case was if you wanted to make a Monte Carlo algorithm to evaluate strategies in a card game for instance.
Might not work - as I say I never actually got around to implementing it.
[1] https://en.wikipedia.org/wiki/Factorial_number_system [2] https://en.wikipedia.org/wiki/Lehmer_code
Also, you’ll have to make sure you pick each permutation is picked with equal probability. random() % factorial(52) likely won’t work because your RNG won’t return integers in a range k × 52! for some k > 1 (https://stackoverflow.com/questions/10984974/why-do-people-s...)
You can think of it as rolling one random number up to n!, then use x%n to pick the first element and then x/n to pick the next (n-1) elements (so the next step is then to pick the second element with (x/n)%(n-1) and use (x/n)/(n-1) for the remaining (n-2) elements).
Though as a sibling comment points out, since you need a random number up to n!, even 64 bits of randomness only get you so far (I think up to 20, but didn't check my math).
for i in [0..n-1] swap(a[i], a[rand in [i..n-1])
where rand in [I..n-1] coud be implemented as i + (rand() % (n-i))
essentially start with your finger at 0, pick a card from the unpicked set which is everything on and after your finger, swap that card with your finger, move your finger up one card
Fisher-Yates is pretty obvious but weirdly subtle in some ways. For someone not paying attention, it would be easy to get wrong, as the grandparent response points out.
If using a library implementation, you do still need to validate that it's using a proper algorithm. And that it doesn't change to something else during an update. Which probably requires you to understand the algorithm, and looking at wikipedia, it's a handful of lines of code to do it right and know that it doesn't change because it's your code. Maybe not worth the time to inspect and reinspect from a library.
Is there a name for this approach/any info on why it doesn't work?
Base case: n=1. Obvious.
Inductive step: n>1. The nth element swaps to itself w/ prob 1/n, and to any other element w/ prob 1/n. By induction the other elements have been uniformly shuffled. Any of those elements is swapped to the nth place w/ prob 1/n.
This proves that
for i in range(min_key,max_key):
swap(arr, i, choice(i, max_key))
works as a shuffle, by growing a random sub-array.*The first algorithm was
for i in range(min_key,max_key):
swap(arr, i, choice(min_key, max_key))
which has different biases.* I am not actually sure that this actually works.
Step 1. Swap a, c -> [c,b,a]
Step 2. Swap b, c -> [b,c,a]
Step 3. Swap a, a -> [b,c,a]
What is the probability that we do exactly these steps? At each step, we have 3 choices, so (1/3) * (1/3) * (1/3) = 1/27. What is the probability that we end up with [b,c,a] ? You might think 1/27 as well, but that is not quite true – it is possible, that we choose different steps, but end up with the same result. For example, we can do [a,b,c]->[b,a,c]->[b,c,a]->[b,c,a]. But the probability will always be a multiple of 1/27 – it is just 1/27 times the number of possible paths that leads to [b,c,a].Now, what should the probability be? There are exactly 6 ways to shuffle [a,b,c] (this is the number of permutations, 3! = 3 * 2 * 1 = 6). So we want to get [b,c,a] with a probability of 1/6. But 1/6 is not a multiple of 1/27 ! (You can see that by looking at the equation 1/6 = x/27, which is the same as x = 27 / 6 = 4.5 .)
The same argument works for any length n > 2, as n*n is not divisible by n-1, but n! is.
There's probably a bunch of other examples of this kind, (easy out of place, tricky in place) but non come to mind atm.
That hasn't stopped the rumors.
So it's quite possible that the online Magic game manipulates the deck. Keeps players engaged more so they make more money. Pretty standard these days; Candy Crush famously does this too. Looks random, but very much isn't.
The game mitigates this with a mulligan rule, where you can get another hand with one less card if you want. That way, you can get rid of a bad hand, but it costs a card each time. Commonly, magic players overvalue the card, and don't mulligan hands that they should, but it is also possible (and common) to just lose games to luck even if you do everything right - top MTG pros only have ~65-70% win rates against random people.
MTG arena apparently also does an internal free mulligan for you (to make the game more fun) - it draws 2 hands initially and gives you the one with the better mix of cards.