Topic: problema con ciclo for  (Letto 112 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline Siuccio

  • python unicellularis
  • *
  • Post: 4
  • Punti reputazione: 0
    • Mostra profilo
problema con ciclo for
« il: Novembre 01, 2020, 18:40 »
buonasera, ho un piccolo problema ed online non riesco a trovare risposta, premesso che utilizzo python da veramente molto poco e probabilmente ci sarà un modo migliore di fare questa cosa.
ho creato un piccolo script che riceve una "password" in input e la ricavi da due tuple che contengono l'alfabeto, il problema sorge quando l'output lo stampa in loop, ho provato con break alla fine dell' if ma non funziona, dove sbaglio? (oltre al fatto che ci saranno mille modi migliori di farlo)
e come ottimizzarlo per usare più caratteri compresi i numeri?


password = [] #genero lista

naso = input("scrivi la password\n") #input utente

password = list(naso) #trasformo l'input in una lista

lettere = ('a', 'b', 'c', 'd','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z')

lett_maiusc = ('A', 'B', 'C', 'D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z')

numeri = ('1','2','3','4','5','6','7','8','9') #ancora da usare
         
for word in lett_maiusc:
    for word2 in lett_maiusc: #tutte maiuscole
       
       
        for word in lett_maiusc:
            for word2 in lettere: #prima maiusc seconda no
               
               
                for word in lettere:
                    for word2 in lettere: #tutte minuscole

                        for word in lettere:
                            for word in lett_maiusc: #prima minuscola seconda no
       
                                risultato = [word, word2]
       
                                if risultato == password:
                                    delimita = ''
                                    response = delimita.join(risultato) #da un output più leggibile
                                    print('la tua password è', response)
                                   

                                else:
                                    pass
                                   
« Ultima modifica: Novembre 01, 2020, 18:46 da Siuccio »

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 321
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #1 il: Novembre 01, 2020, 20:07 »
 :confused: ben 8 cicli for per una sola stringa? ... mi sembra un filino esagerato.

... dove sbagli ... in primo luogo dal non aver valutato cosa "vorresti" fare, le Tue password sono composte esattamente da "2" lettere? Se si prima o poi la Tua routine stamperà qualcosa, altrimenti no ... forse dovresti leggere come funziona il ciclo for sul tutorial (assieme al resto ;) ) e poi fermarti un po' a pensarci su, pianificare per bene cosa fare in base all'obiettivo che Ti poni e riprovare.

Offline Siuccio

  • python unicellularis
  • *
  • Post: 4
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #2 il: Novembre 01, 2020, 20:18 »
si, come detto sono all'inizio e sicuramente ci sarà un modo migliore, in ogni caso, avevo già letto il funzionamento del ciclo for e l'ho usato per trovare anche una "password"  di 8 caratteri ma sempre in minuscolo, non capisco perchè, una volta trovata la soluzione, la continua a stamparla nonostante non avessi avuto questo problema con solo una tupla da iterare. e capisco che usare cicli nidificati a ripetizione non sia conveniente ma non riesco ad immaginare come altro fare, neanche usando un istruzione while

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 321
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #3 il: Novembre 01, 2020, 20:27 »
Comincia con il porti la domanda "Cosa voglio fare"?

Stabilisci, quindi, le condizioni che vuoi siano applicate e cerca la documentazione relativa a tali condizioni, quindi pianifica il Tuo approccio sulle basi acquisite ... se vuoi imparare, funziona così.

Domani riguarderò questo post, se proprio non Ti riesce esponi chiaramente "cosa" vuoi fare e cercherò di aiutarti ma un aiuto esterno NON ti porta la stessa efficacia ad apprendere del sbatterci la testa.

Ciao :)

Offline Siuccio

  • python unicellularis
  • *
  • Post: 4
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #4 il: Novembre 01, 2020, 20:49 »
per esperienza il peggior metodo di apprendimento è quello di sbatterci la testa, si trovano soluzioni alternative e poco funzionali, spesso lunghe e contorte, come appunto nel mio caso.
il mio obbiettivo è trovare la "password" con il metodo bruteforce, cioè provando ogni possibile combinazione e l'utilizzo del cicli nidificati è stato il primo funzionale che mi è venuto in mente, ovviamente funzionale finchè so di quanti caratteri è la stringa da trovare, infatti non rimane che un "esercizio" per studiare

