Preliminari Dizionari Eratostene Laboratorio di Python (con Linux) 7a lezione Giulio Pellitta Università di Bologna 18, 20 aprile 2012 Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Sommario 1 Preliminari 2 Dizionari 3 Eratostene Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Iterazione determinata (for con remove) Questa funzione non restituisce la lista degli interi fino ad minori di n che non sono quadrati (ad esempio, se rimuoviamo 4 poi non possiamo rimuovere 16). def square_remove_wrong(n): L=range(n) for i in L: if i**2 in L: L.remove(i**2) return L La seguente invece fa esattamente così. def square_remove(n): L=range(n) for i in L[:]: if i**2 in L: L.remove(i**2) return L Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Dizionari Un dizionario è un tipo di dato di Python: un’insieme di chiavi (keys) a cui sono associati dei valori (values). I dizionari sono indicizzabili da chiavi di qualsiasi tipo, purché immutabili (niente liste nelle chiavi!). La lista delle chiavi si ottiene con il metodo keys(). Si può cancellare un’associazione chiave:valore con il comando del Riferirsi ad una chiave non esistente causa un errore di runtime. {} #dizionario vuoto D={1:'a','b':5,'C':True} #dizionario con tre valori #('a', 5 e True) associati alle chiavi (1, 'b' e 'C') for k in D: print D[k]#stampa gli oggetti nel dizionario 1 in D #True 1 in D.keys()#True Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Crivello di Eratostene def eratostene_very_easy(n): L,P=[0,0]+range(2,n+1),[] for p in L: if p!=0: for m in range(2*p,n+1,p): L[m]=0 P.append(p) return P Questa funzione è un’implementazione fedele dell’algoritmo descritto la scorsa lezione. Il primo passo è ottenere un programma funzionante che sia chiaro e fidato. Le ottimizzazioni vengono dopo. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene def eratostene_less_easy(n): L,P=[False,False]+[True]*(n-1),[] for p in range(2,n**0.5+1): #oppure n/2+1 if L[p]: for m in range(2*p,n+1,p): L[m]=False for i in range(len(L)): if L[i]: P.append(i) return P Se k non è primo allora ha almeno due fattori propri (maggiori √ di 1); se p è il minimo di questi fattori allora p2 ≤ k ovvero p ≤ k . Se k non è primo allora 2 · p ≤ k (poiché 2p ≤ p2 ); questo limite è peggiore dell’altro ma non richiede il calcolo della radice. Alla fine dei due √ cicli for annidati L[m]=False sse m = k · p per qualche p ≤ n e 2 ≤ k ≤ n/p. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene def eratostene_not_so_easy(n): L,P=[False,False]+[True]*(n-1),[] for p in range(2,n**0.5+1): if L[p]: for m in range(p**2,n+1,p): #! L[m]=False for i in range(len(L)): if L[i]: P.append(i) return P Marcare i multipli più volte è inutile. I multipli di p da 2p a (p − 1)p vengono marcati nelle iterazioni precedenti a p. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene def eratostene_smarter(n): if n<2: return [] L,P=[False]+[True]*((n-1)/2),[2] #! for p in range(3,n**0.5+1,2): #! if L[p/2]: #l'indice sarebbe (p-1)/2 #! for m in range(p**2,n+1,p+p): #! L[m/2]=False #! for i in range(len(L)): if L[i]: P.append(2*i+1) #! return P Possiamo escludere direttamente i pari maggiori di 2. Se p è un primo dispari allora con la divisione tra interi p/2 = (p − 1)/2 (per calcolare l’indice non serve sottrarre 1). Se p ≥ 3 è un primo dispari allora ∀k ≥ 0 2|p2 + (2k + 1)p ; quindi non serve marcare questi multipli, abbiamo appena escluso i pari. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Usando i dizionari def eratostene_dictionary(n): if n<2: return [] D,P={},[2] #! for p in range(3,n**0.5+1,2): if p not in D: #!s for m in range(p**2,n+1,p+p): D[m]=False #! for i in range(3,n+1,2): if i not in D: P.append(i) #! return P Possiamo usare un dizionario invece di una lista per tener traccia di quali numeri sono marcati. Meno efficiente che con le liste (notare: usiamo solo le chiavi del dizionario, non i valori). Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Altri algoritmi def eratostene_multipli(n): (multiples,p) = ([],[]) for i in range(2, n+1): if i not in multiples: p.append(i) for j in range(2*i, n+1, i): multiples.append(j) return p Altra possibilità è generare tutti i multipli dei numeri primi man mano che questi vengono trovati. Se un numero non è tra i multipli, allora è primo. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene def trial_division(n): if n<2: return [] if n==2: return [2] i,P=0,range(3,n+1,2) while P[i]<=n**0.5: j=i+1 while j<len(P): if P[j]%P[i]==0: del P[j] j+=1 i+=1 return [2]+P Altro metodo ancora è dividere i vari numeri per i primi già trovati (“trial division”, divisione per tentativi). Se il resto della divisione è zero, allora il dividendo non è primo. Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Esercizi 1 Definire una funzione “expand” che prende una lista di numeri non-negativi (da controllare che sia così, come per il flatten la scorsa lezione) e restituisce una lista in cui ogni numero x compare bxc volte. 2 Scrivere un programma ricorsivo per il calcolo della successione di Fibonacci (con valori base 0 e 1) facendo uso dei dizionari. 3 Scrivere un programma per calcolare la frequenza delle lettere dell’alfabeto in un file di testo. 4 Gioco dell’impiccato (scrivere un programma che sceglie a caso una parola da un file, ne stampa la lunghezza all’utente e gli chiede di indovinarla). Esercizio per casa: scrivere la soluzione in un file chiamato impiccato.cognome.nome.py e mandarlo via email a [email protected] entro il prossimo laboratorio (subject email: “impiccato”). Giulio Pellitta Laboratorio di Python (con Linux) Preliminari Dizionari Eratostene Suggerimenti 1 2 3 4 Usare la sostituzione di slice come visto la scorsa lezione. Inizializzare un dizionario a {0:0,1:1} e fare una chiamata a funzione solo se l’elemento corrispondente della successione di Fibonacci non è già nel dizionario (sennò calcolarlo e inserirlo). Inserire le frequenze assolute in una lista o dizionario. Partire da questa versione semplificata dell’esercizio della scorsa volta. def parole_da_file(filename): (text, W) = (open(filename,"r").readlines(), []) for line in text: for s in line.split(): W.append(s) return W Mettere in una lista le parole lette da file, sceglierne una a caso e stamparne la lunghezza. L’utente deve indovinare le lettere della parola: se dice una lettera non presente ha una penalità e vince se indovina tutte le lettere senza superare k penalità. Commentare il codice! Giulio Pellitta Laboratorio di Python (con Linux)