Estrarre dati casuali (testo) da elenco, con limite di
cifra. -
dal 04/09/04 pagina vista:
volte
Da un'altra
domanda nasce questo esercizio per estrarre dati casuali in formato testo
(nomi, codici alfanumerici, ecc.), con la fine routine estrazione basata su un
valore numerico limite, predefinito. L'esercizio si può prestare ad una molteplicità di
situazioni, compreso, per la gestione magazzino di un'azienda, di
un'estrazione di articoli da mettere in offerta speciale, casualizzando gli
articoli sulla chiave codice articolo e con un massimo di cifra in offerta,
oppure, perchè no, nell'estrazione casuale di nomi della cabala, con i loro
numeri, per giocate al lotto. Comunque sia. esaminiamo il problema postomi:
Da una tabella
formata almeno da una colonna di valori testo e da una colonna di valori
numerici correlati al testo, vogliamo estrarre il testo e i numeri collegati, randomizzando l'estrazione
sul testo. Vogliamo inoltre che l'estrazione si
interrompa quando il totale dei valori numerici estratti insieme al nome,
abbia raggiunto o superato un valore determinato. Vogliamo anche creare una
nuova tabella con i valori estratti. Vediamo come procedere:
Premessa: per
realizzare una randomizzazione dovremo usare un numero, non è possibile
farlo col testo; il numero però ci viene fornito dal numero di righe dove è
presente il testo, e quindi per prima cosa dovremo identificare la colonna
dove è presente il testo da casualizzare, e poi contarne le righe. Per
identificare la colonna possiamo ricorrere a sistemi diversi:
-
UsedRange - Se l'elenco è uniforme sul foglio
di lavoro, possiamo ricorrervi per definire tutta la tabella, purchè
l'ultima riga dell'area comprenda anche l'ultima cella in basso col nome da
estrarre, altrimenti otterremo che il numero di righe sia falsato, e poi
usare l'indice colonna dove si trova il testo.
-
Riferimento preciso alla colonna che contiene i
nomi da estrarre, es. Range("A1:A200"). Questa
soluzione è più sicura, ma offre lo svantaggio che variazioni (aggiunte) di
dati in tabella, ci costringe a modificare il codice vba per quanto riguarda
il riferimento all'intervallo.
-
End - Metodo ottimale per reperire esattamente
l'ultima cella occupata; usato con la costante xlDown (verso il basso), ma richiede
che tutte le celle della colonna da esaminare contengano valori, altrimenti
con una cella vuota nella colonna tra altri valori, si interrompe il
reperimento di tutta la colonna, esempio: Range([A1],
[A1].End(xlDown)).
Una volta
impostata la zona, ne conteremo le righe, il numero ottenuto ci servirà come
numero da casualizzare (randomizzare, è lo stesso). La randomizzazione ci
fornirà quindi un numero di riga che servirà:
-
a reperire il
testo contenuto nella cella della riga estratta.
-
a reperire il
valore numerico nella cella a lato, stessa riga
-
useremo quindi un
totalizzatore che iniziando a zero, sommi tutti i valori numerici mano a
mano associati al testo estratto.
-
ogni valore testo
e relativo valore numerico associato estratto, lo copieremo in un'altra
zona, formando una tabella con gli estratti
Per poter ripetere
l'estrazione, ci affideremo ad un ciclo For...Next,
che iniziando da 1 e fino al valore rappresentato dal numero di righe,
provveda a garantire la continuazione delle estrazioni. E' in questo ciclo
che istruiremo la condizione che SE raggiunto o superato il valore da noi
scelto, si uscirà dal ciclo interrompendo l'estrazione. Va detto subito, che
l'uscita dal ciclo, per quanto riguarda il rispetto della condizione,
dipende solamente dagli importi che verranno sommati: sarà infatti difficile
che si verifichi la condizione precisa scelta. Vediamo subito un immagine
che spiega meglio:
nell'esempio che
propongo, la cifra oltre la quale si interromperà il ciclo, è 20, ma come
vediamo, i primi due numeri estratti sono 12 e 6, che fanno 18, quindi il
ciclo continua, il terzo numero è 8 che porta il totale a 26 e quindi si
esce. Solo se fosse uscito un 2 come terzo numero, avremmo avuto 20. La
routine ha lavorato perfettamente. siamo usciti se il totale fosse stato 20
o superiore. Vediamo le istruzioni: (i commenti in verde potranno essere
tolti, servono solo come spiegazione)
Sub AcasoTuo()
'come si nota nella foto,
il range D1:E1 fino a completamento elenco, è la zona dove 'faccio
copiare i dati estratti, per cui ad inizio routine identifico l'area
con End, e la 'pulisco (ClearContents) per consentire il refresh dei
nuovi dati. Chi volesse invece 'continuare ad accodare dati, dovrà
eliminare le tre righe sottostanti:
If [D1] <> "" Then
Range([D1], [E1].End(xlDown)).ClearContents
End If
'con "zona" imposto
con UsedRange, tutta l'area contenente dati
Set zona = ActiveSheet.UsedRange
'con "x" ottengo da quante
righe e formato l'elenco "zona"
x = zona.Rows.Count
'imposto un totalizzatore a
zero
tot = 0
'inizio il ciclo For Next
che ripete le istruzioni comprese nel ciclo, da 0 fino al valore di x
For N = 0 To x
'Per evitare la
ripetitività nella sequenza dei valori restituiti dovremo adoperare la
funzione 'Randomize che utilizza numero per inizializzare il
generatore di numeri casuali della 'funzione Rnd assegnandogli un
nuovo valore. Se numero viene omesso, il valore 'restituito dal timer
di sistema verrà utilizzato come nuova base.
'Se Randomize non viene utilizzata, quando la funzione Rnd (senza
argomenti) viene 'chiamata per la prima volta, utilizza come base lo
stesso numero. Per le chiamate 'successive la funzione utilizzerà
l'ultimo numero generato.
Randomize
'ora assegniamo alla
variabile "quale" un numero di riga generato casualmente e basato 'sul
numero rappresentato da "x", e sarà questa variabile che verrà usata
per reperire la 'cella da cui "prendere" il valore (insieme all'indice
colonna rappresentato dal numero 'nell'istruzione Cells(quale, 1),
Cells(quale, 2))
quale = Int(x * Rnd) + 1 'si aggiunge 1
nel caso esca zero
'con le variabili Y
prendiamo il testo, e con Z il numero nella cella accanto
Y = Cells(quale, 1).Value
Z = Cells(quale, 2).Value
'ora incrementiamo il
totalizzatore con il valore rappresentato da Z
tot = tot + Z
'fatto questo ci spostiamo
nella zona prevista per creare la "tabella estratti" (la D1, con 'iRow
impostato alla riga 1 e la colonna impostata a 4 Cells(iRow, 4)), e
cerchiamo la 'prima cella libera a partire da D1, con il ciclo While...Wend
Dim iRow As Integer
iRow = 1
While Cells(iRow, 4) <> ""
iRow = iRow + 1
Wend
'trovata la cella libera,
si rende uguale al valore di Y (il testo) e di Z (il numero a lato)
Cells(iRow, 4).Value = Y
Cells(iRow, 5).Value = Z
'ora si controlla se il
totalizzatore è uguale o maggiore del valore previsto (nel'es.: 20) se
'sarà vera la condizione prevista, si lancia un messaggio col valore
raggiunto
If tot >= 20 Then
MsgBox tot
'con Exit For si esce dal
ciclo interrompendolo, e quindi dalla routine
Exit For
End If
Next
End Sub |
Lavorando sulle
condizioni per uscire dal ciclo, sarà possibile crearsi condizioni
personalizzate, anche multiple. Poichè nella routine sopra, per effetto del
ciclo For..Next la funzione Randomizze, richiamata ad ogni ciclo, può
comunque generare numeri già usciti (e quindi stessi nomi e relativi
valori), si può decidere di modificare le istruzioni inserendo un controllo
che impedisca una doppia uscita degli stessi valori: colorando di giallo le
celle estratte ed uscire ripetendo l'estrazione se il numero estratto
corrisponde ad una cella gialla. Ad ogni lancio di routine si ripristina il
colore di base. Questa la routine, con le spiegazioni solo alle modifiche:
Sub AcasoGiallo()
If [D1] <> "" Then
Range([D1], [E1].End(xlDown)).ClearContents
'si rispristina il colore
delle celle della colonna A (i nomi)
Range([A1], [A1].End(xlDown)).Interior.ColorIndex = xlNone
End If
Set zona = ActiveSheet.UsedRange
x = zona.Rows.Count
tot = 0
10: 'indice riga codice
For N = 1 To x
Randomize
quale = Int(x * Rnd)
'estratto il numero, si
controlla se la cella è gialla, in questo caso si torna all'indice
riga 10 'e si ripete il ciclo di randomizzazione
If Cells(quale, 1).Interior.ColorIndex = 6 Then GoTo 10
'altrimenti si prosegue
Y = Cells(quale, 1).Value
'si colora di giallo la
cella estratta, colonna 1
Cells(quale, 1).Interior.ColorIndex = 6
Z = Cells(quale, 2).Value
tot = tot + Z
Dim iRow As Integer
iRow = 1
While Cells(iRow, 4) <> ""
iRow = iRow + 1
Wend
Cells(iRow, 4).Value = Y
Cells(iRow, 5).Value = Z
If tot >= 20 Then
MsgBox tot
Exit For
End If
Next
End Sub |
Con queste
varianti si avrà la certezza di non duplicare gli estratti. Un'immagine
dell'estrazione:
Credo che sia
tutto ben spiegato, buon lavoro.
prelevato sul sito
www.ennius.altervista.org
|