Italian Stringa di lettere e numeri che non si ripetono

Tony.

Member
Salve a tutti, io ho una stringa di lettere e numeri "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ" esclusi lo 0 e la O, e vorrei che questa stringa, riempia una label con 9 valori diversi, senza ripetersi. Per spiegare meglio, io premo un tasto AVVIA e si genera una stringa tipo "AN2H5LE6D" quindi una substringa con valori tutti diversi. La substringa la vorrei creare di 9 caratteri, qualcuno mi può aiutare a capire come potrei fare? Se ci riesco a fare una cosa del genere, mi piacerebbe anche provare a far sì che substringa contenga 3 numeri e 6 lettere, purché in questa substringa non siano mai uguali. Grazie mille e spero di essermi spiegato.
 

MicroDrie

Well-Known Member
Licensed User
Quello che vuoi ottenere è chiaro per noi, ma ti dimentichi di dirci cosa farai con quella stringa unica di caratteri di 3 e 9 caratteri.

In Olanda c'è un proverbio che dice che molte strade portano a Roma. Non è completamente chiaro quale strada hai seguito per una soluzione. La domanda è: puoi mostrare cosa hai ottenuto finora in un programma e dove sei bloccato.
 

Star-Dust

Expert
Licensed User
Longtime User
Vuole creare una specie di codice casuale in cui non ci siano due caratteri alfanumerici uguali all'interno.
Comunque non spiega se i 3 numeri si devono trovare sempre nella stessa posizione oppure ogni codice i 3 numeri li deve disporre in punti diversi della stringa di 9.

Siccome non è un utente registrato, appaiono i messaggi dopo che Erel li approva, quindi forse domani sapremo la risposta (E falla una donazione di 5€ e ti togli il pensiero)
 

LucaMs

Expert
Licensed User
Longtime User
In Olanda c'è un proverbio che dice che molte strade portano a Roma.
Veramente non è un proverbio olandese; inoltre, dice che "Tutte le strade portano a Roma" :)

Comunque non spiega se i 3 numeri si devono trovare sempre nella stessa posizione oppure ogni codice i 3 numeri li deve disporre in punti diversi della stringa di 9
Direi "oppure"; dovrà essere sicuramente random, anche le posizioni delle cifre.

Proprio in questi giorni, per evitare di fare cose più serie e utili, ho buttato giù al volo una classetta per tirare fuori una serie di elementi (di qualunque tipo: numeri, lettere, oggetti) non ripetuti. Andrebbe bene per la domanda principale; per il mix preciso di 3 cifre e 9 lettere no, o almeno non direttamente.

Che fo, la pubblico? Non l'ho ancora testata decentemente.
 

Star-Dust

Expert
Licensed User
Longtime User
Io ho buttato giu qualcosa. Sia con i numeri nelle prime tre posizioni oppure i numeri a caso

B4X:
    Log("___ STESSA POSIZIONE ____")
    For i=1 To 5
        Log(CodiceTony_Puti)
    Next
  
    Log("_____ Posizione Casuale ____")
    For i=1 To 5
        Log(CodiceTony_Puti_Arriminatu)
    Next

Stessa posizione:
Public Sub CodiceTony_Puti As String
    Dim Risultato As String = ""
    Dim Numeri As String = "123456789"
    Dim Lettere As String = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
    Dim QuantiNumeri As Int = 3
    Dim Pos As Int
  
    'numeri
    For C=1 To QuantiNumeri
        ' trova numero non usato
        Pos = Rnd(0,Numeri.Length)
        Do While Risultato.Contains(Numeri.CharAt(Pos))
            Pos = Rnd(0,Numeri.Length)
        Loop
        Risultato=Risultato & Numeri.CharAt(Pos)
    Next
  
    'Lettere
    For c=QuantiNumeri+1 To 9
        ' trova lettera non usata
        Pos = Rnd(0,Lettere.Length)
        Do While Risultato.Contains(Lettere.CharAt(Pos))
            Pos = Rnd(0,Lettere.Length)
        Loop
        Risultato=Risultato & Lettere.CharAt(Pos)
    Next

    Return Risultato
End Sub

Posizione Casuale:
Public Sub CodiceTony_Puti_Arriminatu As String
    Dim Risultato As String = "         "
    Dim Numeri As String = "123456789"
    Dim Lettere As String = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
    Dim QuantiNumeri As Int = 3
    Dim Pos As Int
    Dim Find As Int
  
    'numeri
    For C=1 To QuantiNumeri
        ' trova numero non usato
        Pos = Rnd(0,Numeri.Length)
        Do While Risultato.Contains(Numeri.CharAt(Pos))
            Pos = Rnd(0,Numeri.Length)
        Loop
      
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
      
        Risultato=Risultato.SubString2(0,Find) & Numeri.CharAt(Pos) & Risultato.SubString(Find+1)
    Next
  
    'Lettere
    For c=QuantiNumeri+1 To 9
        ' trova lettera non usata
        Pos = Rnd(0,Lettere.Length)
        Do While Risultato.Contains(Lettere.CharAt(Pos))
            Pos = Rnd(0,Lettere.Length)
        Loop
      
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
      
        Risultato=Risultato.SubString2(0,Find) & Lettere.CharAt(Pos) & Risultato.SubString(Find+1)
    Next

    Return Risultato
