2016-02-26 19 views

risposta

2

Questa è una cosa che penso sguardi più o meno bello, ma non è così efficiente come può essere:

yourCollection asSet asOrderedCollection shuffled first: numberOfElements 
+0

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_). –

5

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 chiamato aGenerator. 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 il seed 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 almeno anInteger 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.

+1

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". –

+0

@ AmosM.Carpenter buon punto. Quello che uso è più vicino al secondo approccio. –

+1

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. –