Suggerimento rapido come mescolare in modo casuale una matrice in AS3

A volte hai una serie di elementi - potrebbero essere stringhe, potrebbero essere numeri, potrebbero essere oggetti, qualunque - il cui ordine vuoi randomizzare. Questo è particolarmente utile per quiz e giochi d'azzardo, ma è utile in tutte le altre applicazioni. Il metodo più semplice che ho trovato per fare questo è quello di incollare tutti gli elementi in una matrice e poi mischiarla come un mazzo di carte. Ma come possiamo farlo? ?

Come semplice esempio, utilizzeremo le lettere dell'alfabeto:

 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 "];

Esistono diversi approcci che possiamo adottare per ordinare effettivamente questo array.


L'approccio ingenuo

Potremmo creare un secondo array e copiare ogni elemento del primo in una posizione casuale nel secondo:

Il codice potrebbe essere simile a questo (consulta questo suggerimento rapido per ottenere un numero intero casuale all'interno di un intervallo specifico per ulteriori dettagli):

 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 "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; per (var i: int = 0; i < letters.length; i++)  randomPos = int(Math.random() * letters.length); shuffledLetters[randomPos] = letters[i]; 

Ma c'è un grosso problema. Cosa succede se la posizione casuale scelto C è anche il 6?

Qui, UN viene sovrascritto e quindi non si troverà nell'array mescolato. Non è quello che vogliamo, quindi dobbiamo verificare che lo slot sia vuoto prima di copiare la lettera e scegliere uno slot diverso, se non lo è:

 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 "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: int = 0; per (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]; 

Che funzioni. Il problema è che è inefficiente. Vedi, la lettera UN è garantito per adattarsi allo slot selezionato, perché è la prima lettera scelta, quindi tutti gli slot saranno vuoti. Con B, c'è una probabilità di 25 su 26 che il primo slot scelto sia vuoto. Quando siamo a metà dell'alfabeto, questa possibilità scende a 50/50. Nel momento in cui raggiungiamo V, c'è una possibilità 50/50 che non troveremo uno spazio vuoto fino al il quarto tentativo.

Questo significa che molto probabilmente chiameremo Math.random () wayyyyy più di 26 volte. E Math.random () è una funzione relativamente lenta. Qual è un altro approccio che possiamo prendere?


L'approccio dell'uomo medio

Cosa succede se abbiamo memorizzato un elenco di tutti gli slot vuoti in a terzo array, che si ridurrebbe man mano che li attraversavamo?

? e così via, ripetendo fino a quando l'array di slot vuoti non contiene elementi. Il codice per questo sarebbe simile a questo:

 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 "]; var shuffledLetters: Array = new Array (letters.length); var emptySlots: Array = new Array (); per (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); 

Qui usiamo il metodo Array.splice () per rimuovere un singolo elemento dall'elenco degli slot vuoti. Questo rimuove effettivamente l'elemento, piuttosto che impostarne il valore nullo; quindi, dopo aver giuntato il primo slot, emptySlots.length sarà 25 piuttosto che 26.

Questo splice () la funzione è grande; possiamo usarlo per un terzo approccio allo shuffling che elimina questo array di middle man.


L'approccio splicing

Invece di rimuovere elementi dalla matrice di slot vuoti quando abbiamo finito con loro, potremmo rimuoverli dall'array originale non rimosso.

All'inizio non sembra molto utile, ma cosa succede se abbiamo scelto elementi dal originale array a caso, invece di scegliere il loro destinazioni a caso?

? e così via, fino a quando il primo array non contiene elementi.

A differenza degli altri due approcci, finiamo senza l'array originale. Se questo è un problema o no dipende da questo progetto.

Il codice potrebbe assomigliare a questo:

 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 "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; per (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); 

In effetti, da allora splice () restituisce un schieramento di tutti gli elementi che stai impiombando, potremmo semplificare un po 'il codice:<

 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 "]; var shuffledLetters: Array = new Array (letters.length); var randomPos: Number = 0; per (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 

È più ordinato. Ho un altro approccio da condividere; questo è molto diverso dagli altri che abbiamo usato finora.


L'approccio di smistamento

Gli array hanno un metodo, sort (), che per impostazione predefinita riorganizza tutti gli elementi dell'array in ordine alfanumerico crescente, in questo modo:

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

Puoi passare opzioni a questo metodo, come Array.DESCENDING, che inverte l'ordine del tipo:

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

(Ci sono altre opzioni e puoi passarne quante ne vuoi.)

Puoi anche passare un riferimento a a funzione, che indica a Flash come decidere in quale ordine appartengano due elementi. Ad esempio, questa funzione potrebbe essere utilizzata per l'ordinamento numerico:

 function numicalSort (a: Number, b: Number): Number if (a < b) return -1; if (a == b) return 0; if (a > b) ritorno 1; 

Flash esamina ogni coppia di elementi adiacenti nell'array e li riorganizza in base a questa funzione: li scambia se il valore 1 viene restituito e li lascia da soli altrimenti. (A meno che tu non passi Array.DESCENDING così come la funzione, nel qual caso lo scambia se il valore -1 viene restituito e li lascia da soli.) Flash lo ripete su tutto l'array più e più volte finché tutte le coppie non ritornano 0 o -1 (0 o 1 se si utilizza Array.DESCENDING).

Possiamo scherzare con questo. Invece di dargli una vera ragione per cui due elementi dovrebbero essere scambiati, possiamo semplicemente dirgli di scambiarli a caso, usando una funzione di ordinamento come questa:

 function randomSort (a: *, b: *): Number // * indica qualsiasi tipo di input if (Math.random () < 0.5) return -1; else return 1; 

Facile! Ora possiamo usarlo nel nostro codice in questo modo:

 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)

Si prega di notare che la matrice risultante non sarà mescolata casualmente come gli altri approcci che abbiamo usato - in questo approccio, non c'è nemmeno una probabilità 1/26 che qualunque la lettera data finirà in qualunque dato slot. Questo perché scambiamo solo coppie di elementi adiacenti e non facciamo altro. Eppure, è un metodo pulito :)

Ci sono molti altri metodi, lo so. Hai qualcosa che ti piace meglio di questi?

Modificare: Ecco un ottimo post con alcune visualizzazioni molto interessanti, che spiegano il rimescolamento di Fisher-Yates, che funziona sul posto. Lo consiglio vivamente!