fbpx
Wikipedia

Algoritmo de Fisher-Yates

El algoritmo de Fisher-Yates (o alguna variante del mismo), es el que se usa típicamente para barajar en los juegos de azar.

También es el algoritmo que permite recorrer toda una selección (por ejemplo una lista musical), de forma aleatoria una sola vez (una reproducción por cada elemento en la lista). Ver más detalles en la sección más abajo.

El algoritmo Fisher-Yates, es un algoritmo de permutaciones que técnicamente encaja en la categoría de los algoritmos de ordenamiento, aunque en este caso, el fin perseguido es el opuesto, desordenar los ítems que contiene. Y por tanto debería constar en las bibliotecas de ordenamiento como Random Sort al menos.


Historia

El algoritmo Fisher-Yates, aparece por primera vez documentado por Ronald A. Fisher y Frank Yates, en el libro titulado Statistical tables for biological, agricultural and medical research.[1]​ si bien su descripción era realizada con lápiz y papel.

Posteriormente otros autores (probablemente sin conocimiento previo de dicha publicación) elaboraron el mismo algoritmo. Lincoln E. Moses y Robert V. Oakford en Tables of Random Permutations[2]​ y Durstenfeld en CACM-7[3]​(1964), a quienes Knuth cita en su libro The Art of Computer Programming[4]​ y que describe como "Algoritmo P" (pags. 139-140 del Vol.2).

En la 'sabiduría popular' este algoritmo, se conoce como el 'barajado del sombrero', ya que la descripción que Fisher y Yates hicieron de él, es la que comúnmente se lleva a cabo cuando se hace una rifa, por ejemplo (ver los detalles más abajo, en la descripción del mismo).

Transcripción a la programación

Fue Durstenfeld, quien primero hizo una transcripción en la forma de algoritmo para usarse en un ordenador. La descripción de Fisher y Yates, exige el uso de 2 matrices (en los trabajos de campo, es simplemente anotar en dos partes de un papel, aunque bien podría reutilizarse borrando y rescribiendo en el mismo sitio), Durstenfeld usa la propia matriz, para llevar a cabo todo el algoritmo, necesitando solo como memoria extra una variable temporal.

Descripción del algoritmo

La forma más simple de entenderlo es partir de la forma popular:

  • Se escribe cada número en un papelito doblado (construir el array), se introducen todos los números en un sombrero, se agita el contenido dentro del sombrero (se barajan), luego se van sacando papelitos que son dispuestos en el mismo orden en que se sacan, hasta que no quede ninguno. El resultado es la lista barajada.
  • La descripción (grosso modo) que dan Fisher y Yates es la siguiente: Se escribe en una línea los números del orden de la serie de 0 (ó 1) hasta el final de la serie (se supone una serie corta, manejable mediante lápiz y papel) y se dispone debajo otra línea vacía. Y se hace lo siguiente:
  1. Se elige uno al azar.
  2. Se escribe en otra línea (a la derecha de los que ya haya escritos).
  3. Se tacha de la línea anterior (el número salido al azar).

Se repiten estos 3 pasos, hasta que solo quede 1, en la línea de arriba sin tachar, entonces se toma directamente y se pasa a la de abajo.

  • La descripción de Durstenfeld, varía de la de Fisher y Yates, en que al llevarla a cabo en un programa se trata de ahorrar memoria y por tanto trata de usar el mismo array, para ello, va intercambiando el valor de la posición al azar, y lo remplaza por el último del array no remplazado ya. Es decir al iniciar el algoritmo, el primero elegido se remplaza por el último, en el siguiente ciclo se elige al azar entre todos menos el último, que ahora se remplaza por el penúltimo, y de nuevo se elige otro al azar entre todos menos los dos últimos, etc... Básicamente, Durstenfeld, escribe el resultado a la izquierda del previo, en tanto que Fisher-Yates, lo hacía a la derecha.

Es un error común pensar que la aleatoriedad de la lista es dependiente del algoritmo de barajado (siempre que sea correcto, que para cada posición sea elegida una posición al azar), cuando en realidad es dependiente del algoritmo de aleatoriedad/pseudoaleatoriedad. La lista simplemente es reordenada de otra manera, siempre contiene los mismos valores, solo cambian sus posiciones, que son dependientes del algoritmo de aleatoriedad (que no se está implementando, en ningún caso). En cualquier caso, una vez construido el algoritmo, puede o debe probarse su imparcialidad demostrando las desviaciones de probabilidad.

Pseudocódigo del algoritmo

Ésta es la descripción del algoritmo de Durstenfeld. Debe notarse que el elemento que queda al final del recorrido (ítem-0) como entrada, es el primero en la salida, razón por la que no necesita ser intercambiado de posición (es la misma).

 Algoritmo BarajadoAleatorio: Entrada: Un Array(0,1,2,3,4... Cantidad-1) de valores. Salida: El mismo array con sus valores en posiciones aleatorias. Definición de variables: Cantidad: Un entero que señala la cantidad total de ítems que tiene el array. k: Un entero, que rige la cuenta del bucle. az: Un entero, elegido por una función Random en el rango 0-k (nótese que k se va reduciendo). tmp: Un entero, que ha de contener un valor para intercambiar valores entre 2 posiciones. Funciones auxiliares: Tamaño: Una función que devuelve la cantidad de elementos que contiene el array. Random: Una función que devuelve un número aleatorio de un rango de valores. Cantidad = Tamaño(Array) Recorrer con k desde Cantidad-1 hasta 1 Regresivamente az = Random(entre 0 y k)  tmp = Array(az) Array(az) = Array(k) Array(k) = tmp Siguiente 

Aunque el coste (en tiempo y memoria) sea mayor, el mismo algoritmo tendría el siguiente pseudocódigo resuelto con una estructura que permita inserciones y eliminaciones en posiciones arbitrarias:

 Algoritmo BarajadoAleatorio: Las variables son las mismas del caso anterior, excepto, que en la entrada en vez de un array, se recibe una estructura (por ejemplo una colección, una lista enlazada, un árbol, etc...) Recorrer con k desde Cantidad-1 hasta 1 Regresivamente az = Random(entre 0 y k)  Estruc.AñadirItem(Estruc.Item(az), Al Final) Estruc.BorrarItem(az) Siguiente 

Es interesante observar en este caso, que los ítems al añadirlos al final, lo hacen a la derecha del añadido anterior, es decir tal como Fisher y Yates describieron. Si se prefiere, conservar un añadido a la izquierda del previo añadido (tal como en el caso mostrado del array), debe cambiarse la línea de añadido, por la siguiente:

 Estruc.AñadirItem(Estruc.Item(az), En Posición k) 

Puesto que el bucle hace un recorrido regresivo, k vale justo 1 más de la posición límite pedida a la función Random, y se añade justo antes de que sea eliminado el elemento elegido, por tanto k irá siendo siempre un valor menor en cada ciclo, por lo que en efecto se iría colocando a la izquierda del previo. Esto se puede ver más claro, en los ejemplos paso a paso, en la sección correspondiente (más abajo).

Variantes

  • 1 El algoritmo, presenta algunas variantes. De hecho es bastante fácil que al tratar de implementar dicho algoritmo se acabe haciendo este otro. Cuya particularidad más destacable, es que siempre que se baraja se elige de nuevo entre todos los existentes (es como si el sombrero tuviera posiciones donde se coloca cada uno cuando se meten, y tras barajar el elemento sacado del sombrero se volviera a introducir de nuevo y se sacaran tantos como elementos hay en el sombrero y finalmente se expusiera el orden en que los elementos aparecen en el sombrero):
 Recorrer con k desde 0 hasta Cantidad - 1 az = Random(entre 0 y Cantidad-1) tmp = Array(az)  Array(az) = Array(k) Array(k) = tmp Siguiente 
  • 2 También es posible modificar el algoritmo para solucionar una lista cuya cantidad de elementos se desconoce (no tiene una cantidad fija, caso típico de una estructura que permite añadir y eliminar elementos).
  • 3 También es posible modificar el algoritmo, de Durnstenfeld, para que opere en el orden que describe Fisher-Yates (siguiente a la derecha del previo). Es decir, los valores obtenidos se van colocando en la posición más baja y creciendo. Nótese que no hay pérdida de eficiencia, y nótese que el recorrido le basta llegar hasta el penúltimo.
 Recorrer con k desde 0 hasta Cantidad - 2 az = Random(entre k y Cantidad-1) tmp = Array(az)  Array(az) = Array(k) Array(k) = tmp Siguiente 
  • 4 Una variante interesante para determinados juegos, es aquella capaz aun de generar todas las combinaciones posibles, pero que genere algunas con más frecuencia que otras, pero todavía de forma aleatoria, es decir que a pesar de ello no sea predecible. Motivado para permitir un juego que provea jugadas más interesantes y entretenidas con un reparto (de cartas, fichas, etc...) no tan imparcial (en cuanto a lo que sale, no en cuanto a quien sale). (para acentuar esta característica ver la 2ª parte de la sección variaciones parámetrizadas). El siguiente pseudocódigo, obtiene 2 números al azar y ambos señalan posiciones, que son las que se intercambian entre sí. Si sucede que la posición de origen y destino es la misma, el intercambio no produce un cambio real. También puede suceder que una o ambas posiciones, hayan salido antes ya. Ambas cosas permiten que la serie no sea tan diferente entre la entrada y la salida, comparada con las otras implementaciones. Es fácil percibir que al no usarse como índice el contador del bucle, sino 2 índices elegidos al azar, pueda haber posiciones que en un barajado no salga y por tanto aumenta la probabilidad de que más de 1 elemento mantenga a la salida, la misma posición que tenía a la entrada. Puede aumentarse notablemente la eficacia de lo que trata de hacer el algoritmo si el bucle solo recorre la mitad de los elementos (mitad de posibilidades de que salga cada posición). He aquí el pseudocódigo de esta variante:
 Fin = (Tamaño(Array) \ 2) - 1 Recorrer con k desde 0 hasta Fin <--- Solo recorre la mitad. az1 = Random(entre 0 y Cantidad-1) az2 = Random(entre 0 y Cantidad-1) tmp = Array(az1) <---- 'k' no se refiere nunca a la posición de un elemento en el Array(az1) = Array(az2) <--- array de esta variante, como sucede en el resto de variantes. Array(az2) = tmp Siguiente 
  • 5 Hay aun una forma diferente de concebir el algoritmo de barajado, que ya no se ajusta al artículo, pero que aun resulta de interés presentar aquí. Sea una estructura en la que las permutaciones están ya precalculadas y almacenadas y se usa una función random para elegir (el índice) cual de las permutaciones en la estructura es la que se entrega. Esta variante, sin embargo queda en la práctica limitada a series cuya cantidad de elementos sean relativamente pequeño, dado el espacio de memoria que consume almacenar todas la serie de permutaciones posibles. Aunque cuando la velocidad es fundamental esta solución resulta la más apta. Es ideal que una vez halladas las permutaciones diferentes estas sean barajadas (para que no consten el orden creciente en que posiblemente las devuelva la función) y que se haga lo mismo cada cierto número de entregas.
 Variables nuevas: Series: Es una estructura acorde para contener arrays donde cada array mantiene una  permutación distinta de la serie (un árbol de arrays, otro array de arrays, etc...). Veces: Es un contador, para barajar el orden de los arrays en la estructura series, cada vez que alcanza cierto valor, para favorecer la aleatoriedad. X: Un valor límite que controla el límite de veces. Este valor es dependiente  de la cantidad de permutaciones Funciones accesorias Permutaciones: Sería una función de combinatoria, que devuelve una estructura de arrays, con todas las series posibles, donde cada array contiene una serie distinta. Barajar: Es un algoritmo de barajado aleatorio (como los descritos), con la particularidad de que cambiaría los punteros de los índices del array, en vez del contenido. 
 --- Series Es precalculada, (y de nuevo cada vez que cambiare la cantidad de elementos). Series = Permutaciones(Cantidad) 
 Función: BarajarAleatorio veces = veces + 1 si veces = x entonces <--- 'x' es un valor que se estalece en función de la cantidad de elementos del array. Barajar(Series) veces = 0 Fin si az = Random(entre 0 y Cantidad-1) Array = Series(az) Devolver Array Fin función 
  • 6 Otra interesante variación es el algoritmo de Sattolo. Merece ser descrito más ampliamente, en la siguiente sección.

