Newsgroup: it.comp.lang.visual-basic
Titolo: [VB6] Estrazione random da un array senza ripetizioni

Ciao a tutti, non sono un programmatore professionista, e vi chiedo
aiuto per questo problema:
- ho un array A monodimensionale di 156 elementi
- devo proporre all'utente, uno per uno, randomicamente, tutti i 156
elementi dell'array A
- il programma non deve mai ripetere un elemento già proposto
- quando tutti i 156 elementi dell'array A sono stati proposti, il
gioco si azzera e riparte
Praticamente è come se pescassi i 90 numeri della Tombola, sono random,
e non c'è possibilità che uno di essi esca due volte. Finiti i numeri,
si ricomincia da zero.
Per l'estrazione random è stato semplice, ma per far sì che gli
elementi non si ripetessero ho trovato delle difficoltà.
Ho creato un array B di appoggio (sempre monodimensionale e di 156
elementi) in cui vengono memorizzati tutti gli elementi di A
precedentemente proposti (una specie di storico).
Ad ogni nuova "pescata" nell'array A, il programma fa uno scan
dell'array B, esaminandone solo gli elementi registrati (quindi non
tutti e 156) e, nel caso ci trovi un elemento già "pescato" in A,
effettua una nuova pescata (a cui segue un nuovo scan, e così via).
Il problema è che quando si raggiunge il centinaio di pescate fatte in
A, gli elementi pescabili (quelli rimanenti in A stesso) si riducono; a
sua volta lo scan di B diventa più lento perché nel frattempo esso si è
riempito. Peggio ancora, i tentativi di pescaggio eseguiti dal loop
sopra descritto si moltiplicano a dismisura, perché la funzione random
pesca da A senza pregiudizi: non c'è un'ottimizzazione che gli dice
"pesca solo tra gli elementi rimanenti). Insomma, il risultato è che
l'applicazione "tende" ad instaurare un loop infinito.
Come posso rimediare?
Ciao e grazie! ^__^
-- 
Xander (ver. 0.9 beta) - ICQ # 21481462
Per rispondermi via e-mail togli i "3" e ".invalid" dal mio indirizzo.
"It's time to kick ass and chew bubble gum! And I'm all outta gum."
(Duke Nukem)

 
Guarda qui:
http://www.visualbasicforum.com/showthread.php?s=ed8d6cb37000b748dd091aebf0311839&t=76270&highlight=random+numbers
ciao
	maxx
-- 
NOSPAM: Rimuovere i trattini dallo username!
Cut hyphens from my username!

 aiuto per questo problema:
...
Private Sub Form_Load()
    Dim sacchetto(1 To 156) As Long
    Dim estratti(1 To 156) As Boolean
    Dim n As Integer
    Dim e As Integer
    Randomize Timer
    For n = 1 To 156
        sacchetto(n) = Fix(Rnd * 10 ^ 4)
        estratti(n) = False
    Next n
    For n = 1 To 156
        Do
            e = Fix(Rnd * 10 ^ 3)
            Select Case e
                Case 1 To 156
                    If estratti(e) = False Then
                        estratti(e) = True
                        Call MsgBox("Estratto " & n & "=" & sacchetto(e))
                        Exit Do
                    End If
            End Select
        Loop
    Next n
    Unload Me
End Sub

 elementi dell'array A
Taglio
In considerazione che il numero da trovare è piccolo:
1- add una ListBox
2- numero randomico
3- verifico se è presente nella lista
3.1- no > la add
3.2- si > avanzo
4- verifico se il numero di items della lista è 156
4.1- no > richiamo il metodo
4.2- si > exit metodo
Ave
giuseppeD

 gioco si azzera e riparte
Uhm, in realtà ho sbagliato a parlare di array A monodimensionale.
L'array A è bidimensionale, ma definito in questo modo:
Dim ArrayA(1 To 6, 0 To 24)
Per cui, i suoi elementi sono in totale 156.
Ora mi leggo quello che mi avete scritto, speriamo di riuscire a
capirlo ;-)


