Consejo rápido Cómo mezclar aleatoriamente una matriz en AS3

A veces tienes un conjunto de elementos (podrían ser cadenas, podrían ser números, podrían ser objetos, lo que sea), cuyo orden quieras aleatorizar. Esto es particularmente útil para pruebas y juegos de azar, pero es útil en todo tipo de aplicaciones. El método más fácil que he encontrado para hacer esto es pegar todos los elementos en un Array y luego barajarlos como un mazo de cartas. Pero como podemos hacer eso? ?

Como un simple ejemplo, usaremos las letras del alfabeto:

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "];

Hay diferentes enfoques que podemos tomar para ordenar realmente esta matriz.


El enfoque ingenuo

Podríamos crear una segunda matriz y copiar cada elemento de la primera en una posición aleatoria en la segunda:

El código para eso podría tener este aspecto (consulte este Consejo rápido para obtener un número entero aleatorio dentro de un rango específico para obtener más detalles):

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; para (var i: int = 0; i < letters.length; i++)  randomPos = int(Math.random() * letters.length); shuffledLetters[randomPos] = letters[i]; 

Pero hay un gran problema. ¿Qué pasa si la posición aleatoria elegida para do es 6 tambien?

aquí, UNA se sobrescribe y, por lo tanto, no estará en la matriz barajada. Eso no es lo que queremos, por lo que debemos verificar que la ranura esté vacía antes de copiar la carta, y elegir una ranura diferente si no lo está:

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; para (var i: int = 0; i < letters.length; i++)  randomPos = int(Math.random() * letters.length); while (shuffledLetters[randomPos] != null) //repeat as long as the slot is not empty  randomPos = int(Math.random() * letters.length); //pick a different slot  shuffledLetters[randomPos] = letters[i]; 

Eso funciona. El problema es que es ineficiente. Mira la carta UNA está garantizado para encajar en la ranura elegida, porque es la primera letra elegida, por lo que todas las ranuras estarán vacías. Con segundo, hay una posibilidad de 25 en 26 de que la primera ranura elegida esté vacía. Cuando estamos a mitad del alfabeto, esta posibilidad se reduce a 50/50. Cuando lleguemos V, hay una probabilidad de 50/50 de que no encontremos una ranura vacía hasta que la cuarto intento.

Esto significa que es muy probable que estemos llamando Math.random () Wayyyyy más de 26 veces. Y Math.random () Es una función relativamente lenta. ¿Qué otro enfoque podemos tomar??


El enfoque del hombre medio

¿Qué pasa si almacenamos una lista de todas las ranuras vacías en un tercero matriz, que se reduciría a medida que avanzamos a través de ellos?

? y así sucesivamente, repitiendo hasta que la matriz de ranuras vacías no contenga elementos. El código para eso se vería así:

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var emptySlots: Array = new Array (); para (var n: int = 0; n < letters.length; n++)  emptySlots.push(n);  var randomPos:Number = 0; var actualSlot:Number = 0; for (var i:int = 0; i < letters.length; i++)  randomPos = int(Math.random() * emptySlots.length); //note emptySlots.length not letters.length actualSlot = emptySlots[randomPos]; shuffledLetters[actualSlot] = letters[i]; emptySlots.splice(randomPos, 1); 

Aquí usamos el método Array.splice () para eliminar un solo elemento de la lista de ranuras vacías. Esto elimina realmente el elemento, en lugar de simplemente establecer su valor en nulo; así, después de empalmar la primera ranura, emptySlots.length estarán 25 más bien que 26.

Esta empalme() la función es genial; Podemos usarlo para un tercer enfoque para barajar que corta esta matriz de intermediarios.


El enfoque de empalme

En lugar de eliminar elementos de la matriz de ranuras vacías cuando hayamos terminado con ellos, podríamos eliminarlos de la matriz original sin modificaciones.