Offline nuzzopippo

  • python neanderthalensis
  • ****
  • Post: 321
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #5 il: Novembre 02, 2020, 09:10 »
per esperienza il peggior metodo di apprendimento è quello di sbatterci la testa, si trovano soluzioni alternative e poco funzionali, spesso lunghe e contorte, come appunto nel mio caso.
"Sbatterci la testa" significa "Mettersi a studiare ed a ragionare", le soluzioni "poco funzionali" migliorano man mano che si apprende, certamente NON è sufficiente studiarsi SOLO il linguaggio, esiste molta blibliografia in merito a pattern progettuali e di svariati aspetti della impostazione di software, anche quelli sono tra le cose da imparare.

il mio obbiettivo è trovare la "password" con il metodo bruteforce, cioè provando ogni possibile combinazione e l'utilizzo del cicli nidificati è stato il primo funzionale che mi è venuto in mente, ovviamente funzionale finchè so di quanti caratteri è la stringa da trovare, infatti non rimane che un "esercizio" per studiare

"bruteforce" ho dubbio NON sia consentito affrontare argomenti del genere ... comunque, data la banalità dell'obiettivo che Ti poni e considerando che un eventuale sito o password scardinabile con un tale metodo NON merita di esistere ...

Il primo errore che hai commesso è suddividere in TRE liste i caratteri "inseribili" (che tra parentesi sono MOLTI di più), basta una singola stringa, tipo questa:
Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license()" for more information.
>>> import datetime
>>> from dateutil.relativedelta import relativedelta
>>> ctrl_str = ''.join([chr(i) for i in range(48,122) if (i>=48 and i<=57) or
    (i>=65 and i<=90) or (i>=97 and i<=122)])
>>> ctrl_str
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy'


Una stringa è iterabile, non dovrai fare altro che scorrerla tante volte quanto lunga "sai" essere la password da trovare, supposta sia 4 caratteri definiremo 4 variabili per la contenzione dei singoli caratteri (diciamo c1-c4) e scorreremo il riferimento sino a trovare la corrispondenza:
>>> def pwd_len4_match(pwd):
start = datetime.datetime.now()
counter = 0
summer = 0
for c1 in ctrl_str:
for c2 in ctrl_str:
for c3 in ctrl_str:
for c4 in ctrl_str:
test = c1+c2+c3+c4
counter += 1
if counter == 100000:
print('*', end='')
counter = 0
summer += 1
if test == pwd:
print()
ends = datetime.datetime.now()
diff = relativedelta(ends, start)
print(pwd, 'trovata in %d ore, %d minuti e %d secondi in %d x 100.000 + %d tentativi' %(diff.hours, diff.minutes, diff.seconds, summer, counter))
return True
print()
return False

>>> my_pwd = input('password (4 caratteri) : ')
password (4 caratteri) : Wiki
>>> pwd_len4_match(my_pwd)
**************************************************************************
Wiki trovata in 0 ore, 0 minuti e 1 secondi in 74 x 100.000 + 29967 tentativi
True

... noterai che ho messo degli indicatori per segnalarTi qualcosa : la corrispondenza è stata trovata in circa 1 secondo ma sono stati effettuati 7.429.967 tentativi.

Qualora noi "dovessimo" trovare una password lunga 5 caratteri

>>> def pwd_len5_match(pwd):
start = datetime.datetime.now()
counter = 0
summer = 0
for c1 in ctrl_str:
for c2 in ctrl_str:
for c3 in ctrl_str:
for c4 in ctrl_str:
for c5 in ctrl_str:
test = c1+c2+c3+c4+c5
counter += 1
if counter == 100000:
print('*', end='')
counter = 0
summer += 1
if test == pwd:
print()
ends = datetime.datetime.now()
diff = relativedelta(ends, start)
print(pwd, 'trovata in %d ore, %d minuti e %d secondi in %d x 100.000 + %d tentativi' %(diff.hours, diff.minutes, diff.seconds, summer, counter))
return True
print()
return False

>>> my_pwd = input('password (5 caratteri) : ')
password (5 caratteri) : Wikia
>>> pwd_len5_match(my_pwd)
********************************* ... (tante righe)
************************************************************
Wikia trovata in 0 ore, 2 minuti e 1 secondi in 4532 x 100.000 + 27963 tentativi
True
>>>

i tempi si allungano di circa 120 volte ed i tentativi diventano 453.227.963 ... non tentare di riprodurre il codice per 6 tentativi, probabilmente avresti un overflow ed i tempi sarebbero mooolto lunghi.

