Parfois, vous avez un ensemble d'éléments - par exemple, des chaînes, des nombres, des objets, peu importe - dont vous souhaitez randomiser l'ordre. Ceci est particulièrement utile pour les quiz et les jeux de hasard, mais est utile dans toutes sortes d'autres applications. La méthode la plus simple que j'ai trouvée pour cela consiste à coller tous les objets dans un tableau, puis à le mélanger comme un jeu de cartes. Mais comment pouvons-nous faire cela? ?
Comme exemple simple, nous allons utiliser les lettres de l'alphabet:
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", "W", "X" "," Y "," Z "];
Il existe différentes approches que nous pouvons adopter pour trier réellement ce tableau.
Nous pourrions créer un deuxième tableau et copier chaque élément du premier dans une position aléatoire dans le deuxième:
Le code correspondant à ceci pourrait ressembler à ceci (voir cette astuce sur l'obtention d'un entier aléatoire dans une plage spécifique pour plus de détails):
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", "W", "X" "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; pour (var i: int = 0; i < letters.length; i++) randomPos = int(Math.random() * letters.length); shuffledLetters[randomPos] = letters[i];
Mais il y a un énorme problème. Et si la position aléatoire choisie pour C
est 6 aussi?
Ici, UNE
est écrasé et ne sera donc pas dans le tableau mélangé. Ce n'est pas ce que nous voulons, nous devons donc vérifier que l'emplacement est vide avant de copier la lettre et choisir un emplacement différent si ce n'est pas le cas:
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", "W", "X" "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; pour (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];
Ça marche. Le problème, c'est que c'est inefficace. Voir, la lettre UNE
est garanti pour tenir dans la fente choisie, parce que c'est la première lettre choisie, de sorte que toutes les fentes seront vides. Avec B
, il y a 25 chances sur 26 que le premier emplacement choisi soit vide. Quand nous sommes à mi-chemin de l'alphabet, cette chance tombe à 50/50. Au moment où nous atteignons V
, il y a une chance sur 50 que nous ne trouvions pas une fente vide jusqu'à ce que le Quatrième tentative.
Cela signifie que nous sommes très susceptibles d'appeler Math.random ()
wayyyyy plus de 26 fois. Et Math.random ()
est une fonction relativement lente. Quelle autre approche pouvons-nous adopter??
Et si on stockait une liste de tous les emplacements vides dans un troisième tableau, qui rétrécirait que nous sommes passés à travers eux?
? et ainsi de suite, en répétant jusqu'à ce que le tableau d'emplacements vides ne contienne plus d'éléments. Le code pour cela ressemblerait à ceci:
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", "W", "X" "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var emptySlots: Array = new Array (); pour (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);
Ici, nous utilisons la méthode Array.splice () pour supprimer un seul élément de la liste des emplacements vides. Cela supprime réellement l'élément, plutôt que de simplement définir sa valeur sur nul
; donc, après avoir épissé la première fente, emptySlots.length
sera 25
plutôt que 26
.
Ce épissure()
la fonction est excellente; nous pouvons l'utiliser pour une troisième approche du brassage qui découpe ce tableau d'homme intermédiaire.
Au lieu de supprimer des éléments du tableau d'emplacements vides lorsque nous en avons terminé, nous pourrions les supprimer du tableau d'origine non brassé..
Cela ne semble pas très utile au début - mais si on choisissait des éléments du original tableau au hasard, au lieu de choisir leur les destinations au hasard?
? et ainsi de suite, jusqu'à ce que le premier tableau ne contienne aucun élément.
Contrairement aux deux autres approches, nous nous retrouvons sans le tableau d'origine. Que ce soit un problème ou non dépend de ce projet.
Le code pourrait ressembler à ceci:
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", "W", "X" "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; pour (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);
En fait, depuis épissure()
renvoie un Tableau
de tous les éléments que vous épissez, nous pourrions simplifier un peu le code:<
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", "W", "X" "," Y "," Z "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; pour (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
C'est mieux. J'ai une autre approche à partager; celui-ci est très différent des autres que nous avons utilisés jusqu'à présent.
Les tableaux ont une méthode, sort (), qui réorganise par défaut tous les éléments du tableau en ordre alphanumérique croissant, comme suit:
var mixedLetters: Array = ["G", "B", "P", "M", "F"]; mixedLetters.sort (); // mixedLetters est maintenant ["B", "F", "G", "M", "P"]
Vous pouvez passer des options à cette méthode, comme Tableau.DESCENDING
, ce qui inverse l'ordre du tri:
var mixedLetters: Array = ["G", "B", "P", "M", "F"]; mixedLetters.sort (Array.DESCENDING); // mixedLetters est maintenant ["P", "M", "G", "F", "B"]
(Il existe d'autres options et vous pouvez en passer autant que vous le souhaitez.)
Vous pouvez également transmettre une référence à un une fonction, ce qui indique à Flash comment décider dans quel ordre deux éléments doivent appartenir. Par exemple, cette fonction peut être utilisée pour le tri numérique:
function numericalSort (a: Number, b: Number): Nombre if (a < b) return -1; if (a == b) return 0; if (a > b) retourne 1;
Flash examine chaque paire d'éléments adjacents dans le tableau et les réorganise en fonction de cette fonction: il les échange si la valeur 1
est retourné, et les laisse seuls autrement. (Sauf si vous passez Tableau.DESCENDING
ainsi que la fonction, auquel cas il les échange si la valeur -1
est renvoyé et les laisse seuls sinon.) Flash répète ceci dans tout le tableau encore et encore jusqu'à ce que toutes les paires reviennent 0
ou -1
(0
ou 1
si vous utilisez Tableau.DESCENDING
).
Nous pouvons jouer avec cela. Au lieu de lui donner une véritable raison pour échanger deux éléments, nous pouvons simplement lui demander de les échanger au hasard, en utilisant une fonction de tri comme celle-ci:
function randomSort (a: *, b: *): Number // * signifie tout type d'entrée if (Math.random () < 0.5) return -1; else return 1;
Facile! Maintenant nous pouvons l’utiliser dans notre code comme ceci:
function randomSort (a: *, b: *): Nombre 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)
Veuillez noter que le tableau résultant ne sera pas aussi aléatoire que les autres approches que nous avons utilisées - dans cette approche, il n'y a pas une chance sur 26 que tout la lettre donnée finira dans tout emplacement donné. En effet, nous n'échangeons que des paires d'éléments adjacentes et ne faisons rien d'autre que cela. Pourtant, c'est une méthode soignée :)
Il y a beaucoup d'autres méthodes, je sais. Vous avez mieux que ceux que vous aimez?
Modifier: Voici un excellent article avec quelques visualisations très cool, expliquant le shuffle Fisher-Yates, qui fonctionne à la place. Je le recommande fortement!