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)