Spero basti, in ogni caso non interverrò ulteriolmente su di un "bruteforce"

Offline RicPol

  • python sapiens sapiens
  • ******
  • Post: 3.141
  • Punti reputazione: 9
    • Mostra profilo
Re:problema con ciclo for
« Risposta #6 il: Novembre 02, 2020, 14:58 »
Scusate ma... non ci siamo mica tanto, qui.

Allora, per prima cosa sgombriamo il campo da una possibilità. Questo, per caso, è un esercizio che è stato *assegnato* da qualcuno all'OP (un prof, un corso che sta seguendo, un libro...)? Perché allora i casi sono due: o il prof ha assegnato un esercizio troppo difficile, oppure l'OP non ha seguito con profitto il corso fin qui.
Questa è la possibilità facile.

Se invece questo è un esercizio che l'OP si è "inventato" da solo, tanto per provare... allora qui andiamo nel complicato.
Partiamo da questa affermazione fondamentale (quindi la scrivo in maiuscolo, tanto per essere chiaro): IMPARARE E RISOLVERE PROBLEMI SONO DUE COSE DIVERSE, al contrario di quanto molti amano dire, superficialmente.
Sì, è vero che ci sono sovrapposizioni tra le due cose. Nel corso di un processo di apprendimento si risolvono problemi: ma questa si chiama "didattica" e consiste nel proporre problemi attentamente confezionati in modo da corrispondere al livello di competenza acquisito dall'allievo in quel momento; in un certo senso, sono dei "problemi giocattolo", non dei problemi veri.
All'inverso, è vero che risolvendo problemi spesso si impara qualcosa. Ma questa si chiama "esperienza" e presuppone comunque a monte la conoscenza necessaria per risolvere il problema (e/o la capacità di acquisirne autonomamente dell'altra necessaria allo scopo).

Ora, personalmente io credo che l'OP farebbe meglio a IMPARARE, invece di dedicarsi a RISOLVERE PROBLEMI. Il mio consiglio è di lasciar perdere questo problema ("sbattendoci la testa" o no, è lo stesso) e seguire un buon libro passo-passo (il Lutz è sempre la scelta sicura). Affidarsi a un percorso didattico che in qualche modo lo sostenga... Specialmente se, oltre a essere principiante di Python, è anche principiante della programmazione, degli algoritmi, eccetera.



Ciò detto, vediamo invece la seconda cosa. Che cosa bisogna fare per RISOLVERE UN PROBLEMA?
Bisogna fare esattamente QUATTRO passaggi. Esattamente quattro, esattamente nell'ordine. Non c'è scampo: altrimenti non state risolvendo il problema, state solo pasticciando... e magari a forza di pasticciare inciampate nella soluzione, eh?... anzi, capita abbastanza spesso. Ma non vuol dire che avete "risolto il problema", vuol dire che avete "trovato la soluzione del problema". Sono due cose diverse.
Il primo passaggio consiste nel RICONOSCERE il problema, nel "dargli un nome", se volete. Questo molto spesso è il passaggio più difficile, specialmente per il principiante. Occorre fare un gigantesco sforzo di astrazione e lasciar perdere il caso specifico che si ha di fronte, per capire che in che cosa consiste il problema generale, a quale famiglia di problemi appartiene... come "si chiama" il problema.
Per esempio, nel nostro caso il caso specifico è "voglio indovinare una password!!! con la brute force!!!!!" Ma indovinare una password non è un "problema", è un "caso d'uso". E "brute force" non è... nulla, è solo un'espressione che non significa nulla.
Quindi lo sforzo di astrazione consisterebbe nel capire che questo problema (almeno per come ce lo stiamo ponendo qui adesso) appartiene alla famiglia dell'analisi combinatoria, e nello specifico consiste nel

trovare le combinazioni (con ripetizione!) di "k" elementi di un insieme di "n" elementi.

Questo è il problema che cerchiamo di risolvere. Dove "k" sarà poi la lunghezza della password, nel nostro caso specifico. E "n" finiranno per essere i caratteri ammessi nel campo della password (lettere, numeri, segni speciali, quel che volete).
Se uno arriva a capire che questo è il problema da risolvere, capisce anche che per esempio non ha senso chiedersi, come fa l'OP, "come faccio ad aggiungere le maiuscole e i numeri". Il problema è formulato correttamente (ripetiamo) come

le combinazioni di "k" elementi di UN insieme di "n" elementi