Variante Sattolo

Fue publicado en 1986 en IPL-22,[5]​ por Sattolo en Information Processing Letters[6]​ y tiene algunas particularidades que se describen:

  • Hace el barajado, con la garantía de que cada elemento no ocupe la posición (a la salida) que ocupaba inicialmente (a la entrada). Para ello se recurre a condicionar la búsqueda aleatoria del elemento al rango de un elemento por debajo de la posición actual, del que se va a reemplazar. Esta es la particularidad interesante de este algoritmo, pero no la única.
  • Las permutaciones que realiza una y otra vez, acaban siendo cíclicas (se repiten), aunque no sigan un orden específico, si pueden presentarse secuencias ya aparecidas.
  • Hay permutaciones que jamás pueden ocurrir, en series impares (en dichas series solo alcanza a la mitad de las permutaciones, si la serie entra ordenada y se ejecutan el número suficiente de veces). Por ello no es un algoritmo imparcial y debe evitarse su uso en juegos de apuestas (habría la mitad de combinaciones sin ninguna posibilidad real y la otra mitad de combinaciones con el doble de posibilidades), salvo que se tomen las debidas precauciones y sea estrictamente necesario que ningún elemento repita su posición a la salida respecto de la entrada, y siempre que se tenga un conocimiento expreso y exahustivo de los resultados que puede ofrecer. El modo de prevenir dicho problema es garantizar que siempre se usan listas de cantidad pares, añadiendo cuando sean impares un último elemento (que se deseche en uso).
  • Éste, es otro caso de algoritmo, que por error se puede llegar a implementar al tratar de implementar el de Durstenfeld, ya que la diferencia entra ambas implementaciones es mínima.
 Recorrer con k desde Cantidad-1 hasta 1 Regresivamente az = Random(entre 0 y k-1) <--- EL CAMBIO: k-1, en vez de k (es la diferencia con Durstenfeld)  tmp = Array(az) Array(az) = Array(k) Array(k) = tmp Siguiente 
  • Ya que el algoritmo para conjuntos cuya cantidad de elementos es impar evita la mitad de combinaciones, puede interesar reproducir en algún momento otras permutaciones. El modo de lograr otras combinaciones (entre el resto de combinaciones), consiste en solicitar, un nuevo barajado para todo el conjunto menos 1 (ó varios) elemento (por ejemplo el último o el primero), el cual mantiene su posición. Es decir, hay que generar una combinación nueva que mantenega al menos 1 elemento en la misma posición que antes. Para ello habría que generar el código dedicado al algoritmo (haciéndole creer que tiene un elemento menos, tal como se expresaba en una de las variantes previas). Se consigue así generar diversas series cíclicas (pero no se garantiza que sean todo el resto), dependiendo de cual o cuales sean los valores que se omitan en el barajado, el ciclo de la serie será más largo o más corto. El siguiente pseudocodigo se encarga precisamente de eso.
 Cantidad = Tamaño(Array) Si se pide OmitirUno entonces Cantidad = Cantidad - 1 Fin si Recorrer con k desde Cantidad-1 hasta 1 Regresivamente az = Random(entre 0 y k-1)  tmp = Array(az) Array(az) = Array(k) Array(k) = tmp Siguiente Que se invocaría con la siguiente llamada: Llamada a la función BarajarAleatorio(Array, Sattolo, OmitirUno) 

Variaciones parametrizadas