End Sub
 

drgottjr

Expert
Licensed User
Longtime User
Veramente non è un proverbio olandese; inoltre, dice che "Tutte le strade portano a Roma" :)


Direi "oppure"; dovrà essere sicuramente random, anche le posizioni delle cifre.

Proprio in questi giorni, per evitare di fare cose più serie e utili, ho buttato giù al volo una classetta per tirare fuori una serie di elementi (di qualunque tipo: numeri, lettere, oggetti) non ripetuti. Andrebbe bene per la domanda principale; per il mix preciso di 3 cifre e 9 lettere no, o almeno non direttamente.

Che fo, la pubblico? Non l'ho ancora testata decentemente.
troppo complicato e sintetico;)
 

LucaMs

Expert
Licensed User
Longtime User
Io ho buttato giu qualcosa
La mia classetta è più "sofisticata", nel senso che non usa dei loop all'interno dei quali controllare che un elemento sia già presente o meno, cosa che allungherebbe i tempi (immaginate una serie di 20 numeri, ad esempio, da prelevare tutti e in ordine casuale: al 20° quanti cicli farebbe per trovare l'unico ancora non utilizzato?).
Non che io abbia inventato qualcosa di nuovo, ho usato una tecnica molto nota.

Se la costruzione non va ripetuta parecchie volte, va benissimo anche come fatto da @Star-Dust, ovviamente.
 

Star-Dust

Expert
Licensed User
Longtime User
il loop non controlla se è già dentro. Quello lo vedo semplicemente con Content.
Il loop server a effettuare una ricerca di un nuovo carattere casuale.
Si potrebbe fare con una list di carattere e man mano che li usi li elimini ma credo non risparmi molto. E comunque i caratteri sono solo 9 quindi è abbastanza veloce
 
Last edited:

Star-Dust

Expert
Licensed User
Longtime User
Visto che non ti piace il loop per la ricerca del carattere non usato, te l'ho tolto.
Posizione fissa:
Public Sub CodiceTony_Puti As String
    Dim Risultato As String = ""
    Dim Numeri As String = "123456789"
    Dim Lettere As String = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
    Dim QuantiNumeri As Int = 3
    Dim Pos As Int
 
    'numeri
    For C=1 To QuantiNumeri
        ' trova numero non usato
        Pos = Rnd(0,Numeri.Length)
        Risultato=Risultato & Numeri.CharAt(Pos)
        Numeri=Numeri.Replace(Numeri.CharAt(Pos),"") ' elimina carattere usato
    Next
 
    'Lettere
    For c=QuantiNumeri+1 To 9
        ' trova lettera non usata
        Pos = Rnd(0,Lettere.Length)
      
        Risultato=Risultato & Lettere.CharAt(Pos)
        Lettere=Lettere.Replace(Lettere.CharAt(Pos),"") ' elimina carattere usato
    Next

    Return Risultato
End Sub

Posizione casuale:
Public Sub CodiceTony_Puti_Arriminatu As String
    Dim Risultato As String = "         "
    Dim Numeri As String = "123456789"
    Dim Lettere As String = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
    Dim QuantiNumeri As Int = 3
    Dim Pos As Int
    Dim Find As Int
 
    'numeri
    For C=1 To QuantiNumeri
        ' trova numero non usato
        Pos = Rnd(0,Numeri.Length)
      
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
    
        Risultato=Risultato.SubString2(0,Find) & Numeri.CharAt(Pos) & Risultato.SubString(Find+1)
        Numeri=Numeri.Replace(Numeri.CharAt(Pos),"") ' elimina carattere usato
    Next
 
    'Lettere
    For c=QuantiNumeri+1 To 9
        ' trova lettera non usata
        Pos = Rnd(0,Lettere.Length)
    
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
    
        Risultato=Risultato.SubString2(0,Find) & Lettere.CharAt(Pos) & Risultato.SubString(Find+1)
        Lettere=Lettere.Replace(Lettere.CharAt(Pos),"") ' elimina carattere usato
    Next

    Return Risultato
End Sub
 

LucaMs

Expert
Licensed User
Longtime User
Visto che non ti piace il loop per la ricerca del carattere non usato, te l'ho tolto.
Giusto; in quel modo le routine sono molto più veloci.

Ancora non è il metodo ideale - in generale, non nel caso specifico - perché se dovessi eseguire l'operazione più di una volta, dovresti reinizializzare i dati iniziali, che in questo caso sono solo due stringhe, quella numerica e quella letterale, perché vengono man mano accorciate se non azzerate.
Anche questo non è un problema, a meno che tu non debba eseguire tutta l'operazione centinaia/migliaia di volte al minuto, ad esempio e, soprattutto, che i dati a disposizione siano appunto solo due stringhe non lunghe di caratteri.

