Come posso ottenere in modo elegante un numero specifico (> 1) di elementi casuali distinti da una raccolta?Come ottenere un numero specifico di elementi casuali da una raccolta in Smalltalk?
risposta
Questa è una cosa che penso sguardi più o meno bello, ma non è così efficiente come può essere:
yourCollection asSet asOrderedCollection shuffled first: numberOfElements
Si consideri il seguente frammento di codice
sample: anInteger from: aCollection using: aGenerator
| sample |
sample := Set new: anInteger.
[sample size = anInteger]
whileFalse: [ | element |
element := aCollection atRandom: aGenerator.
sample add: element].
^sample asArray
Alcune osservazioni
Generatore esplicito: utilizza esplicitamente un determinato generatore, ad esempio un'istanza di
Random
, che ho chiamatoaGenerator
. Per motivi matematici, se stai ricevendo campioni per la tua applicazione, tutti dovrebbero usare lo stesso generatore per tutto il tuo programma. Anche questo ti darà un ulteriore vantaggio: salva e successivamente ripristina ilseed
e sarai in grado di riprodurre un precedente comportamento "casuale" del tuo sistema, che è buono per il test.Nessun controllo la disponibilità: il codice non verifica che è possibile ottenere il campione desiderato, che sarebbe il caso se
aCollection
non hai almenoanInteger
elementi diversi.Codice classe: il metodo dovrebbe passare a una classe.
Ad esempio:
Random >> sample: anInteger from: aCollection
| sample |
sample := Set new: anInteger.
[sample size = anInteger]
whileFalse: [ | element |
element := aCollection atRandom: self.
sample add: element].
^sample asArray
UPDATE
Ecco un altro approccio:
Random >> remove: anInteger from: aCollection
| sample |
sample := OrderedCollection new: anInteger.
anInteger timesRepeat: [| index element |
index := aCollection size atRandom: self.
element := aCollection removeAt: index.
sample add: element].
^sample
commento
Di solito succede che quando vogliamo campionare senza ripetizione, vogliamo anche rimuovere gli elementi dalla collezione mentre li selezioniamo casualmente. In questi casi, ciò che spesso accade è che la collezione è nota per non avere ripetizioni.
Hmm, mi piace l'approccio "generatore di byo", ma se la raccolta e la dimensione del campione sono grandi, potresti rimanere bloccato fino alla fine, aspettando un po 'che il generatore scelga un numero "libero". –
@ AmosM.Carpenter buon punto. Quello che uso è più vicino al secondo approccio. –
Mi spiace essere nitpicky, ma non dovresti usare '#removeIndex:' - è pensato per essere privato. L'uso del metodo pubblico '#removeAt:' invece avrebbe il vantaggio aggiuntivo di rispondere all'elemento rimosso, il che significa che è possibile eliminare la variabile temporanea extra 'element' (ad esempio, basta aggiungere" esempio: (aCollection removeAt: index) ') .Entrambi i metodi "remove" si trovano su "OrderedCollection", quindi non funzionerà con altri tipi di collezioni. –
Molto elegante. L'unico modo che potevo vedere per migliorare l'efficienza (che in genere non sarebbe un problema, ma alcune persone ne restano appese ...) sarebbe di mescolare gli _indices_ invece della raccolta (perché ci sarebbero meno indici) , e poi usa qualcosa come "#atAll:" per scegliere quegli indici randomizzati da un set (avresti ancora bisogno di # # asSet se li vuoi _distinct_). –