Hay algunas variaciones que pueden ser aplicadas a todas las implementaciones comentadas hasta el momento, bien que pueda variar el código exacto que deba añadirse o modificarse en cada una. Implementar estas variaciones, implica añadir parámetros en las funciones para que al invocarlas puede elegirse el valor necesario para que cumpla la misión encomendada.

  • Una variación aplicable a todas las implementaciones, consiste en hacer un barajado dejando 1 elemento de ellos (o varios, seguidos) sin barajar, es decir quedando en la misma posición. El modo más sencillo de llevarlo a efecto es mentir al algoritmo en cuestión haciéndole creer que tiene un elemento menos, así baraja todos (los que el algoritmo en cuestión baraje) menos uno (o esos varios). Tal circunstancia puede reclamarse en un parámetro opcional en la llamada a la función. He aquí el pseudocódigo que se podría añadir a cualquiera de ellos, detrás se pone la implementación de Durstenfeld, para apreciar lo que implica (más arriba en Sattolo, se detalla un caso, que hace uso de esta variación, para generar series cíclicas pero diferente a la previa).
 Cantidad = Tamaño(Array) Si se pide OmitirUno entonces <--- OmitirUno sería aquí un parámetro opcional de tipo buleano Cantidad = Cantidad - 1 Fin si 
 Recorrer con k desde Cantidad-1 hasta 1 Regresivamente az = Random(entre 0 y k) tmp = Array(az) Array(az) = Array(k) Array(k) = tmp Siguiente 
 La invocación a una función que implementa esta variación en el algoritmo sería: Llamada a la función BarajarAleatorio(Array, Durstenfeld, OmitirUno) 
  • Tal y como se comentaba en la variante numerada como 4 (más arriba), a menudo hay juegos donde es regla del mismo (establecida así en las reglas o acordado entre los propios jugadores) que el barajado no sea excesivo (suelen ser juegos de entretenimiento y no tanto de apuestas por el posible riesgo que entraña), para permitir un juego más interesante y entretenido (por ejemplo un juego como el tute 'cabrón' acepta muy bien esta situación). Puede entenderse como al tiempo que los jugadores con peores cartas o fichas, finalizan antes su juego, sus cartas quedan acumuladas juntas, al tiempo quedan en la partida los jugadores con más triunfos, que al final de la partida quedan también amontonadas sus cartas, con lo que en la baraja, quedan reunidos los triunfos por un lado y las cartas sin valor por otro, de modo que al barajar escasamente los triunfos puedan aún estar algo acumulados y que el reparto de cartas en una nueva partida, provea cartas significativas a un jugador u otro. A veces para garantizar que esto mismo no se enturbia, se procede a repartir varias cartas seguidas a cada jugador, en vez de repartir a cada jugador una carta cada vez. Ambas cosas favorecen que una partida produzca jugadas más interesantes (el algoritmo de dicha variante se halla más arriba como variante 4). Podemos pedir a un algoritmo que cada x elementos del recorrido, omita y barajados (es el pseudocódigo que se presenta a continuación). En este sentido, puede proveerse una variación a los algoritmos proporcionados para favorecer ese escaso barajado, en esta ocasión, tomamos la variante que mejor conjunta con esta causa:
 Entrada: Un Array(0,1,2,3,4... Cantidad-1) de valores. Grupo: un entero que señala cada cuantos se evita un barajado. Salto: un entero que indica cuantos seguidos quedan sin barajar. Cantidad = Tamaño(Array) Fin = (Cantidad \ 2) - 1 Recorrer con k desde 0 hasta Fin <--- Solo recorre la mitad. Si k es congruente con Grupo entonces ---> congruente con, refiere a la operación módulo  (que se traduce como: si ((k mod grupo) = 0) luego k = k + salto En otro caso  az1 = Random(entre 0 y Cantidad-1) az2 = Random(entre 0 y Cantidad-1) tmp = Array(az1) Array(az1) = Array(az2) <---- 'k' no se refiere nunca a la posición de un elemento en el array de esta variante. Array(az2) = tmp Fin si Siguiente 
 La invocación a una función que implementa esta variación en el algoritmo sería: Llamada a la función BarajarAleatorio(Array, Semibarajado, Grupo=13, Salto=3) 

Supongamos una baraja de 52 cartas y supongamos que queremos que cada 13 cartas que baraje, se salte 3. Siendo el array de 0 a 51 elementos, en las posiciones k módulo 13 (que son 0, 13, 26 y 39) al llegar a dichas posiciones aumentará k en 3 unidades, que en efecto tiene como consecuencia no barajar 3 elementos seguidos, como son 4 veces (4 grupos de 13) hay 12 barajados menos. También pueden especificarse los parámetros como los que se omiten y los que se barajan (en el ejemplo, serían Barajan=10 y se Omiten=3) y la suma de ambos sería el equivalente al grupo, así se evita la necesidad de comprobar que salto sea menor que grupo.

Debe notarse que esta variación no evita que los valores en esas posiciones se mantenga en su mismo sitio (pueden salir elegidas por la función Random en cualquier otro instante del recorrido del bucle), pero si tiene por efecto, que 12 elementos de los 52 no sean barajados, lo que por supuesto tendrá incidencia, en una cierta similitud entre la serie a la salida y la serie de entrada.


  • Todas las implementaciones, puede ser también fácilmente modificadas para hacer el barajado, solo con un subconjunto del rango del array (no a todos), señalando un parámetro de inicio del elemento que se empieza a barajar y otro de la cantidad de elementos afectados. Esto (también) puede aplicarse a listas extremadamente largas donde la ocupación de la memoria pueda ser crítica, o el tiempo necesario para disponer, de todo el array barajado, no es aceptable. Dicha modificación puede solucionar el problema. Es imaginable un array de mil millones de elementos, donde solo se modifica 1 millón de elementos consecutivos cada vez, e incluso una modificación donde se modifica 1 millón de elementos a una distancia de 1000 elementos entre uno y otro(el 1, el 1001, 2001, 3001, 4001.... 1001001), cada vez...
  • Todavía todas las implementaciones, admiten aún otra parametrización para hacer un barajado por bloques (lo que comúnmente en los juegos de cartas se llama cortar), que intercambia bloques de cartas y donde los elementos individuales en cada bloque mantiene su posición. En las cartas, al ser barajadas a mano, cada bloque tiene un tamaño indeterminado e independiente del otro. Hay dos formas típicas de cortar: Cortar en 2 montones (de diferente tamaño) con las manos, uno se pone encima del que antes estaba arriba y se repite el proceso varias veces. Otras veces, sobre el tapete se reparten pequeños montones (de cantidades desiguales por lo general, ya que se hace a mano) y se recogen de la mesa en orden diferente al que se ha repartido. Básicamente este es el pseudocódigo que se expone a continuación con la salvedad de que se especifica un tamaño de bloque que se ajusta para todos excepto (quizás) el último montón. La función recibiría un parámetro señalando el tamaño del bloque a considerar.
 Función BarajadoAleatorio Método BarajadoEnBloques Cantidad = Tamaño(Array) Grupos = Cantidad \ Bloque <--- es una división entera, desprecia decimales. Si (Cantidad módulo Bloque) es mayor que 0 entonces sumar 1 a Grupos En la posibilidad de que el último grupo no esté completo se intercambia aparte. Es decir intercambiamos primero el último bloque por el que bloque elegido al azar. Az = Random(entre 0 y Grupos-1) Si Az es distinto de' Grupos-1 entonces <--- si el grupo al azar es el último,      no hace falta intercambiarlo. p = Az * Bloque Recorrer con k desde (Grupos-1)*bloque hasta Cantidad-1 tmp = Array(p) Array(p) = Array(k) Array(k) = tmp p = p + 1 Siguiente Fin si ----> aquí va el inserto que se señala unos párrafos más abajo <--- Grupos = Grupos - 1 Recorrer con J desde (Grupos - 1) hasta 1 Regresivamente Az = Random(entre 0 y J) Si Az es distinto de' J entonces <--- si el grupo al azar es el mismo del     bucle, no hace falta intercambiarlo. p = Az * Bloque n = Az * Bloque Recorrer con k desde n hasta n + Bloque - 1 tmp = Array(p) Array(p) = Array(k) Array(k) = tmp p = p + 1 Siguiente Fin si Siguiente 
 Y la invocación a la función sería así: Llamada a la función BarajarAleatorio(Array, BarajadoEnBloques, Bloque=4) 

Imagínese un mazo de 28 cartas, y bloques de 4 cartas, lo que nos daría 7 montones y sería equivalente a barajar una serie de 7 elementos (7 * 4 = 28), si los montones fueran de 6 cartas el último montón solo tendría 4 cartas, luego cuando saliera éste solo se intercambiarían 4 cartas con el bloque que se intercambia. Se pone una serie de resultados de 28 elementos, con un bloque de 4, para darse cuenta como el afecta a un barajado de esta manera. Obsérveses como las tuplas de 4 elementos (se ha coloreado un par de ellas), no cambian entre sí el orden (de sus elementos):

 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 24 25 26 27 20 21 22 23 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 16 17 18 19 00 01 02 03 20 21 22 23 24 25 26 27 04 05 06 07 08 09 10 11 12 13 14 15 00 01 02 03 24 25 26 27 12 13 14 15 16 17 18 19 04 05 06 07 08 09 10 11 20 21 22 23 04 05 06 07 16 17 18 19 20 21 22 23 24 25 26 27 12 13 14 15 00 01 02 03 08 09 10 11 

Si el bloque produce grupos exactos entonces no generará todas las permutaciones posibles, de hecho ni aunque haya bloques no exactos, no generará todas las permutaciones posibles. Por ejemplo para una serie de 7 elementos (que permite 5040 permutaciones distintas de la serie) con un bloque de 2 genera 144 permutaciones, con un bloque de 3 genera sólo 12 permutaciones, con un bloque de 4, 5 y 6 solo hay 2 permutaciones posibles (ya que solo ofrece 2 grupos). Por ello hay que considerar que el número de permutaciones obedece más al número de grupos y los elementos en el último grupo, que a la cantidad de elementos. Por último, señalar que si el tamaño de bloque es 1, equivale a barajar todos los ítems, cual si no se usara dicha funcionalidad. Hay que considerar que la entrada y la salida varían poco entre sí, pero dado que tras la salida hay un nuevo juego, la siguiente entrada tendrá una permutación muy distinta de la salida previa. Si el caso es que se desea que haya una mayor posibilidad de permutaciones, podría barajarse el último bloque (que antes estaba en otra posición y se intercambió con el último), de modo que en sucesivas veces cuando sea cambiado por el bloque en otra posición, irá generando una mayor cantidad de permutaciones aunque varíen poco de una a otra en el tiempo. Esto puede ir especificado con otro parámetro o quedar fijo en la variación del algoritmo. El pseudocódigo sería el siguiente y se colocaría justos donde aparece esta línea:

 ----> aquí va el inserto que se señala unos párrafos más abajo <--- 
 ' Barajar los elementos internos del que ahora es el último bloque. Min = ((Grupos - 1) * Bloque) Recorrer con k desde Cantidad - 1 hasta Min Regresivamente <---- el barajado es método Durstenfeld,      en este ejemplo, pero podría ser otro Az = Random(entre Min y k) <---- Min: limita el rango inferior en vez de 0 tmp = Array(k) Array(k) = Array(Az) Array(Az) = tmp Siguiente 

Fíjese en las siguientes salidas como es barajado siempre el bloque que se intercambió a la última posición y como desde entonces ese bloque se traslada así (con ese nuevo orden entre sus elementos) a la siguiente entrada (sin cambios tras la salida). Para estudiar el caso, aquí la salida de una serie es la entrada de la siguiente, caso que se supone que no será cierto una vez se traslade a una aplicación real (que tras un barajado habrá un juego tal que acaben sus elementos en otro orden). Se marca en azul el bloque que será el último en la siguiente etapa, y se observa de rojo ese bloque en la siguiente fase, posicionado el último y ya barajado. Ese bloque así barajado mantiene su nuevo orden (interno) en lo sucesivo.

 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <---entrada 04 05 06 07 20 21 22 23 08 09 10 11 24 25 26 27 12 13 14 15 00 01 02 03 19 16 17 18 19 16 17 18 04 05 06 07 24 25 26 27 08 09 10 11 20 21 22 23 00 01 02 03 14 15 13 12 04 05 06 07 24 25 26 27 20 21 22 23 19 16 17 18 14 15 13 12 08 09 10 11 02 01 00 03 02 01 00 03 14 15 13 12 08 09 10 11 20 21 22 23 19 17 16 18 07 04 07 05 26 25 24 27 14 15 12 13 02 08 09 10 11 01 00 03 19 17 16 18 07 04 07 05 20 21 22 23 25 27 24 26 <--- el último     bloque se intercambia consigo mismo esta vez. 

Como se puede apreciar, esta variación también permite generar permutaciones aptas para cuando se desea generar jugadas interesantes y entretenidas en juegos que lo necesitan, y en cambio no es aceptable en juegos con apuestas.

Usos

Como ya se ha dicho en el encabezado del artículo, es el algoritmo que se usa típicamente para barajar en los juegos de azar (o alguna de sus variantes).

Se usa también en los modelos estadísticos cuando se requiere simular un origen de datos aleatorio. Un caso práctico, para probar la eficiencia de los algoritmos de ordenamiento. hacer simulaciones del clima, etc...

También se usa en sistemas y programas donde se desea usar una sola vez cada elemento de la serie pero de una forma aleatoria, sin recurrir a una estructura adicional de almacenamiento. Se provee un ejemplo al detalle, sobre las listas de reproducción de canciones.

Reproducción aleatoria de los elementos de una lista

El presente algoritmo, resuelve también este caso, sin la necesidad de recurrir a una segunda estructura donde mover los elementos retirados de la primera.

Imaginemos el caso más corriente, una lista de canciones de la que se desea reproducir cada canción una sola vez, pero que lo hagan en orden aleatorio. Inicialmente se tendrá una lista con las rutas de las canciones, lista que conviene dejar intacta (no intercambiar sus elementos). Se usa un array cuyo contenido (inicialmente) son los índices de las canciones en la lista. Si el orden es aleatorio, se manda barajar el array, si el orden solicitado es el original, se reasigna a cada índice el valor del índice de la lista (el orden natural correlativo). La reproducción finalmente usará como índice, el valor de dicho array. En vez de invocar: Reproducir(Ruta(k)), se invocará: Reproducir(Ruta(Array(k))), así la lista original, no necesita ser tocada ni duplicada para mantener el orden original (es más rápido en memoria asignar valores numéricos, que textos).

 Si la lista se desea reproducir en la forma original, Necesitamos recrear el orden original: Recorrer con k desde 0 hasta Cantidad-1 Array(k) = k Siguiente Llamada a la función ReproducirLista 
 Si la lista se desea reproducir en forma aleatoria, Desordenamos el array con el algoritmo: Llamada a la función BarajarAleatorio(Array) Llamada a la función ReproducirLista 
 Y la función ReproducirLista sería esta. Recorrer con k desde 0 hasta Cantidad-1 Reproducir(Ruta(Array(k))) Siguiente 

Ejemplos paso a paso con comentarios

Se irán exponiendo ejemplos paso a paso de las implementaciones más relevantes, sobre una serie de 7 elementos, en tablas con columnas, cuyo significado son:

  • Ciclo: identifica a la variable que controla el bucle, cuando es descendente irá de 6 a 0 cuando sea creciente irá de 0 a 6. Su valor se colorea de rojo
  • Rango: Rango de posiciones elegible en cada instante.
  • Azar: La posición que ha salido al azar. Su valor se colorea de azul
  • Antes: El orden que mantiene la serie antes de aplicar la operación actual.
  • Después: El orden que mantiene la serie tras aplicar la operación.
  • Resultado: Alguna tabla muestra una columna adicional, para más claridad. Su valor se colorea de rojo.
  • Otros detalles:
    • En la tabla que corresponda, se separa la serie con :, para mayor claridad.
    • Aquellos campos que no precisen ser rellenados se señalan con -.
    • Las tablas se completan visualmente, hasta su término (aun cuando no necesite un ciclo final), para acabar de verlo claro.
    • Cuando ambas posiciones (columnas ciclo y azar) se refieran a la misma, el valor se colorea de púrpura.
    • El contenido del campo Después de un ciclo, es el campo Antes del siguiente ciclo, que se repite para mejor seguimiento.

Tabla paso a paso (implementación Durstenfeld)

Para el algoritmo descrito en el pseudocódigo (Durstenfeld). Nótese las siguientes cuestiones para este ejemplo:

  • Los intercambios son entre valores de las posiciones ciclo y azar.
  • Nótese especialmente como los valores van colocándose a la derecha del separador : que se ha puesto para mejor visibilidad.
  • Hay que fijarse también, que aunque han salido 3 veces al azar la posición 0, ello no tiene influencia significativa en el resultado, ya que se refiere a una posición y cada vez será intercambiado por un valor en una posición distinta.
  • El ciclo 2, tanto la posición de ciclo, como la de azar refieren a la posición 2, luego el valor se intercambia con sí mismo.
  • En esta implementación, no es preciso hacer el ciclo 0 (no hay necesidad de barajar si solo hay 1 elemento entre los que escoger), por tanto se queda el valor que yace en ese momento en dicha posición y se ahorra un ciclo en el bucle.
Ciclo Rango Azar Antes Después
      0 1 2 3 4 5 6 : 0 1 2 3 4 5 6 :
6 0-6 4 0 1 2 3 4 5 6 : 0 1 2 3 6 5 :4
5 0-5 0 0 1 2 3 6 5 : 4 5 1 2 3 6 : 0 4
4 0-4 0 5 1 2 3 6 : 0 4 6 1 2 3 : 5 0 4
3 0-3 2 6 1 2 3 : 5 0 4 6 1 3 : 2 5 0 4
2 0-2 2 6 1 3 : 2 5 0 4 6 1 : 3 2 5 0 4
1 0-1 0 6 1 : 3 2 5 0 4 1 : 6 3 2 5 0 4
- 0-0 - 1 : 6 3 2 5 0 4 1 6 3 2 5 0 4 :

Tabla paso a paso (implementación Fisher-Yates)

Para el algoritmo descrito en el pseudocódigo como variante de la implementación de Durstenfeld, posicionando los elementos a la derecha del previo, tal como describieron Fisher-Yates. Nótese las siguientes cuestiones para este ejemplo:

  • Las posiciones al azar, no pueden ser iguales que en el ejemplo previo, ya que a medida que aumenten los ciclos, no pueden salir los valores de ciclos ya pasados.
  • El intercambio siempre se hará entre el valor de la posicioón elegida al azar y el primero de la serie que entra en juego. Se ha puesto como separador :, para apreciar donde empieza los que entran en juego.
  • El resultado va quedando a la derecha de la serie (ver en el encabezado los : a la derecha de la serie en juego.
Ciclo Rango Azar Antes Después
      : 0 1 2 3 4 5 6 : 0 1 2 3 4 5 6
0 0-6 5 : 0 1 2 3 4 5 6 5 : 1 2 3 4 0 6
1 1-6 3 5 : 1 2 3 4 0 6 5 3 : 2 1 4 0 6
2 2-6 6 5 3 : 2 1 4 0 6 5 3 6 : 1 4 0 2
3 3-6 4 5 3 6 : 1 4 0 2 5 3 6 4 : 1 0 2
4 4-6 5 5 3 6 4 : 1 0 2 5 3 6 4 0 : 1 2
5 5-6 6 5 3 6 4 0 : 1 2 5 3 6 4 0 2 : 1
- 6-6 - 5 3 6 4 0 2 : 1 5 3 6 4 0 2 1 :

Tabla paso a paso (implementación Fisher-Yates original)

Para la descripción del algoritmo original, empleando para ello, lápiz y papel, es decir tachando de la lista en una línea y escribiendo los resultados en una nueva línea, que será la lista resultante. Esto supone simular 2 estructuras, una que contiene la lista original de la que se extraen y otra donde se insertan. Nótese las siguientes cuestiones para este ejemplo:

  • Se emplean los mismos valores de resultado que para el ejemplo anterior, lo que fuerza a elegir otros valores de azar, para que coincidan ambos resultados.
  • Aparte de diferenciarse en la representación respecto del anterior y en los valores de la columna Azar, también cambian los valores de la columna rango, pues se sigue el esquema de sacar del sombrero, por lo que en cada ciclo, contiene uno menos entre los que elegir.
  • Fijarse en como el valor en la posición libre que señala azar, se tacha en la lista después y pasa al resultado.
  • Los ítems tachados, no se cuentan ya al indizar la lista (no existen, en la tabla se dejan, para poder comparar los cambios).
Ciclo Rango Azar Antes Después Resultado
      0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 6
0 0-6 5 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5
1 0-5 3 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3
2 0-4 4 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6
3 0-3 3 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4
4 0-2 0 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 0
5 0-1 1 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 0 2
6 0-0 - 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 0 2 1

Tabla paso a paso (implementación Sattolo)

Para la descripción del algoritmo Sattolo, variante de Durstenfeld. Nótese las siguientes cuestiones para este ejemplo:

  • No puede emplearse los mismos valores (columna Azar) que en aquella tabla, ya que el rango de posiciones elegible es en cada ciclo uno menor que allí (ver columna rango).
  • Nótese la gran diferencia con durstenfeld, la columna rango se ve reducida a un ítem menos entre los que elegir al azar, respecto de la columna ciclo.
  • Dado que lo interesante de esta implementación es la capacidad del algoritmo para no repetir valores en la posición inicial, operar con 7 elementos, no sería suficiente para verlo claramente, por ello se amplía la lista a 10 elementos.
  • Nótese como el valor de la columna Azar, nunca iguala el valor de la columna Ciclo (ver en pseudocódigo el condicionante del rango en la función Random: Azar = Random(entre 0 y k-1))
Ciclo Rango Azar Antes Después
      0 1 2 3 4 5 6 7 8 9 : 0 1 2 3 4 5 6 7 8 9 :
9 0-8 6 0 1 2 3 4 5 6 7 8 9 : 0 1 2 3 4 5 9 7 8 : 6
8 0-7 4 0 1 2 3 4 5 9 7 8 : 6 0 1 2 3 8 5 9 7 : 4 6
7 0-6 4 0 1 2 3 8 5 9 7 : 4 6 0 1 2 3 7 5 9 : 8 4 6
6 0-5 1 0 1 2 3 7 5 9 : 8 4 6 0 9 2 3 7 5 : 1 8 4 6
5 0-4 1 0 9 2 3 7 5 : 1 8 4 6 0 5 2 3 7 : 9 1 8 4 6
4 0-3 3 0 5 2 3 7 : 9 1 8 4 6 0 5 2 7 : 3 9 1 8 4 6
3 0-2 0 0 5 2 7 : 3 9 1 8 4 6 7 5 2 : 0 3 9 1 8 4 6
2 0-1 1 7 5 2 : 0 3 9 1 8 4 6 7 2 : 5 0 3 9 1 8 4 6
1 0-0 0 7 2 : 5 0 3 9 1 8 4 6 2 : 7 5 0 3 9 1 8 4 6
- - - 2 : 5 0 3 9 1 8 4 6 : 2 7 5 0 3 9 1 8 4 6

Resultados de una serie de llamadas consecutivas

En la siguiente tabla se muestran los resultados de una serie de llamadas a la función, para apreciar como en efecto, no se repiten (observar verticalmente la columna resultados, nunca un elemento en la misma posición en la siguiente llamada).

  • Para esta tabla se han realizado 12 llamadas y se prescindido de los pasos internos de la función. Y para la ocasión se ponen las columnas siguientes:
    • Llamada: El índice indica la enésima llamada a la función BarajadoAleatorio para la implementación de Sattolo.
    • Azar: Contiene la lista de posiciones aleatorias obtenida (internamente) en esa llamada (por si desea recrearse manualmente siguiendo el esquema de la tabla previa).
    • Entrada y Salida: Son columnas que muestran la lista a la entrada y la salida de la función. La lista de la columna Salida de una llamada a la función, es la misma lista que se consigna en la columna Entrada de la siguiente llamada.
  • Nótese como en la columna Azar, verticalmente siempre los valores que salieron son menores, que el número que indica el encabezado (se ha coloreado toda la sub-columna encabezada por el 8 de color rojo, para apreciarse mejor, pero se aplica a todas ellas). La posición horizontal en dicha columna Azar, indica el valor obtenido por la función Random en cada ciclo interno del bucle, dado el rango en el que se ve obligado a elegir).
  • Apréciese, como en la columna Salida, ningún valor se repite en la misma posición que tenía en la llamada anterior (se ha coloreado de azul verticalmente, la sub-columna 6ª, para dirigir mejor la vista, pero se aplica a todas ellas)). Una tabla similar con cualquiera de las otra implementaciones arrojaría muchas coincidencias.
