¿Por qué la agrupación aleatoria al iterar sobre ella y cambiarla por un elemento aleatorio entre 0 y el último elemento de la matriz no produce una barajadura distribuida uniformemente?

Estoy escribiendo este contenido de esta publicación de blog que compara dos algoritmos de barajado diferentes para ver cuál es imparcial.

¿Qué es un algoritmo aleatorio aleatorio?

Considere una matriz con elementos distintos A [1… n]

Un algoritmo aleatorio perfectamente imparcial barajaría aleatoriamente todos los elementos de la matriz de manera que después de barajar:

1.La probabilidad de que la operación de barajado produzca una permutación particular de la matriz original es la misma para todas las permutaciones (es decir, ¡ya que hay n! permutaciones, la probabilidad de que la operación aleatoria produzca una permutación particular es 1 / n!

2.Para cualquier elemento e en la matriz y para cualquier posición j (1 <= j <= n), la probabilidad de que el elemento termine en la posición A [j] es 1 / n

Considere dos algoritmos de barajado

SHUFFLE 1

barajar (A [1… n]) {

para i = 1 a n {

// Encuentra un entero aleatorio entre 1 yn inclusive

int rand = ALEATORIO (1, n);

intercambie A [i] con A [rand];

}

}

SHUFFLE 2

barajar (A [1… n]) {

para i = 1 a n {

// Encuentra un número entero aleatorio entre i y n inclusive

int rand = RANDOM (i, n);

intercambie A [i] con A [rand];

}

}

¿Cómo se comparan estos dos algoritmos de barajado? Simulemos y descubramos.

Simulación de Shuffle 1

A continuación se muestra una simulación de Shuffle 2 con una matriz de.

Conteo de permutaciones:

{1,2,3} – cuenta 4

{1,3,2} – cuenta 5

{2,1,3} – cuenta 5

{2,3,1} – cuenta 5

{3,1,2} – cuenta 4

{3,2,1} – cuenta 4

Conclusión: Shuffle 1 está sesgado

Simulación de Shuffle 2

A continuación se muestra una simulación de Shuffle 2 con una matriz {1,2,3}.

Conteo de permutaciones:

{1,2,3} – cuenta 1

{1,3,2} – cuenta 1

{2,1,3} – cuenta 1

{2,3,1} – cuenta 1

{3,1,2} – cuenta 1

{3,2,1} – cuenta 1

Conclusión: Shuffle 2 es un algoritmo de barajado perfecto imparcial

¿Podemos demostrar que Shuffle 2 producirá un shuffle imparcial en todos los casos?

Para cualquier elemento e, la probabilidad de que se baraje en la primera posición

= probabilidad de que se seleccione para intercambiar cuando i = 1

= 1 / n

Para cualquier elemento e, la probabilidad de que se baraje en la segunda posición

= probabilidad de que NO se seleccione para la primera posición x probabilidad de que se seleccione para intercambiar cuando i = 2

= (n-1) / nx 1 / (n-1)

= 1 / n

Para cualquier elemento e, la probabilidad de que se baraje en una posición particular = 1 / n

Espero que esta explicación haya sido útil. Si está interesado, puede practicar resolviendo desafíos de programación o leer preguntas técnicas de entrevistas sobre estructuras de datos y algoritmos en mi blog.

Una de las condiciones para la distribución aleatoria uniformemente distribuida sería que cada elemento de la matriz inicial tenga la misma probabilidad de estar en cualquiera de los índices de la matriz aleatoria final.
El algoritmo que ha escrito no logrará eso, tan pronto como termine la primera iteración, la posición index = 0 ya se ha llenado con elementos de matriz inicial de probabilidad uniforme wrt. Podríamos dejar ese elemento de índice sin cambios después de eso, pero eso no es lo que terminamos haciendo bajo la regla “… intercambiando con un elemento aleatorio entre o por el último elemento” (un enfoque correcto sería intercambiar un elemento con un elemento aleatorio entre i al último elemento) que daría lugar a una probabilidad no uniforme, aquí están las matemáticas …

Para cada iteración, representemos todos los movimientos posibles del elemento en una matriz de probabilidad, dejemos que la matriz [i] [j] = la probabilidad de transición del elemento en la posición i que se mueve a la posición j.

tome la longitud de array = 3, habría 3 iteraciones para este array

para la primera etapa de la iteración, la matriz de probabilidad de transición sería:
[matemáticas] X1 = \ begin {bmatrix}
1/3 y 1/3 y 1/3 \\
1/3 y 2/3 y 0 \\
1/3 y 0 y 2/3
\ end {bmatrix} [/ math]

para la segunda etapa de la iteración:
[matemáticas] X2 = \ begin {bmatrix}
2/3 y 1/3 y 0 \\
1/3 y 1/3 y 1/3 \\
0 y 1/3 y 2/3
\ end {bmatrix} [/ math]

para la tercera etapa:
[matemáticas] X3 = \ begin {bmatrix}
2/3 y 0 y 1/3 \\
0 y 2/3 y 1/3 \\
1/3 y 1/3 y 1/3
\ end {bmatrix} [/ math]

Si multiplicamos estas tres matrices, obtendremos la probabilidad de transición general W = X1 * X2 * X3 donde w [i] [j] representaría la probabilidad de que el elemento en la posición i se mueva a la posición j como resultado de esta combinación. algoritmo.
esto funciona porque [math] prob (i \ rightarrow k) = \ sum_ {j} ^ {} prob (i \ rightarrow j) * prob (j \ rightarrow k) [/ math] que es lo mismo que la regla de multiplicación de matrices [matemática] matriz [i] [k] = \ sum_ {j} ^ {} matriz [i] [j] * matriz [j] [k] [/ matemática]

Aquí está el código matlab para generar estas matrices:

n = 3; %length of the array/string = n, to be shuffled n times
x = zeros(n,n,n); %initializing probability matrix for n stages
w = eye(n); % identity matrix
for z= 1:1:n
for i = 1:1:n
for j= 1:1:n
if(z==i)
x(i,:,z) = 1/n;
elseif (z==j)
x(i,j,z) = 1/n;
elseif (i==j)
x(i,j,z) = 1-1/n;
end
end
end
w = w*x(:,:,z); %probability matrix at the end of the shuffle
end

esto es lo que obtendrás:
[matemáticas] W = \ begin {bmatrix}
.3333 y .3333 y .3333 \\
.3704 y .2963 y .3333 \\
.2963 y .3704 y .3333
\ end {bmatrix} [/ math]

Si observa la tercera fila, representa la probabilidad de que el elemento en la posición 3 se mueva a las posiciones (1,2,3) con [.2963 .3704 .3333] probabilidad, que es la misma que en la respuesta de Jonathan Paulson. Cada elemento de esta matriz debe ser 1/3 para un algoritmo de barajado correcto, para este algoritmo particular si barajamos 123, una matriz barajada que comienza con 2XX es mucho más probable que 1XX o 3XX.

Una forma de ver esto es que si cuenta el número de rutas a través del algoritmo para barajar entre 0 y el último elemento, esto resulta ser [matemática] n ^ n [/ matemática], mientras que el número de permutaciones distribuidas uniformemente es [matemáticas] n! [/ matemáticas]. Como [math] n ^ n [/ math] no es divisible por [math] n! [/ Math] para [math] n \ geq 3 [/ math], no es posible que tengas la misma probabilidad de cada permutación. (El número de rutas a través del algoritmo de Fisher-Yates es [math] n! [/ Math])

Con el enfoque de matriz de Abhishek Shekhawat, también puede ver que las matrices de transición tienen términos con denominador [matemática] n ^ n [/ matemática] cuando se multiplican; allí, como en la respuesta de Jonathan Paulson, las probabilidades para [matemática] n = 3 [ / matemáticas] terminan siendo [matemáticas] 8/27, 1/3, 10/27 [/ matemáticas].

Google Codejam acaba de tener un problema al respecto: Panel – Ronda 1A 2014 – Google Code Jam. Supongo que la existencia del problema es una prueba de una especie 🙂

Si calcula la probabilidad de que el elemento 3 termine en la posición 1 al barajar una matriz de 3 elementos mediante este algoritmo, verá que no es 1/3, aunque debería estarlo.

Desafortunadamente, no conozco una buena manera de encontrar esta probabilidad. Aquí hay un código (muy) feo que enumera todas las probabilidades de intercambio 3 ^ 3:

$ cat a.py
P = [0, 0, 0]

# Mantenga un registro de dónde está ‘3’
# P3i es la posición después de intercambiar
P30 = 2
para i1 en el rango (3):
# Si estuviéramos en un intercambio
si 0 == P30 o i1 == P30:
# Cambiar a la otra posición
P31 = i1 + 0 – P30
más:
P31 = P30
para i2 en el rango (3):
si 1 == P31 o i2 == P31:
P32 = i2 + 1 – P31
más:
P32 = P31
para i3 en el rango (3):
si 2 == P32 o i3 == P32:
P33 = i3 + 2 – P32
más:
P33 = P32
# Cada conjunto de intercambios ocurre con probabilidad 1/27
P [P33] + = 1.0 / 27
imprimir P

$ python a.py
[0.2962962962962963, 0.37037037037037035, 0.3333333333333333]

Jeff Atwood tiene una gran publicación de blog sobre esto aquí: The Danger of Naïveté.