Esto no suena muy útil al principio, pero, ¿y si seleccionáramos elementos del original matriz al azar, en lugar de elegir su destinos al azar?

? Y así sucesivamente, hasta que la primera matriz no contiene elementos..

A diferencia de los otros dos enfoques, terminamos sin la matriz original. Si esto es un problema o no depende de este proyecto.

El código podría verse así:

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; para (var i: int = 0; i < shuffledLetters.length; i++) //use shuffledLetters.length because splice() will change letters.length  randomPos = int(Math.random() * letters.length); shuffledLetters[i] = letters[randomPos]; //note this the other way around to the naive approach letters.splice(randomPos, 1); 

De hecho, desde empalme() devuelve un Formación De todos los elementos que está empalmando, podríamos simplificar un poco el código:<

 letras var: Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; para (var i: int = 0; i < shuffledLetters.length; i++)  randomPos = int(Math.random() * letters.length); shuffledLetters[i] = letters.splice(randomPos, 1)[0]; //since splice() returns an Array, we have to specify that we want the first (only) element 

Eso es más ordenado. Tengo un enfoque más para compartir; este es muy diferente a los otros que hemos usado hasta ahora.


El enfoque de clasificación

Las matrices tienen un método, sort (), que de forma predeterminada reorganiza todos los elementos de la matriz en orden alfanumérico ascendente, de este modo:

 var mixedLetters: Array = ["G", "B", "P", "M", "F"]; mixedLetters.sort (); // mixedLetters es ahora ["B", "F", "G", "M", "P"]

Puede pasar opciones a este método, como Array.DESCENDING, que invierte el orden de la clase:

 var mixedLetters: Array = ["G", "B", "P", "M", "F"]; mixedLetters.sort (Array.DESCENDING); // mixedLetters ahora es ["P", "M", "G", "F", "B"]

(Hay otras opciones, y puedes pasar tantas como quieras).

También puede pasar una referencia a un función, que le dice a Flash cómo decidir en qué orden pertenecen los dos elementos. Por ejemplo, esta función podría usarse para ordenar numéricamente:

 función numericalSort (a: Number, b: Number): Number if (a < b) return -1; if (a == b) return 0; if (a > b) devuelve 1; 

Flash examina cada par de elementos adyacentes en la matriz y los reorganiza de acuerdo con esta función: los intercambia si el valor 1 Se devuelve, y los deja solos de lo contrario. (A menos que pase Array.DESCENDING así como la función, en cuyo caso los intercambia si el valor -1 se devuelve y, de lo contrario, los deja solos.) Flash repite esto en toda la matriz una y otra vez hasta que regresan todos los pares 0 o -1 (0 o 1 si usa Array.DESCENDING).

Podemos meternos con esto. En lugar de darle una razón genuina por la que se deben intercambiar dos elementos, podemos decirle que los intercambie al azar, usando una función de clasificación como esta:

 function randomSort (a: *, b: *): Number // * significa cualquier tipo de entrada if (Math.random () < 0.5) return -1; else return 1; 

¡Fácil! Ahora podemos usarlo en nuestro código así:

 function randomSort (a: *, b: *): Number if (Math.random () < 0.5) return -1; else return 1;  var letters:Array = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]; letters.sort(randomSort); //(no need for the shuffledLetters[] Array)

Tenga en cuenta que la matriz resultante no se mezclará de forma tan aleatoria como los otros enfoques que hemos utilizado. En este enfoque, no existe una posibilidad de 1/26. alguna carta dada terminará en alguna Ranura dada. Esto se debe a que solo estamos intercambiando pares de elementos adyacentes y no haciendo más que eso. Aún así, es un método limpio :)

Hay muchos otros métodos, lo sé. Tienes alguno que te guste más que estos?

Editar: Aquí hay un gran post con algunas visualizaciones muy interesantes, que explica el shuffle de Fisher-Yates, que funciona en su lugar. Lo recomiendo altamente!