Llamada Azar Entrada Salida
  9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
1 6 4 4 1 1 3 0 1 0 0 1 2 3 4 5 6 7 8 9 2 7 5 0 3 9 1 8 4 6
2 0 3 6 4 1 3 2 0 0 2 7 5 0 3 9 1 8 4 6 9 8 6 5 4 7 3 1 0 2
3 4 6 0 3 2 1 1 1 0 9 8 6 5 4 7 3 1 0 2 7 1 0 2 8 6 5 9 3 4
4 7 6 4 5 4 0 2 1 0 7 1 0 2 8 6 5 9 3 4 2 3 1 0 7 4 6 8 5 9
4 0 7 4 0 2 0 0 1 0 2 3 1 0 7 4 6 8 5 9 4 0 3 5 6 1 9 7 8 2
6 2 3 2 5 4 1 0 0 0 4 0 3 5 6 1 9 7 8 2 9 7 8 4 0 6 1 2 5 3
7 3 3 4 1 3 0 0 1 0 9 7 8 4 0 6 1 2 5 3 8 6 1 2 9 5 7 0 3 4
8 8 2 5 2 1 3 1 1 0 8 6 1 2 9 5 7 0 3 4 7 8 9 0 2 6 4 5 1 3
9 5 5 6 5 0 2 2 0 0 7 8 9 0 2 6 4 5 1 3 8 0 5 2 9 7 1 4 3 6
10 4 3 2 2 1 0 0 1 0 8 0 5 2 9 7 1 4 3 6 1 3 7 6 8 0 4 5 2 9
11 3 3 1 2 1 2 1 0 0 1 3 7 6 8 0 4 5 2 9 2 8 1 0 4 5 7 3 9 6
12 4 3 0 4 2 3 1 1 0 2 8 1 0 4 5 7 3 9 6 5 3 7 8 9 1 6 2 0 4

Test de aleatoriedad sobre las implementaciones

Véase también

