Generally speaking, a shuffling mechanism has two components:

1.       Shuffling algorithm.

2.       Pseudo-random number generator (PRNG).

While the shuffling algorithm can have a certain bias of its own, the whole shuffling mechanism cannot have any less bias than the PRNG itself has. In other words, a shuffling algorithm cannot be any more random than its PRNG. Let’s look at one of most efficient shuffling algorithms out there, the Fisher–Yates shuffle.

The source code (or, rather, the pseudocode) of the modern Fisher-Yates shuffle has been publicly available since 1964, with implementations in tens of languages.

To shuffle an array a of n elements (indices 0..n-1):

  for i from n − 1 downto 1 do

       j ← random integer with 0 ≤ j ≤ i

       exchange a[j] and a[i]

However, it still remains one of the most widely used algorithms for this purpose.

If you look closely, the algorithm depends on random integer. This is where the PRNG comes into play. If your PRNG is unbiased and your implementation is correct, then your shuffles should be unbiased as well.

Plenty of good PRNGs which are well-vetted, time-tested, and proven to be unbiased are available. Since a PRNG is deterministic (for the same initial state, it always returns the same value) the security/randomness of the whole shuffling mechanism relies on the randomness of the PRNG’s initial state, its seed. This reliance on the quality of the seed is an extension of Kerckhoffs’ Principle, since without knowing the seed knowledge of the algorithm tells you nothing. Luckily, most good PRNGs utilize good “randomness sources” provided by the operating system, such as /dev/urandom.