e non "...nella somma di TRE insiemi...". Per il semplice fatto che la somma (l'unione) di tre insiemi è a sua volta un insieme... quindi l'OP dovrebbe ragionare in termini di "quanti/quali caratteri sono ammessi" (il numero "n" di elementi), e non ha nessun senso distinguere l'insieme delle maiuscole, l'insieme delle minuscole, l'insieme dei numeri, l'insieme dei caratteri speciali...
Ma a questa comprensione ci si può arrivare solo se ci si sforza di riconoscere qual è il problema, "dare un nome" al problema.

Quindi, una volta che abbiamo capito qual è di preciso il problema, vediamo bene che anche la soluzione di Nunzio è sbagliata per principio. Non ha senso proporre una funzione per quando k è uguale a 4, un'altra funzione per quando k è uguale a 5, e così via. Quello che occorrerà fare è una funzione che produca l'algoritmo per la soluzione generale:

>>> def combinations(n, k):
         # ...

tale per cui, per esempio,

>>> combinations('abcde', 3)
['aaa', 'aab', 'aac', 'aad', 'aae', ..., 'cce', 'cdd', 'cde', 'cee', 'ddd', 'dde', 'dee', 'eee']  # 35 valori
>>> combinations('abcde', 4)
['aaaa', 'aaab', 'aaac', 'aaad', ..., 'ceee', 'dddd', 'ddde', 'ddee', 'deee', 'eeee']  # 70 valori

Solo a questo punto potremmo poi applicare questa soluzione al nostro caso specifico (finalmente! la brute force!), con un'altra funzione:

>>> import string
>>> chars = string.ascii_letters + string.digits + '#?$%&-_'   # per esempio...
>>> def guess_password(password):
        for guess in combinations(chars, len(password)):
            if guess == password:
                return 'found: ' + guess
>>> guess_password('my_secret')
found: my_secret
>>> guess_password('my_long_long_long_long_long_long_long_long_secret')
found: my_long_long_long_long_long_long_long_long_secret  # dopo un po' di tempo


Ma torniamo ai nostri quattro passaggi.
Dopo aver capito qual è il nostro problema, il secondo passaggio è DOCUMENTARSI sul problema. Questo è fondamentale. Googlare, googlare, googlare come dei pazzi. Non alla ricerca di ricettine da copincollare, beninteso, come vogliono i principianti SEMPRE. Ma googlare per capire di che cosa stiamo parlando. Se sto cercando di fare un programma che mi cataloga la mia biblioteca, avrò bisogno di avere qualche nozione basilare di biblioteconomia (e già per trovare il parolone, bisogna saper googlare). Quanto ne so, davvero, di come si cataloga un libro? Se voglio tenere i conti di casa, forse mi conviene farmi un'idea di quali feature offrono, tipicamente, i centomila programmi esistenti per tenere i conti di casa. E così via. Meglio studiare, prima di cominciare a pasticciare con la tastiera.
Di solito il primo e il secondo passaggio si rimbalzano tra loro più volte. Documentandosi uno finisce per arrivare a definizioni sempre più precise del problema. Definire più precisamente il problema consente di raffinare le ricerche di documentazione. Per esempio, nel nostro caso possiamo partire da una vaga idea di "combinazione" e poi, leggendo in giro, scoprire che è diverso dire "con ripetizioni" o "senza ripetizioni". Quindi ci pensiamo sopra, e arriviamo a definire meglio il nostro problema: a noi servono le "combinazioni con ripetizione" perché uno può usare tante volte lo stesso carattere, in una password. E così via.
Documentandosi si finisce sempre per scoprire anche già qualche algoritmo pronto, perché ovviamente! non saremo i primi a porci questo problema nella storia dell'umanità. Questa è una BUONA cosa, a patto di non limitarsi a copincollare la ricettina. La cosa migliore poi è trovare l'algoritmo implementato in un linguaggio che non è il nostro (in Java, in C++)... lo sforzo di capire produrrà risultati sorprendenti nella nostra testa.

Per esempio, nel nostro caso potremmo anche imbatterci nel curioso piccolo fatto... che Python ha già una funzione nella libreria standard che trova esattamente le combinazioni con ripetizione. Eh già, guarda un po'. E se uno guarda la documentazione di questa funzione, trova anche una implementazione in Python dell'algoritmo che ci serve... ed è una implementazione molto elegante, ma anche molto difficile da capire... su cui davvero uno, se ha voglia, può "sbatterci la testa" per un po'.

Il terzo passaggio consiste nel FORMALIZZARE la soluzione. Questo si fa SEMPRE con carta e penna, e può voler dire diverse cose. Per i problemi più complessi e compositi potrebbe essere un elenco preciso degli attori; magari un po' di diagrammi UML; magari uno schema delle tabelle di un database con le loro relazioni; va a sapere.  In ogni caso, qualcosa che fissi con chiarezza come intendiamo procedere.
Nel caso degli algoritmi semplici, come il nostro, non resta che... scrivere l'algoritmo in italiano (pseudo-codice) riga per riga. Questo è l'esercizio noioso che il principiante schiva come la morte. Però è anche la cosa che serve: altrimenti si sta solo pasticciando sulla tastiera. Talvolta può essere facile, talvolta più difficile. La rete è piena di esempi di algoritmi in pseudo-codice. Bisogna abituarsi a farseli a manina... non c'è santo. Spesso aiutano dei disegni, dei diagrammi... quello che volete. L'importante è che sia chiaro nel momento in cui poi si scriverà il codice.

Ora, il problema di questo algoritmo particolare è che è un po' complicato da visualizzare correttamente. Un problema è che ci sono due varibili libere ("n" e "k"), per cui è un po' più difficile immaginare una tabella... Tra l'altro è uno di quegli algoritmi che si "pensano" più facilmente immaginando una struttura ricorsiva... e questo è tutto dire, visto che di solito la ricorsione è difficile da pensare.
Non è che non lo si possa studiare, eh... e sicuramente in giro per la rete qualche aiuto si trova. Però non sarebbe uno di quelli che proporrei in un percorso didattico, almeno non per studenti alle prime armi.

Il quarto passaggio, finalmente, è SCRIVERE IL CODICE. Cercare di tradurre l'algoritmo visualizzato in codice. Anche questo non è proprio automatico. Per esempio, nel nostro caso si può ricorrere a cicli "for" oppure a dei "while"... è una scelta. Oppure, dicevamo, alla ricorsione.
Anche qui, il terzo e il quarto passaggio si rimbalzano spesso tra di loro. Uno può cominciare con una piccola parte dell'algoritmo, scrivere quel pezzo di codice, poi tornare indietro... fare delle prove e verificare la correttezza delle sue assunzioni...
Però bisogna chiarire bene una cosa: questo "avanti e indietro" tra il terzo e il quarto passaggio, è una cosa comune e ammessa dalla pratica di programmazione. A seconda dei contesti si può chiamare in vari modi (fattorizzare il codice, approccio bottom-up... test-driven development... chi più ne ha più ne metta); ma non vuol dire "mettersi a pasticciare e vediamo che cosa esce fuori". La cosa importante è che non si dovrebbe comunque scrivere una riga di codice senza sapere che cosa si sta facendo (magari poi ci si sbaglia... ma questo fa parte del gioco).
Dico questo perché molto spesso c'è una confusione alla base... questo incentivo a "pasticciare" è tipico in effetti di molti corsi didattici, che propongono di impare "mettendoci le mani" subito... però questi sono appunto dei percorsi didattici, che ti dicono di mettere le mani "a caso" su esercizi molto ben calibrati e progressivi (si spera!), non si problemi reali scelti un po' a caso. Di nuovo, bisogna stare attenti a distinguere tra "la didattica" e "la soluzione di problemi".


E qui chiudo perché per oggi mi sembra di aver dato anche troppo. La morale è sempre quella: Python è un linguaggio che può essere facile per chi già ha l'abitudine a programmare, ma per i principianti è molto più complesso di quello che si dice. E la programmazione è ancora più complessa. In generale, non bisogna sorprendersi di trovarsi di fronte a dei gradini difficili. L'ideale è imparare affidandosi a un percorso didattico valido, invece di avere la pretesa di "sperimentare" e "farsi un programma per vedere come va". Ma mi rendo conto che non è un consiglio che viene accolto bene, in genere.

Offline Siuccio

  • python unicellularis
  • *
  • Post: 4
  • Punti reputazione: 0
    • Mostra profilo
Re:problema con ciclo for
« Risposta #7 il: Novembre 02, 2020, 16:07 »
grazie a tutti per le risposte e per il tempo perso nel rispondere ad un povero principiante.
Ho capito il problema alla base, meglio seguire un percorso di studio un più preciso invece che girovagare su internet e trovare soluzioni (non funzionanti) a problemi che mi trovo davanti.
comprerò senz'altro il libro per partire dalle basi assolute.
Grazie a tutti.