Referencias

  1. Fisher, Ronald A.; Yates, Frank (1948) [1938]. Statistical tables for biological, agricultural and medical research (3rd edición). Londres: Oliver & Boyd. pp. 26-27. OCLC 14222135.  Nota: la 6ª edición, ISBN 0-02-844720-4, es , pero ofrece un algortmo de barajado algo diferente, por C. R. Rao.
  2. Moses, Lincoln E.; Oakford, Robert V. (1963) [1963]. Tables of Random Permutations (1ª edición). Stanford (California): Stanford University Press. pp. ???. ISBN 0-8047-0148-2. 
  3. doi 10.1145/364520.364540
  4. Knuth, Donald Erwin (1969 (1981 2ªed.)). Seminumerical algorithms. The Art of Computer Programming 2. Reading, MA: Addison–Wesley. pp. 124-125 (139-140 en 2ª ed.). ISBN 0-201-03822-6. 
  5. doi 10.1016/0020-0190(86)90073-6
  6. Sandra Sattolo (25 de junio de 1985). «An algorithm to generate a random cyclic permutation» [Un algoritmo para generar una permutación cíclica aleatoria]. En Elsevier B.V., ed. Istituto di Matematica, Facolta' di Scienze Matematiche e Fisiche, Universita' degli Studi di Udine, via P. Mantica, 33100 Udine, Italy. Information Processing Letters (30-mayo-1986) 22 (6): 315-317. 
  •   Datos: Q6522952