Il metodo "giusto", o meglio il più veloce, è usare un array (che tra l'altro potrebbe contenere qualunque cosa).

Esempio:

Ho un array composto da 30 elementi: Elems(30) (riempio l'array soltanto una volta, all'inizio).
Imposto una variabile globale (a livello di modulo) sul numero di elementi:
Private mNumElems As Int = 30
Cerco un elemento in modo casuale:
Index = Rnd(0, mNumElems)
Trovato = Elems(Index)

Eseguo lo "swapping" (scambio) tra l'elemento dell'array trovato e l'ultimo nell'array:
Elems(Index) = Elems(mNumElems - 1) ' "sposto" l'ultimo elemento nel posto di quello appena trovato
Elems(mNumElems - 1) = Trovato ' metto l'elemento trovato nell'ultimo posto dell'array
Decremento di 1 mNumElems, così al prossimo "giro", la Rnd non lo terrà più in considerazione, quindi non verrà utilizzato di nuovo:
mNumElems = mNumElems - 1

In questo modo, il prossimo elemento verrà ancora prelevato con:
Index = Rnd(0, mNumElems)
Trovato = Elems(Index)

ma mNumElems varrà 29, il 30°, già usato, non potrà venire "puntato", preso di nuovo. Inoltre, l'array continuerà ad avere 30 elementi, senza doverlo riempire di nuovo (appunto come dovresti fare con le due stringhe che invece vengono "accorciate").
Dovendo eseguire tutta l'operazione più volte, non dovrò caricare di nuovo l'array iniziale, dovrò solo reimpostare mNumElems su 30.

E' un algoritmo molto noto, non ho inventato alcunché!!!
 
Last edited:

Tony.

Member
Io ho buttato giu qualcosa. Sia con i numeri nelle prime tre posizioni oppure i numeri a caso

Posizione Casuale:
Public Sub CodiceTony_Puti_Arriminatu As String
    Dim Risultato As String = "         "
    Dim Numeri As String = "123456789"
    Dim Lettere As String = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
    Dim QuantiNumeri As Int = 3
    Dim Pos As Int
    Dim Find As Int
 
    'numeri
    For C=1 To QuantiNumeri
        ' trova numero non usato
        Pos = Rnd(0,Numeri.Length)
        Do While Risultato.Contains(Numeri.CharAt(Pos))
            Pos = Rnd(0,Numeri.Length)
        Loop
     
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
     
        Risultato=Risultato.SubString2(0,Find) & Numeri.CharAt(Pos) & Risultato.SubString(Find+1)
    Next
 
    'Lettere
    For c=QuantiNumeri+1 To 9
        ' trova lettera non usata
        Pos = Rnd(0,Lettere.Length)
        Do While Risultato.Contains(Lettere.CharAt(Pos))
            Pos = Rnd(0,Lettere.Length)
        Loop
     
        'trova spazio libero
        Find = Rnd(0,Risultato.Length)
        Do While Risultato.CharAt(Find)<>" "
            Find = Rnd(0,Risultato.Length)
        Loop
     
        Risultato=Risultato.SubString2(0,Find) & Lettere.CharAt(Pos) & Risultato.SubString(Find+1)
    Next

    Return Risultato
End Sub
Grazie mille, con questa porzione di codice, sono riuscito a risolvere il mio problema ed ho terminato l'applicazione. Solo che non so come mai ma ho riscontrato un problema, ovvero che utilizzando il bridge, riesco tranquillamente a far girare l'app sul mio Device mentre se da debug passo a release, poi premo su progetto > compila ed esegui e prelevo l'apk da dentro la cartella Objects e la installo, l'applicazione crasha. Mi sapete dire come risolvere questa cosa o cosa sbaglio? Grazie mille
 

Elric

Well-Known Member
Licensed User
A saperlo che ci stavate lavorando, evitavo di farmelo in casa... :p

Io mi sono costruito questa:
B4X:
Sub PickRandomNoRepeatedFromList(myTotalItemToRandomize As Int, myListToPick As List, myNewList As List) As ResumableSub
 
    Private mylstPool As List
    mylstPool.Initialize
    mylstPool.AddAll(myListToPick)
    Private myRandomIndexWherePickElement As Int
    Private mySizelstPool As Int = myListToPick.Size
    Private myInt As Int
 
 
    For i = 1 To myTotalItemToRandomize
        If mySizelstPool > 0 Then
            myRandomIndexWherePickElement = Rnd(0, mySizelstPool)
        Else
            myRandomIndexWherePickElement = 0
        End If
        myInt = mylstPool.Get(myRandomIndexWherePickElement)
        myNewList.Add(myInt)
        mylstPool.RemoveAt(myRandomIndexWherePickElement)
        mySizelstPool = mySizelstPool - 1
    Next
 
    Return myNewList
End Sub
Probabilmente con le liste non è molto efficiente.

La mia fonte di qualche anno fa: https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
 
Last edited:
Top