Tempo addietro nacque una discossuine proprio su un thread di questo 
tipo, e termipilucco postò una soluzione ottima.
Ora, sinceramente, non ricordo come fece... ma la base era questa:
Hai il vettore V(1 to 156).
MAX=156
Ripeti
	Peschi un numero casuale N: 1<=N<=MAX
	Il numero pescato è V(N)
	Poni V(N)=V(MAX)
	Poni MAX=MAX-1
Finchè MAX=1
V(1) è l'ultimo elemento estraibile.
-- 
Rivolterò il mondo per trovarlo, e anche se andasse in Indocina uno dei 
nostri starà nascosto in una ciotola di riso pronto a sparargli nel culo.
(Pulp Fiction)

 Per cui, i suoi elementi sono in totale 156.
No comment sul sistema di memorizzazione degli elementi (in base 1 per un
indice, in base 0 per l'altro).
In ogni caso il metodo piu' veloce in assoluto di estrarre dei numeri e'
mettere tutti gli elementi in una Collection (monodimensionale quindi),
generare un numero Random fra 1 e Collection.Count ed estrarre/rimuovere
l'elemento con quell'indice dalla Collection, il tutto finche'
Collection.Count > 0.
In questo modo i "numeri" estratti non devono essere necessariamente uguali
all'indice del numero stesso.
hth,
Lokar
-- 
Sito Comune: 
http://www.it-lang-vb.net/
Manifesto: 
http://www.it-lang-vb.net/docs/Manifesto.txt
Statuto: 
http://www.it-lang-vb.net/docs/Statuto.txt
UGIdotNET - 
http://www.ugidotnet.org

 Per cui, i suoi elementi sono in totale 156.
In realta' sono 150 =)


> Dim ArrayA(1 To 6, 0 To 24)
Scusa il taglio quoting ma a quest'ora ho poca voglia di fare taglia e cuci
=)
Ti do un sistema alternativo per mantenere l'indicizzazione (se ti serve)
della matrice:
E' possibile rappresentare una matrice in un vettore sequenziale (e' quello
che fanno tutti i compilatori alle nostre spalle :P)
Ammettiamo che il tuo Array sia di Booleani (una specie di battaglia
navale).
E i numeri da estrarre (da 1 a 150) li mettiamo in una Collection.
Detti X ed Y gli indici per l'Array avremo:
<code>
Private Const BASE_X As Long = 1
Private Const MAX_X As Long = 6
Private Const BASE_Y As Long = 0
Private Const MAX_Y As Long = 24
Private Scacchiera() As Boolean
Private Sacchetto As Collection
</code>
Ecco le funzioni che dati gli indici restituiscono la posizione sequenziale
e viceversa:
<code>
Private Function IndexFromArrayElement(ByVal X As Long, ByVal Y As Long) As
Long
    'IN -> X, Y: Indici dell'elemento nella matrice
    'OUT -> Posizione sequenziale dell'elemento nella matrice
    IndexFromArrayElement = (X - BASE_X) * (MAX_Y - BASE_Y + 1) + (Y -
BASE_Y) + 1
End Function
Private Sub ArrayElementFromIndex(ByVal Pos As Long, ByRef X As Long, ByRef
Y As Long)
    'IN -> Index: Posizione sequenziale dell'elemento nella matrice
    'IN -> X, Y: Indici dell'elemento della matrice
    X = Int((Pos - 1) / (MAX_Y - BASE_Y + 1)) + BASE_X
    Y = ((Pos - 1) Mod (MAX_Y - BASE_Y + 1)) + BASE_Y
End Sub
</code>
Per comodita' ho fatto restituire la posizione in base 1, visto che le
collection sono in base 1.
Inizializziamo il gioco:
<code>
Private Sub InitGioco()
    Dim i As Long
    'Inizializziamo la scacchiera (tutti False)
    ReDim Scacchiera(BASE_X To MAX_X, BASE_Y To MAX_Y) As Boolean
    'Inizializziamo i numeri (da 1 a 150)
    Set Sacchetto = New Collection
    For i = 1 To IndiceDaElemento(MAX_X, MAX_Y)
        Call Sacchetto.Add(i)
    Next i
    'Inizializziamo il generatore di numeri casuali
    Call Randomize(Time)
