Alcuni utenti mi hanno chiesto se esiste un modo per ottenere con Excel una sequenza di valori casuali non ripetuti. Infatti la funzione =casuale() restituisce si dei valori casuali ma essi si ripetono.
Ovviamente è possibile farlo e ci possono essere diverse soluzioni. La soluzione che propongo è una funzione personalizzata che ho chiamato =estrai_numero(minimo; massimo) che inserita in una cella genera un numero casuale intero tra un valore minimo e massimo specificati senza ripeterlo finchè non ha generato tutti quelli possibili tra il minimo ed il massimo, dopodiché ricomincia con un’altra sequenza casuale diversa da quella precedente.
Ad esempio, se inserendo nelle seguenti celle:
- A1 =estrai_numero(1;4) otterrai un numero intero tra 1 e 4, ad esempio 3
- A2 = estrai_numero(1;4) otterrai uno di questi numeri 1,2 e 4 ma non 3 (ad es. 2)
- A3 = estrai_numero(1;4) otterrai uno di questi numeri 1 e 4 ma non 3 e 2 (ad es. 4)
- A4 = estrai_numero(1;4) otterrai il numero 1
- A5 = estrai_numero(1;4) otterrai un numero tra 1 e 4, (es. 2) perché la sequenza di prima è finita e ne è stata generata un’altra diversa dalla precedente…
Il codice è il seguente, da inserire in un modulo e che ho commentato a scopo didattico:
'* '* La funzione estrai_numero restituisce un numero casuale compreso tra '* un valore minimo e massimo senza restituire più volte lo stesso '* numero fino alla fine della sequenza numerica. Finita la sequenza '* generata ne crea una nuova, casuale e diversa dalla precedente e '* così via. '* '* Il codice è a scopo didattico e sei libero di utilizzarlo dove desideri '* a patto di non rimuovere l'autore originale: '* (c) Fabio Ottaviani 2009 '* web: https://www.chicercatrova2000.it '* per segnalare eventuali problemi: debug@chicercatrova2000.it '* '* Aggiornato il 15 Novembre 2009 '* Versione 0.1 '* Global deck_array() As Integer Global indice As Integer Global ultimo_range As String Public Function estrai_numero(ByVal n_min As Integer, ByVal n_max As Integer) As String Dim attuale_range As String attuale_range = CStr(n_min) & ":" & CStr(n_max) '* '* se l'array non è mai stato inizializzato oppure è cambiato il range di valori richiesto '* oppure sono stati estratti tutti i valori... '* If indice >= n_max Or Not (ultimo_range = attuale_range) Then '* '* ...lo inizializzo e lo riempio con i valori dal minimo al massimo richiesti... '* ReDim deck_array(n_min To n_max) For n = n_min To n_max deck_array(n) = n Next '* '* ... poi ne mischio i valori richiamando la funzione mischia(n_volte)... '* mischia ((n_max - n_min) * 10) indice = n_min ultimo_range = attuale_range Else '* '* ...altrimenti incremento l'indice... '* indice = indice + 1 End If '* '* ...e restituisco il valore puntato dalla variabile indice. '* estrai_numero = CStr(deck_array(indice)) End Function Private Function mischia(ByVal volte As Integer) '* '* Subroutine che esegue la mischiatura dei valori nell'array deck_array() inizializzato. '* La tecnica utilizzata è quella che sceglie due variabili dell'array a caso '* e ne scambia i valori contenuti, ripetendo un certo numero di volte l'array '* risulterà mischiato '* Randomize Dim min As Integer Dim max As Integer Dim tmp As Integer Dim a As Integer Dim b As Integer min = LBound(deck_array) max = UBound(deck_array) For n = 1 To volte a = min + Int((max - min + 1) * Rnd()) b = min + Int((max - min + 1) * Rnd()) tmp = deck_array(b) deck_array(b) = deck_array(a) deck_array(a) = tmp Next deck_array(min) = -deck_array(min) '* Per indicare il primo elemento della sequenza metto in negativo il primo valore End Function
Per semplicità potete scaricare il foglio di excel con la funzione ed il codice qui. Per renderlo comprensibile il più possibile ho evitato di inserire troppi controlli sugli errori ed ho evitato di perfezionarlo con altre caratteristiche aggiuntive. Le uniche accortezze che ho inserito è che il primo elemento di una sequenza è in negativo così è possibile individuarlo e nel caso si cambino i valori minimo e massimo si generi una nuova sequenza.
Enjoy!