algoritmo, fisher, yates, algoritmo, fisher, yates, alguna, variante, mismo, típicamente, para, barajar, juegos, azar, también, algoritmo, permite, recorrer, toda, selección, ejemplo, lista, musical, forma, aleatoria, sola, reproducción, cada, elemento, lista,. El algoritmo de Fisher Yates o alguna variante del mismo es el que se usa tipicamente para barajar en los juegos de azar Tambien es el algoritmo que permite recorrer toda una seleccion por ejemplo una lista musical de forma aleatoria una sola vez una reproduccion por cada elemento en la lista Ver mas detalles en la seccion mas abajo El algoritmo Fisher Yates es un algoritmo de permutaciones que tecnicamente encaja en la categoria de los algoritmos de ordenamiento aunque en este caso el fin perseguido es el opuesto desordenar los items que contiene Y por tanto deberia constar en las bibliotecas de ordenamiento como Random Sort al menos Indice 1 Historia 2 Transcripcion a la programacion 2 1 Descripcion del algoritmo 2 2 Pseudocodigo del algoritmo 3 Variantes 3 1 Variante Sattolo 3 2 Variaciones parametrizadas 4 Usos 4 1 Reproduccion aleatoria de los elementos de una lista 5 Ejemplos paso a paso con comentarios 5 1 Tabla paso a paso implementacion Durstenfeld 5 2 Tabla paso a paso implementacion Fisher Yates 5 3 Tabla paso a paso implementacion Fisher Yates original 5 4 Tabla paso a paso implementacion Sattolo 5 4 1 Resultados de una serie de llamadas consecutivas 6 Test de aleatoriedad sobre las implementaciones 7 Vease tambien 8 ReferenciasHistoria EditarEl algoritmo Fisher Yates aparece por primera vez documentado por Ronald A Fisher y Frank Yates en el libro titulado Statistical tables for biological agricultural and medical research 1 si bien su descripcion era realizada con lapiz y papel Posteriormente otros autores probablemente sin conocimiento previo de dicha publicacion elaboraron el mismo algoritmo Lincoln E Moses y Robert V Oakford en Tables of Random Permutations 2 y Durstenfeld en CACM 7 3 1964 a quienes Knuth cita en su libro The Art of Computer Programming 4 y que describe como Algoritmo P pags 139 140 del Vol 2 En la sabiduria popular este algoritmo se conoce como el barajado del sombrero ya que la descripcion que Fisher y Yates hicieron de el es la que comunmente se lleva a cabo cuando se hace una rifa por ejemplo ver los detalles mas abajo en la descripcion del mismo Transcripcion a la programacion EditarFue Durstenfeld quien primero hizo una transcripcion en la forma de algoritmo para usarse en un ordenador La descripcion de Fisher y Yates exige el uso de 2 matrices en los trabajos de campo es simplemente anotar en dos partes de un papel aunque bien podria reutilizarse borrando y rescribiendo en el mismo sitio Durstenfeld usa la propia matriz para llevar a cabo todo el algoritmo necesitando solo como memoria extra una variable temporal Descripcion del algoritmo Editar La forma mas simple de entenderlo es partir de la forma popular Se escribe cada numero en un papelito doblado construir el array se introducen todos los numeros en un sombrero se agita el contenido dentro del sombrero se barajan luego se van sacando papelitos que son dispuestos en el mismo orden en que se sacan hasta que no quede ninguno El resultado es la lista barajada La descripcion grosso modo que dan Fisher y Yates es la siguiente Se escribe en una linea los numeros del orden de la serie de 0 o 1 hasta el final de la serie se supone una serie corta manejable mediante lapiz y papel y se dispone debajo otra linea vacia Y se hace lo siguiente Se elige uno al azar Se escribe en otra linea a la derecha de los que ya haya escritos Se tacha de la linea anterior el numero salido al azar Se repiten estos 3 pasos hasta que solo quede 1 en la linea de arriba sin tachar entonces se toma directamente y se pasa a la de abajo La descripcion de Durstenfeld varia de la de Fisher y Yates en que al llevarla a cabo en un programa se trata de ahorrar memoria y por tanto trata de usar el mismo array para ello va intercambiando el valor de la posicion al azar y lo remplaza por el ultimo del array no remplazado ya Es decir al iniciar el algoritmo el primero elegido se remplaza por el ultimo en el siguiente ciclo se elige al azar entre todos menos el ultimo que ahora se remplaza por el penultimo y de nuevo se elige otro al azar entre todos menos los dos ultimos etc Basicamente Durstenfeld escribe el resultado a la izquierda del previo en tanto que Fisher Yates lo hacia a la derecha Es un error comun pensar que la aleatoriedad de la lista es dependiente del algoritmo de barajado siempre que sea correcto que para cada posicion sea elegida una posicion al azar cuando en realidad es dependiente del algoritmo de aleatoriedad pseudoaleatoriedad La lista simplemente es reordenada de otra manera siempre contiene los mismos valores solo cambian sus posiciones que son dependientes del algoritmo de aleatoriedad que no se esta implementando en ningun caso En cualquier caso una vez construido el algoritmo puede o debe probarse su imparcialidad demostrando las desviaciones de probabilidad Pseudocodigo del algoritmo Editar Esta es la descripcion del algoritmo de Durstenfeld Debe notarse que el elemento que queda al final del recorrido item 0 como entrada es el primero en la salida razon por la que no necesita ser intercambiado de posicion es la misma Algoritmo BarajadoAleatorio Entrada Un Array 0 1 2 3 4 Cantidad 1 de valores Salida El mismo array con sus valores en posiciones aleatorias Definicion de variables Cantidad Un entero que senala la cantidad total de items que tiene el array k Un entero que rige la cuenta del bucle az Un entero elegido por una funcion Random en el rango 0 k notese que k se va reduciendo tmp Un entero que ha de contener un valor para intercambiar valores entre 2 posiciones Funciones auxiliares Tamano Una funcion que devuelve la cantidad de elementos que contiene el array Random Una funcion que devuelve un numero aleatorio de un rango de valores Cantidad Tamano Array Recorrer con k desde Cantidad 1 hasta 1 Regresivamente az Random entre 0 y k tmp Array az Array az Array k Array k tmp Siguiente Aunque el coste en tiempo y memoria sea mayor el mismo algoritmo tendria el siguiente pseudocodigo resuelto con una estructura que permita inserciones y eliminaciones en posiciones arbitrarias Algoritmo BarajadoAleatorio Las variables son las mismas del caso anterior excepto que en la entrada en vez de un array se recibe una estructura por ejemplo una coleccion una lista enlazada un arbol etc Recorrer con k desde Cantidad 1 hasta 1 Regresivamente az Random entre 0 y k Estruc AnadirItem Estruc Item az Al Final Estruc BorrarItem az Siguiente Es interesante observar en este caso que los items al anadirlos al final lo hacen a la derecha del anadido anterior es decir tal como Fisher y Yates describieron Si se prefiere conservar un anadido a la izquierda del previo anadido tal como en el caso mostrado del array debe cambiarse la linea de anadido por la siguiente Estruc AnadirItem Estruc Item az En Posicion k Puesto que el bucle hace un recorrido regresivo k vale justo 1 mas de la posicion limite pedida a la funcion Random y se anade justo antes de que sea eliminado el elemento elegido por tanto k ira siendo siempre un valor menor en cada ciclo por lo que en efecto se iria colocando a la izquierda del previo Esto se puede ver mas claro en los ejemplos paso a paso en la seccion correspondiente mas abajo Variantes Editar1 El algoritmo presenta algunas variantes De hecho es bastante facil que al tratar de implementar dicho algoritmo se acabe haciendo este otro Cuya particularidad mas destacable es que siempre que se baraja se elige de nuevo entre todos los existentes es como si el sombrero tuviera posiciones donde se coloca cada uno cuando se meten y tras barajar el elemento sacado del sombrero se volviera a introducir de nuevo y se sacaran tantos como elementos hay en el sombrero y finalmente se expusiera el orden en que los elementos aparecen en el sombrero Recorrer con k desde 0 hasta Cantidad 1 az Random entre 0 y Cantidad 1 tmp Array az Array az Array k Array k tmp Siguiente 2 Tambien es posible modificar el algoritmo para solucionar una lista cuya cantidad de elementos se desconoce no tiene una cantidad fija caso tipico de una estructura que permite anadir y eliminar elementos 3 Tambien es posible modificar el algoritmo de Durnstenfeld para que opere en el orden que describe Fisher Yates siguiente a la derecha del previo Es decir los valores obtenidos se van colocando en la posicion mas baja y creciendo Notese que no hay perdida de eficiencia y notese que el recorrido le basta llegar hasta el penultimo Recorrer con k desde 0 hasta Cantidad 2 az Random entre k y Cantidad 1 tmp Array az Array az Array k Array k tmp Siguiente 4 Una variante interesante para determinados juegos es aquella capaz aun de generar todas las combinaciones posibles pero que genere algunas con mas frecuencia que otras pero todavia de forma aleatoria es decir que a pesar de ello no sea predecible Motivado para permitir un juego que provea jugadas mas interesantes y entretenidas con un reparto de cartas fichas etc no tan imparcial en cuanto a lo que sale no en cuanto a quien sale para acentuar esta caracteristica ver la 2ª parte de la seccion variaciones parametrizadas El siguiente pseudocodigo obtiene 2 numeros al azar y ambos senalan posiciones que son las que se intercambian entre si Si sucede que la posicion de origen y destino es la misma el intercambio no produce un cambio real Tambien puede suceder que una o ambas posiciones hayan salido antes ya Ambas cosas permiten que la serie no sea tan diferente entre la entrada y la salida comparada con las otras implementaciones Es facil percibir que al no usarse como indice el contador del bucle sino 2 indices elegidos al azar pueda haber posiciones que en un barajado no salga y por tanto aumenta la probabilidad de que mas de 1 elemento mantenga a la salida la misma posicion que tenia a la entrada Puede aumentarse notablemente la eficacia de lo que trata de hacer el algoritmo si el bucle solo recorre la mitad de los elementos mitad de posibilidades de que salga cada posicion He aqui el pseudocodigo de esta variante Fin Tamano Array 2 1 Recorrer con k desde 0 hasta Fin lt Solo recorre la mitad az1 Random entre 0 y Cantidad 1 az2 Random entre 0 y Cantidad 1 tmp Array az1 lt k no se refiere nunca a la posicion de un elemento en el Array az1 Array az2 lt array de esta variante como sucede en el resto de variantes Array az2 tmp Siguiente 5 Hay aun una forma diferente de concebir el algoritmo de barajado que ya no se ajusta al articulo pero que aun resulta de interes presentar aqui Sea una estructura en la que las permutaciones estan ya precalculadas y almacenadas y se usa una funcion random para elegir el indice cual de las permutaciones en la estructura es la que se entrega Esta variante sin embargo queda en la practica limitada a series cuya cantidad de elementos sean relativamente pequeno dado el espacio de memoria que consume almacenar todas la serie de permutaciones posibles Aunque cuando la velocidad es fundamental esta solucion resulta la mas apta Es ideal que una vez halladas las permutaciones diferentes estas sean barajadas para que no consten el orden creciente en que posiblemente las devuelva la funcion y que se haga lo mismo cada cierto numero de entregas Variables nuevas Series Es una estructura acorde para contener arrays donde cada array mantiene una permutacion distinta de la serie un arbol de arrays otro array de arrays etc Veces Es un contador para barajar el orden de los arrays en la estructura series cada vez que alcanza cierto valor para favorecer la aleatoriedad X Un valor limite que controla el limite de veces Este valor es dependiente de la cantidad de permutaciones Funciones accesorias Permutaciones Seria una funcion de combinatoria que devuelve una estructura de arrays con todas las series posibles donde cada array contiene una serie distinta Barajar Es un algoritmo de barajado aleatorio como los descritos con la particularidad de que cambiaria los punteros de los indices del array en vez del contenido Series Es precalculada y de nuevo cada vez que cambiare la cantidad de elementos Series Permutaciones Cantidad Funcion BarajarAleatorio veces veces 1 si veces x entonces lt x es un valor que se estalece en funcion de la cantidad de elementos del array Barajar Series veces 0 Fin si az Random entre 0 y Cantidad 1 Array Series az Devolver Array Fin funcion 6 Otra interesante variacion es el algoritmo de Sattolo Merece ser descrito mas ampliamente en la siguiente seccion Variante Sattolo Editar Fue publicado en 1986 en IPL 22 5 por Sattolo en Information Processing Letters 6 y tiene algunas particularidades que se describen Hace el barajado con la garantia de que cada elemento no ocupe la posicion a la salida que ocupaba inicialmente a la entrada Para ello se recurre a condicionar la busqueda aleatoria del elemento al rango de un elemento por debajo de la posicion actual del que se va a reemplazar Esta es la particularidad interesante de este algoritmo pero no la unica Las permutaciones que realiza una y otra vez acaban siendo ciclicas se repiten aunque no sigan un orden especifico si pueden presentarse secuencias ya aparecidas Hay permutaciones que jamas pueden ocurrir en series impares en dichas series solo alcanza a la mitad de las permutaciones si la serie entra ordenada y se ejecutan el numero suficiente de veces Por ello no es un algoritmo imparcial y debe evitarse su uso en juegos de apuestas habria la mitad de combinaciones sin ninguna posibilidad real y la otra mitad de combinaciones con el doble de posibilidades salvo que se tomen las debidas precauciones y sea estrictamente necesario que ningun elemento repita su posicion a la salida respecto de la entrada y siempre que se tenga un conocimiento expreso y exahustivo de los resultados que puede ofrecer El modo de prevenir dicho problema es garantizar que siempre se usan listas de cantidad pares anadiendo cuando sean impares un ultimo elemento que se deseche en uso Este es otro caso de algoritmo que por error se puede llegar a implementar al tratar de implementar el de Durstenfeld ya que la diferencia entra ambas implementaciones es minima Recorrer con k desde Cantidad 1 hasta 1 Regresivamente az Random entre 0 y k 1 lt EL CAMBIO k 1 en vez de k es la diferencia con Durstenfeld tmp Array az Array az Array k Array k tmp Siguiente Ya que el algoritmo para conjuntos cuya cantidad de elementos es impar evita la mitad de combinaciones puede interesar reproducir en algun momento otras permutaciones El modo de lograr otras combinaciones entre el resto de combinaciones consiste en solicitar un nuevo barajado para todo el conjunto menos 1 o varios elemento por ejemplo el ultimo o el primero el cual mantiene su posicion Es decir hay que generar una combinacion nueva que mantenega al menos 1 elemento en la misma posicion que antes Para ello habria que generar el codigo dedicado al algoritmo haciendole creer que tiene un elemento menos tal como se expresaba en una de las variantes previas Se consigue asi generar diversas series ciclicas pero no se garantiza que sean todo el resto dependiendo de cual o cuales sean los valores que se omitan en el barajado el ciclo de la serie sera mas largo o mas corto El siguiente pseudocodigo se encarga precisamente de eso Cantidad Tamano Array Si se pide OmitirUno entonces Cantidad Cantidad 1 Fin si Recorrer con k desde Cantidad 1 hasta 1 Regresivamente az Random entre 0 y k 1 tmp Array az Array az Array k Array k tmp Siguiente Que se invocaria con la siguiente llamada Llamada a la funcion BarajarAleatorio Array Sattolo OmitirUno Variaciones parametrizadas Editar Hay algunas variaciones que pueden ser aplicadas a todas las implementaciones comentadas hasta el momento bien que pueda variar el codigo exacto que deba anadirse o modificarse en cada una Implementar estas variaciones implica anadir parametros en las funciones para que al invocarlas puede elegirse el valor necesario para que cumpla la mision encomendada Una variacion aplicable a todas las implementaciones consiste en hacer un barajado dejando 1 elemento de ellos o varios seguidos sin barajar es decir quedando en la misma posicion El modo mas sencillo de llevarlo a efecto es mentir al algoritmo en cuestion haciendole creer que tiene un elemento menos asi baraja todos los que el algoritmo en cuestion baraje menos uno o esos varios Tal circunstancia puede reclamarse en un parametro opcional en la llamada a la funcion He aqui el pseudocodigo que se podria anadir a cualquiera de ellos detras se pone la implementacion de Durstenfeld para apreciar lo que implica mas arriba en Sattolo se detalla un caso que hace uso de esta variacion para generar series ciclicas pero diferente a la previa Cantidad Tamano Array Si se pide OmitirUno entonces lt OmitirUno seria aqui un parametro opcional de tipo buleano Cantidad Cantidad 1 Fin si Recorrer con k desde Cantidad 1 hasta 1 Regresivamente az Random entre 0 y k tmp Array az Array az Array k Array k tmp Siguiente La invocacion a una funcion que implementa esta variacion en el algoritmo seria Llamada a la funcion BarajarAleatorio Array Durstenfeld OmitirUno Tal y como se comentaba en la variante numerada como 4 mas arriba a menudo hay juegos donde es regla del mismo establecida asi en las reglas o acordado entre los propios jugadores que el barajado no sea excesivo suelen ser juegos de entretenimiento y no tanto de apuestas por el posible riesgo que entrana para permitir un juego mas interesante y entretenido por ejemplo un juego como el tute cabron acepta muy bien esta situacion Puede entenderse como al tiempo que los jugadores con peores cartas o fichas finalizan antes su juego sus cartas quedan acumuladas juntas al tiempo quedan en la partida los jugadores con mas triunfos que al final de la partida quedan tambien amontonadas sus cartas con lo que en la baraja quedan reunidos los triunfos por un lado y las cartas sin valor por otro de modo que al barajar escasamente los triunfos puedan aun estar algo acumulados y que el reparto de cartas en una nueva partida provea cartas significativas a un jugador u otro A veces para garantizar que esto mismo no se enturbia se procede a repartir varias cartas seguidas a cada jugador en vez de repartir a cada jugador una carta cada vez Ambas cosas favorecen que una partida produzca jugadas mas interesantes el algoritmo de dicha variante se halla mas arriba como variante 4 Podemos pedir a un algoritmo que cada x elementos del recorrido omita y barajados es el pseudocodigo que se presenta a continuacion En este sentido puede proveerse una variacion a los algoritmos proporcionados para favorecer ese escaso barajado en esta ocasion tomamos la variante que mejor conjunta con esta causa Entrada Un Array 0 1 2 3 4 Cantidad 1 de valores Grupo un entero que senala cada cuantos se evita un barajado Salto un entero que indica cuantos seguidos quedan sin barajar Cantidad Tamano Array Fin Cantidad 2 1 Recorrer con k desde 0 hasta Fin lt Solo recorre la mitad Si k es congruente con Grupo entonces gt congruente con refiere a la operacion modulo que se traduce como si k mod grupo 0 luego k k salto En otro caso az1 Random entre 0 y Cantidad 1 az2 Random entre 0 y Cantidad 1 tmp Array az1 Array az1 Array az2 lt k no se refiere nunca a la posicion de un elemento en el array de esta variante Array az2 tmp Fin si Siguiente La invocacion a una funcion que implementa esta variacion en el algoritmo seria Llamada a la funcion BarajarAleatorio Array Semibarajado Grupo 13 Salto 3 Supongamos una baraja de 52 cartas y supongamos que queremos que cada 13 cartas que baraje se salte 3 Siendo el array de 0 a 51 elementos en las posiciones k modulo 13 que son 0 13 26 y 39 al llegar a dichas posiciones aumentara k en 3 unidades que en efecto tiene como consecuencia no barajar 3 elementos seguidos como son 4 veces 4 grupos de 13 hay 12 barajados menos Tambien pueden especificarse los parametros como los que se omiten y los que se barajan en el ejemplo serian Barajan 10 y se Omiten 3 y la suma de ambos seria el equivalente al grupo asi se evita la necesidad de comprobar que salto sea menor que grupo Debe notarse que esta variacion no evita que los valores en esas posiciones se mantenga en su mismo sitio pueden salir elegidas por la funcion Random en cualquier otro instante del recorrido del bucle pero si tiene por efecto que 12 elementos de los 52 no sean barajados lo que por supuesto tendra incidencia en una cierta similitud entre la serie a la salida y la serie de entrada Todas las implementaciones puede ser tambien facilmente modificadas para hacer el barajado solo con un subconjunto del rango del array no a todos senalando un parametro de inicio del elemento que se empieza a barajar y otro de la cantidad de elementos afectados Esto tambien puede aplicarse a listas extremadamente largas donde la ocupacion de la memoria pueda ser critica o el tiempo necesario para disponer de todo el array barajado no es aceptable Dicha modificacion puede solucionar el problema Es imaginable un array de mil millones de elementos donde solo se modifica 1 millon de elementos consecutivos cada vez e incluso una modificacion donde se modifica 1 millon de elementos a una distancia de 1000 elementos entre uno y otro el 1 el 1001 2001 3001 4001 1001001 cada vez Todavia todas las implementaciones admiten aun otra parametrizacion para hacer un barajado por bloques lo que comunmente en los juegos de cartas se llama cortar que intercambia bloques de cartas y donde los elementos individuales en cada bloque mantiene su posicion En las cartas al ser barajadas a mano cada bloque tiene un tamano indeterminado e independiente del otro Hay dos formas tipicas de cortar Cortar en 2 montones de diferente tamano con las manos uno se pone encima del que antes estaba arriba y se repite el proceso varias veces Otras veces sobre el tapete se reparten pequenos montones de cantidades desiguales por lo general ya que se hace a mano y se recogen de la mesa en orden diferente al que se ha repartido Basicamente este es el pseudocodigo que se expone a continuacion con la salvedad de que se especifica un tamano de bloque que se ajusta para todos excepto quizas el ultimo monton La funcion recibiria un parametro senalando el tamano del bloque a considerar Funcion BarajadoAleatorio Metodo BarajadoEnBloques Cantidad Tamano Array Grupos Cantidad Bloque lt es una division entera desprecia decimales Si Cantidad modulo Bloque es mayor que 0 entonces sumar 1 a Grupos En la posibilidad de que el ultimo grupo no este completo se intercambia aparte Es decir intercambiamos primero el ultimo bloque por el que bloque elegido al azar Az Random entre 0 y Grupos 1 SiAzes distinto de Grupos 1 entonces lt si el grupo al azar es el ultimo no hace falta intercambiarlo p Az Bloque Recorrer con k desde Grupos 1 bloque hasta Cantidad 1 tmp Array p Array p Array k Array k tmp p p 1 Siguiente Fin si gt aqui va el inserto que se senala unos parrafos mas abajo lt Grupos Grupos 1 Recorrer con J desde Grupos 1 hasta 1 Regresivamente Az Random entre 0 y J SiAzes distinto de J entonces lt si el grupo al azar es el mismo del bucle no hace falta intercambiarlo p Az Bloque n Az Bloque Recorrer con k desde n hasta n Bloque 1 tmp Array p Array p Array k Array k tmp p p 1 Siguiente Fin si Siguiente Y la invocacion a la funcion seria asi Llamada a la funcion BarajarAleatorio Array BarajadoEnBloques Bloque 4 Imaginese un mazo de 28 cartas y bloques de 4 cartas lo que nos daria 7 montones y seria equivalente a barajar una serie de 7 elementos 7 4 28 si los montones fueran de 6 cartas el ultimo monton solo tendria 4 cartas luego cuando saliera este solo se intercambiarian 4 cartas con el bloque que se intercambia Se pone una serie de resultados de 28 elementos con un bloque de 4 para darse cuenta como el afecta a un barajado de esta manera Observeses como las tuplas de 4 elementos se ha coloreado un par de ellas no cambian entre si el orden de sus elementos 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 24 25 26 27 20 21 22 23 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 16 17 18 19 00 01 02 03 20 21 22 23 24 25 26 27 04 05 06 07 08 09 10 11 12 13 14 15 00 01 02 03 24 25 26 27 12 13 14 15 16 17 18 19 04 05 06 07 08 09 10 11 20 21 22 23 04 05 06 07 16 17 18 19 20 21 22 23 24 25 26 27 12 13 14 15 00 01 02 03 08 09 10 11 Si el bloque produce grupos exactos entonces no generara todas las permutaciones posibles de hecho ni aunque haya bloques no exactos no generara todas las permutaciones posibles Por ejemplo para una serie de 7 elementos que permite 5040 permutaciones distintas de la serie con un bloque de 2 genera 144 permutaciones con un bloque de 3 genera solo 12 permutaciones con un bloque de 4 5 y 6 solo hay 2 permutaciones posibles ya que solo ofrece 2 grupos Por ello hay que considerar que el numero de permutaciones obedece mas al numero de grupos y los elementos en el ultimo grupo que a la cantidad de elementos Por ultimo senalar que si el tamano de bloque es 1 equivale a barajar todos los items cual si no se usara dicha funcionalidad Hay que considerar que la entrada y la salida varian poco entre si pero dado que tras la salida hay un nuevo juego la siguiente entrada tendra una permutacion muy distinta de la salida previa Si el caso es que se desea que haya una mayor posibilidad de permutaciones podria barajarse el ultimo bloque que antes estaba en otra posicion y se intercambio con el ultimo de modo que en sucesivas veces cuando sea cambiado por el bloque en otra posicion ira generando una mayor cantidad de permutaciones aunque varien poco de una a otra en el tiempo Esto puede ir especificado con otro parametro o quedar fijo en la variacion del algoritmo El pseudocodigo seria el siguiente y se colocaria justos donde aparece esta linea gt aqui va el inserto que se senala unos parrafos mas abajo lt Barajar los elementos internos del que ahora es el ultimo bloque Min Grupos 1 Bloque Recorrer con k desde Cantidad 1 hasta Min Regresivamente lt el barajado es metodo Durstenfeld en este ejemplo pero podria ser otro Az Random entre Min y k lt Min limita el rango inferior en vez de 0 tmp Array k Array k Array Az Array Az tmp Siguiente Fijese en las siguientes salidas como es barajado siempre el bloque que se intercambio a la ultima posicion y como desde entonces ese bloque se traslada asi con ese nuevo orden entre sus elementos a la siguiente entrada sin cambios tras la salida Para estudiar el caso aqui la salida de una serie es la entrada de la siguiente caso que se supone que no sera cierto una vez se traslade a una aplicacion real que tras un barajado habra un juego tal que acaben sus elementos en otro orden Se marca en azul el bloque que sera el ultimo en la siguiente etapa y se observa de rojo ese bloque en la siguiente fase posicionado el ultimo y ya barajado Ese bloque asi barajado mantiene su nuevo orden interno en lo sucesivo 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 lt entrada 04 05 06 07 20 21 22 23 08 09 10 11 24 25 26 27 12 13 14 15 00 01 02 03 19 16 17 18 19 16 17 18 04 05 06 07 24 25 26 27 08 09 10 11 20 21 22 23 00 01 02 03 14 15 13 12 04 05 06 07 24 25 26 27 20 21 22 23 19 16 17 18 14 15 13 12 08 09 10 11 02 01 00 03 02 01 00 03 14 15 13 12 08 09 10 11 20 21 22 23 19 17 16 18 07 04 07 05 26 25 24 27 14 15 12 13 02 08 09 10 11 01 00 03 19 17 16 18 07 04 07 05 20 21 22 23 25 27 24 26 lt el ultimo bloque se intercambia consigo mismo esta vez Como se puede apreciar esta variacion tambien permite generar permutaciones aptas para cuando se desea generar jugadas interesantes y entretenidas en juegos que lo necesitan y en cambio no es aceptable en juegos con apuestas Usos EditarComo ya se ha dicho en el encabezado del articulo es el algoritmo que se usa tipicamente para barajar en los juegos de azar o alguna de sus variantes Se usa tambien en los modelos estadisticos cuando se requiere simular un origen de datos aleatorio Un caso practico para probar la eficiencia de los algoritmos de ordenamiento hacer simulaciones del clima etc Tambien se usa en sistemas y programas donde se desea usar una sola vez cada elemento de la serie pero de una forma aleatoria sin recurrir a una estructura adicional de almacenamiento Se provee un ejemplo al detalle sobre las listas de reproduccion de canciones Reproduccion aleatoria de los elementos de una lista Editar El presente algoritmo resuelve tambien este caso sin la necesidad de recurrir a una segunda estructura donde mover los elementos retirados de la primera Imaginemos el caso mas corriente una lista de canciones de la que se desea reproducir cada cancion una sola vez pero que lo hagan en orden aleatorio Inicialmente se tendra una lista con las rutas de las canciones lista que conviene dejar intacta no intercambiar sus elementos Se usa un array cuyo contenido inicialmente son los indices de las canciones en la lista Si el orden es aleatorio se manda barajar el array si el orden solicitado es el original se reasigna a cada indice el valor del indice de la lista el orden natural correlativo La reproduccion finalmente usara como indice el valor de dicho array En vez de invocar Reproducir Ruta k se invocara Reproducir Ruta Array k asi la lista original no necesita ser tocada ni duplicada para mantener el orden original es mas rapido en memoria asignar valores numericos que textos Si la lista se desea reproducir en la forma original Necesitamos recrear el orden original Recorrer con k desde 0 hasta Cantidad 1 Array k k Siguiente Llamada a la funcion ReproducirLista Si la lista se desea reproducir en forma aleatoria Desordenamos el array con el algoritmo Llamada a la funcion BarajarAleatorio Array Llamada a la funcion ReproducirLista Y la funcion ReproducirLista seria esta Recorrer con k desde 0 hasta Cantidad 1 Reproducir Ruta Array k SiguienteEjemplos paso a paso con comentarios EditarSe iran exponiendo ejemplos paso a paso de las implementaciones mas relevantes sobre una serie de 7 elementos en tablas con columnas cuyo significado son Ciclo identifica a la variable que controla el bucle cuando es descendente ira de 6 a 0 cuando sea creciente ira de 0 a 6 Su valor se colorea de rojo Rango Rango de posiciones elegible en cada instante Azar La posicion que ha salido al azar Su valor se colorea de azul Antes El orden que mantiene la serie antes de aplicar la operacion actual Despues El orden que mantiene la serie tras aplicar la operacion Resultado Alguna tabla muestra una columna adicional para mas claridad Su valor se colorea de rojo Otros detalles En la tabla que corresponda se separa la serie con para mayor claridad Aquellos campos que no precisen ser rellenados se senalan con Las tablas se completan visualmente hasta su termino aun cuando no necesite un ciclo final para acabar de verlo claro Cuando ambas posiciones columnas ciclo y azar se refieran a la misma el valor se colorea de purpura El contenido del campo Despues de un ciclo es el campo Antes del siguiente ciclo que se repite para mejor seguimiento Tabla paso a paso implementacion Durstenfeld Editar Para el algoritmo descrito en el pseudocodigo Durstenfeld Notese las siguientes cuestiones para este ejemplo Los intercambios son entre valores de las posiciones ciclo y azar Notese especialmente como los valores van colocandose a la derecha del separador que se ha puesto para mejor visibilidad Hay que fijarse tambien que aunque han salido 3 veces al azar la posicion 0 ello no tiene influencia significativa en el resultado ya que se refiere a una posicion y cada vez sera intercambiado por un valor en una posicion distinta El ciclo 2 tanto la posicion de ciclo como la de azar refieren a la posicion 2 luego el valor se intercambia con si mismo En esta implementacion no es preciso hacer el ciclo 0 no hay necesidad de barajar si solo hay 1 elemento entre los que escoger por tanto se queda el valor que yace en ese momento en dicha posicion y se ahorra un ciclo en el bucle Ciclo Rango Azar Antes Despues 0 1 2 3 4 5 6 0 1 2 3 4 5 6 6 0 6 4 0 1 2 3 4 5 6 0 1 2 3 6 5 45 0 5 0 0 1 2 3 6 5 4 5 1 2 3 6 0 44 0 4 0 5 1 2 3 6 0 4 6 1 2 3 5 0 43 0 3 2 6 1 2 3 5 0 4 6 1 3 2 5 0 42 0 2 2 6 1 3 2 5 0 4 6 1 3 2 5 0 41 0 1 0 6 1 3 2 5 0 4 1 6 3 2 5 0 4 0 0 1 6 3 2 5 0 4 1 6 3 2 5 0 4 Tabla paso a paso implementacion Fisher Yates Editar Para el algoritmo descrito en el pseudocodigo como variante de la implementacion de Durstenfeld posicionando los elementos a la derecha del previo tal como describieron Fisher Yates Notese las siguientes cuestiones para este ejemplo Las posiciones al azar no pueden ser iguales que en el ejemplo previo ya que a medida que aumenten los ciclos no pueden salir los valores de ciclos ya pasados El intercambio siempre se hara entre el valor de la posicioon elegida al azar y el primero de la serie que entra en juego Se ha puesto como separador para apreciar donde empieza los que entran en juego El resultado va quedando a la derecha de la serie ver en el encabezado los a la derecha de la serie en juego Ciclo Rango Azar Antes Despues 0 1 2 3 4 5 6 0 1 2 3 4 5 60 0 6 5 0 1 2 3 4 5 6 5 1 2 3 4 0 61 1 6 3 5 1 2 3 4 0 6 5 3 2 1 4 0 62 2 6 6 5 3 2 1 4 0 6 5 3 6 1 4 0 23 3 6 4 5 3 6 1 4 0 2 5 3 6 4 1 0 24 4 6 5 5 3 6 4 1 0 2 5 3 6 4 0 1 25 5 6 6 5 3 6 4 0 1 2 5 3 6 4 0 2 1 6 6 5 3 6 4 0 2 1 5 3 6 4 0 2 1 Tabla paso a paso implementacion Fisher Yates original Editar Para la descripcion del algoritmo original empleando para ello lapiz y papel es decir tachando de la lista en una linea y escribiendo los resultados en una nueva linea que sera la lista resultante Esto supone simular 2 estructuras una que contiene la lista original de la que se extraen y otra donde se insertan Notese las siguientes cuestiones para este ejemplo Se emplean los mismos valores de resultado que para el ejemplo anterior lo que fuerza a elegir otros valores de azar para que coincidan ambos resultados Aparte de diferenciarse en la representacion respecto del anterior y en los valores de la columna Azar tambien cambian los valores de la columna rango pues se sigue el esquema de sacar del sombrero por lo que en cada ciclo contiene uno menos entre los que elegir Fijarse en como el valor en la posicion libre que senala azar se tacha en la lista despues y pasa al resultado Los items tachados no se cuentan ya al indizar la lista no existen en la tabla se dejan para poder comparar los cambios Ciclo Rango Azar Antes Despues Resultado 0 1 2 3 4 5 6 0 1 2 3 4 5 6 0 1 2 3 4 5 60 0 6 5 0 1 2 3 4 5 6 0 1 2 3 4 5 6 51 0 5 3 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 32 0 4 4 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 63 0 3 3 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 44 0 2 0 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 05 0 1 1 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 0 26 0 0 0 1 2 3 4 5 6 0 1 2 3 4 5 6 5 3 6 4 0 2 1Tabla paso a paso implementacion Sattolo Editar Para la descripcion del algoritmo Sattolo variante de Durstenfeld Notese las siguientes cuestiones para este ejemplo No puede emplearse los mismos valores columna Azar que en aquella tabla ya que el rango de posiciones elegible es en cada ciclo uno menor que alli ver columna rango Notese la gran diferencia con durstenfeld la columna rango se ve reducida a un item menos entre los que elegir al azar respecto de la columna ciclo Dado que lo interesante de esta implementacion es la capacidad del algoritmo para no repetir valores en la posicion inicial operar con 7 elementos no seria suficiente para verlo claramente por ello se amplia la lista a 10 elementos Notese como el valor de la columna Azar nunca iguala el valor de la columna Ciclo ver en pseudocodigo el condicionante del rango en la funcion Random Azar Random entre 0 y k 1 Ciclo Rango Azar Antes Despues 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 9 0 8 6 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 9 7 8 68 0 7 4 0 1 2 3 4 5 9 7 8 6 0 1 2 3 8 5 9 7 4 67 0 6 4 0 1 2 3 8 5 9 7 4 6 0 1 2 3 7 5 9 8 4 66 0 5 1 0 1 2 3 7 5 9 8 4 6 0 9 2 3 7 5 1 8 4 65 0 4 1 0 9 2 3 7 5 1 8 4 6 0 5 2 3 7 9 1 8 4 64 0 3 3 0 5 2 3 7 9 1 8 4 6 0 5 2 7 3 9 1 8 4 63 0 2 0 0 5 2 7 3 9 1 8 4 6 7 5 2 0 3 9 1 8 4 62 0 1 1 7 5 2 0 3 9 1 8 4 6 7 2 5 0 3 9 1 8 4 61 0 0 0 7 2 5 0 3 9 1 8 4 6 2 7 5 0 3 9 1 8 4 6 2 5 0 3 9 1 8 4 6 2 7 5 0 3 9 1 8 4 6Resultados de una serie de llamadas consecutivas Editar En la siguiente tabla se muestran los resultados de una serie de llamadas a la funcion para apreciar como en efecto no se repiten observar verticalmente la columna resultados nunca un elemento en la misma posicion en la siguiente llamada Para esta tabla se han realizado 12 llamadas y se prescindido de los pasos internos de la funcion Y para la ocasion se ponen las columnas siguientes Llamada El indice indica la enesima llamada a la funcion BarajadoAleatorio para la implementacion de Sattolo Azar Contiene la lista de posiciones aleatorias obtenida internamente en esa llamada por si desea recrearse manualmente siguiendo el esquema de la tabla previa Entrada y Salida Son columnas que muestran la lista a la entrada y la salida de la funcion La lista de la columna Salida de una llamada a la funcion es la misma lista que se consigna en la columna Entrada de la siguiente llamada Notese como en la columna Azar verticalmente siempre los valores que salieron son menores que el numero que indica el encabezado se ha coloreado toda la sub columna encabezada por el 8 de color rojo para apreciarse mejor pero se aplica a todas ellas La posicion horizontal en dicha columna Azar indica el valor obtenido por la funcion Random en cada ciclo interno del bucle dado el rango en el que se ve obligado a elegir Apreciese como en la columna Salida ningun valor se repite en la misma posicion que tenia en la llamada anterior se ha coloreado de azul verticalmente la sub columna 6ª para dirigir mejor la vista pero se aplica a todas ellas Una tabla similar con cualquiera de las otra implementaciones arrojaria muchas coincidencias Llamada Azar Entrada Salida 9 8 7 6 5 4 3 2 1 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 91 6 4 4 1 1 3 0 1 0 0 1 2 3 4 5 6 7 8 9 2 7 5 0 3 9 1 8 4 62 0 3 6 4 1 3 2 0 0 2 7 5 0 3 9 1 8 4 6 9 8 6 5 4 7 3 1 0 23 4 6 0 3 2 1 1 1 0 9 8 6 5 4 7 3 1 0 2 7 1 0 2 8 6 5 9 3 44 7 6 4 5 4 0 2 1 0 7 1 0 2 8 6 5 9 3 4 2 3 1 0 7 4 6 8 5 94 0 7 4 0 2 0 0 1 0 2 3 1 0 7 4 6 8 5 9 4 0 3 5 6 1 9 7 8 26 2 3 2 5 4 1 0 0 0 4 0 3 5 6 1 9 7 8 2 9 7 8 4 0 6 1 2 5 37 3 3 4 1 3 0 0 1 0 9 7 8 4 0 6 1 2 5 3 8 6 1 2 9 5 7 0 3 48 8 2 5 2 1 3 1 1 0 8 6 1 2 9 5 7 0 3 4 7 8 9 0 2 6 4 5 1 39 5 5 6 5 0 2 2 0 0 7 8 9 0 2 6 4 5 1 3 8 0 5 2 9 7 1 4 3 610 4 3 2 2 1 0 0 1 0 8 0 5 2 9 7 1 4 3 6 1 3 7 6 8 0 4 5 2 911 3 3 1 2 1 2 1 0 0 1 3 7 6 8 0 4 5 2 9 2 8 1 0 4 5 7 3 9 612 4 3 0 4 2 3 1 1 0 2 8 1 0 4 5 7 3 9 6 5 3 7 8 9 1 6 2 0 4Test de aleatoriedad sobre las implementaciones EditarVease tambien EditarAleatoriedad Entropia informacion Numero aleatorio Pruebas de aleatoriedad Secuencia pseudoaleatoria Teoria de probabilidad Variable aleatoriaReferencias Editar Fisher Ronald A Yates Frank 1948 1938 Statistical tables for biological agricultural and medical research 3rd edicion Londres Oliver amp Boyd pp 26 27 OCLC 14222135 Nota la 6ª edicion ISBN 0 02 844720 4 es disponible a traves de la red pero ofrece un algortmo de barajado algo diferente por C R Rao Moses Lincoln E Oakford Robert V 1963 1963 Tables of Random Permutations 1ª edicion Stanford California Stanford University Press pp ISBN 0 8047 0148 2 doi 10 1145 364520 364540 Knuth Donald Erwin 1969 1981 2ªed Seminumerical algorithms The Art of Computer Programming 2 Reading MA Addison Wesley pp 124 125 139 140 en 2ª ed ISBN 0 201 03822 6 doi 10 1016 0020 0190 86 90073 6 Sandra Sattolo 25 de junio de 1985 An algorithm to generate a random cyclic permutation Un algoritmo para generar una permutacion ciclica aleatoria En Elsevier B V ed Istituto di Matematica Facolta di Scienze Matematiche e Fisiche Universita degli Studi di Udine via P Mantica 33100 Udine Italy Information Processing Letters 30 mayo 1986 22 6 315 317 Datos Q6522952Obtenido de https es wikipedia org w index php title Algoritmo de Fisher Yates amp oldid 136585254, wikipedia, wiki, leyendo, leer, libro, biblioteca,

español

, española, descargar, gratis, descargar gratis, mp3, video, mp4, 3gp, jpg, jpeg, gif, png, imagen, música, canción, película, libro, juego, juegos