End Sub
<\code>
Una funzioncina di appoggio (per eleganza) per vedere se ci sono ancora
numeri da estrarre:
Private Function FineEstrazioni() As Boolean
    FineEstrazioni = CBool(Sacchetto.Count = 0)
End Function
Ora la funzione per estrarre un numero:
<code>
Private Function EstraiNumero() As Long
    Dim idx As Long
    'Se la Collection e' vuota restituisce 0
    If Sacchetto.Count = 0 Then
        Exit Function
    End If
    'Estrae un indice della Collection
    idx = Int(Rnd() * Sacchetto.Count) + 1
    'Restituisce il numero che si trova in quella posizione
    'nella Collection
    EstraiNumero = Sacchetto.Item(idx)
    'Rimuove l'elemento estratto
    Call Sacchetto.Remove(idx)
End Function
</code>
Una "manche" ultraveloce:
<code>
Private Sub Gioca()
    Dim X As Long
    Dim Y As Long
    Dim Pos As Long
    'Inizializza il tutto
    Call InitGioco
    'Finche' non finiscono i numeri
    Do Until FineEstrazioni
        'Estrai il numero
        Pos = EstraiNumero
        'Ricava la posizione corrispondente nella matrice
        Call ElementoDaIndice(Pos, X, Y)
        'Invalida la cella!
        Scacchiera(X, Y) = False
    Loop
    'GAME OVER =)
End Sub
</code>
Spero che il gioco non sia fine a se stesso =)
Potrebbe farti comodo una funzioncina per forzare l'uscita di un numero in
base coordinate. Eccola qua:
<code>
Private Sub ForzaUscita(ByVal X As Long, ByVal Y As Long)
    Dim v As Variant    'Per il ciclo
    Dim idx As Integer  'Da rimuovere
    Dim Uscente As Long 'Valore uscente
    'Ricaviamo il valore dell'elemento uscente
    'dagli indici dell'elemento della matrice
    Uscente = IndiceDaElemento(X, Y)
    'Un piccolo trucco per ciclare piu' in fretta:
    'For Each insieme ad un indice incrementato manualmente
    For Each v In Sacchetto
        idx = idx + 1
        If CLng(v) = Uscente Then
            'Trovato!
            Call Sacchetto.Remove(idx)
            Scacchiera(X, Y) = False
            Exit Sub
        End If
    Next v
End Sub
</code>
Spero che il codice postato ti sia utile anche per imparare qualcosa e non
ti limiti ad un mero copia-incolla =)
La versione a oggetti piu' in la' 8-)


 Dim ArrayA(1 To 6, 0 To 24)
Versione rivista e corretta per array a due dimensioni:
Dim ArrayA(1 To 6, 0 To 24)
Dim v1 As Long
Dim v2 As Long
Dim r1 As Long
Dim r2 As Long
Randomize
v1 = UBound(ArrayA)
v2 = UBound(ArrayA, 2)
While v1 <> LBound(ArrayA) - 1
    r1 = Int(Rnd * v1) + LBound(ArrayA)
    r2 = Int(Rnd * v2) + LBound(ArrayA, 2)
    'Debug.Print r1, r2
    MsgBox ArrayA(r1, r2)  ' numero estratto
    ArrayA(r1, r2) = ArrayA(v1, v2)
    v2 = v2 - 1
    If v2 = LBound(ArrayA, 2) - 1 Then
        v2 = UBound(ArrayA, 2)
        v1 = v1 - 1
    End If
Wend
termoPilucco
Inviato da 
www.mynewsgate.net

 Collection.Count > 0.
Carino (non conoscevo l'oggetto Collection) ma mi tocca riscrivere un
bel po' di cose... Grazie del suggerimento cmq!
Ciao!


 In realta' sono 150 =)
Si hai ragione, ma avevo il cervello lesso ieri sera ;-)


 ti limiti ad un mero copia-incolla =)
Grazie infinite, quanto ci avrai messo a scrivere tutto quel codice? 30
secondi? ;-) Sei forte.
Ciao!
