efficienza: fare le cose con poche risorse
risorse = tempo e spazio
in questo corso vediamo solo il tempo
cronometriamo il programma?
ma il tempo dipende da:
assunzione generale:
non ci interessa il tempo esatto ma una stima generale
indipendente da: computer, linguaggio, dati
operazioni semplici e complesse:
ipotesi semplificative:
riconducono = ?
il calcolatore in sè non esegue x in a
fa un ciclo sugli elementi, confrontando ogni elemento di a con
x
| Python | come viene fatto | |
|---|---|---|
if x in a: print 'presente' |
⇒ |
c=False
for e in a:
if x==e:
c=True
break
if c:
print 'presente'
|
espressioni regolari: ancora più complicato
invece di dire: ogni istruzione 1 millisecondo
o 12 nanosecondi, o 0.9 microsecondi…
una istruzione = tempo 1
non 1 millisecondo, ma una generica unità di tempo
equivale a:
il tempo di esecuzione si misura come numero di istruzioni semplici che vengono eseguite
occorre fare qualcosa con una lista a
in generale, una stessa cosa si può fare con più programmi diversi
possono avere tempi diversi
per esempio, tre programmi:
fanno la stessa cosa, ma con tempi diversi
| len(a) | prog 1
5×len(a) |
prog 2
100×len(a) |
prog 3
2len(a) |
|
|---|---|---|---|---|
| 1 | 5 | 100 | 2 | |
| 5 | 25 | 500 | 32 | |
| 20 | 100 | 2000 | 1048576 |
a corta ⇒ tempi brevi comunque
i tempi contano quando a è lunga:
ci interessa come cresce il tempo quando l'input diventa grande
non ci interessa il tempo preciso
5×len(a) e 100×len(a) crescono in modo simile
2len(a) molto di più
sia n la dimensione dell'input
se è una lista, n=len(a)
esempi di costi:
primi due lineari: efficienti
terzo quadratico: un po' meno
quarto esponenziale: non efficiente
c×n, d×n2 e f×2n li consideriamo tempi diversi
ma c×n e d×n no, ecc.
1000×n+2000 è come 2×n+1
se:
si dice che il tempo è O(n2)
notazione big-O
O-grande
45×n2+1000×n+500 ⇒
45×n2 ⇒
n2
è O(n2)
un programma ha costo (in termini di tempo) O(f(n)) se:
t(n) < a×f(n)+b
sull'esempio (si può assumere n≥1):
45×n2+1000×n+500 < 45×n2+1000×n2+500 = 1045×n2+500 = a×n2+b
costanti a=1045 e b=500
con O:
se poi si misura il tempo in notazione O sono valide le assunzioni fatte sul costo delle singole istruzioni
esempio: ricerca in una lista
c=False
for e in a:
if x==e:
c=True
break
il costo medio dipende dalle probabilità
esempio, x in a:
consideriamo i dati sui quali ci vuole più tempo
sulla ricerca in una lista: l'elemento non c'è
oppure è l'ultimo
in altri casi migliore, medio e peggiore coincidono:
esempio: somma degli elementi di una lista
altro modo di vedere l'efficienza:
grandezza dell'input che un programma può risolvere in un dato tempo
| 1 secondo | 1 minuto (=60 secondi) |
1 ora (=3600 secondi) |
1 giorno (=86400 secondi) |
|
|---|---|---|---|---|
| 100×n | 10 | 600 | 36000 | 864000 |
| 10×n2 | 10 | 77 | 600 | 2939 |
| n3 | 10 | 39 | 153 | 442 |
| 2n | 9 | 15 | 21 | 26 |
più grande problema risolubile in un certo tempo
assumendo una operazione ogni millesimo di secondo
| 1 secondo | 1 minuto (=60 secondi) |
1 ora (=3600 secondi) |
1 giorno (=86400 secondi) |
|
|---|---|---|---|---|
| 100×n | 10 | 600 | 36000 | 864000 |
| 10×n2 | 10 | 77 | 600 | 2939 |
| n3 | 10 | 39 | 153 | 442 |
| 2n | 9 | 15 | 21 | 26 |
all'inizio numeri simili
problema grande 200:
grandezza dell'input: n
in generale:
costo O(nc) = costo polinomiale
intuitivamente:
è una qualsiasi delle istruzioni che vengono eseguite più volte
sono quelle dei cicli "più interni"
esempio…
a = [3, 2, -1, 2, 1] s = 0 for x in a: s = s + x print(s)
somma di una lista
| programma | numero di esecuzioni | |
|---|---|---|
a = [3, 2, -1, 2, 1] s = 0 for x in a: s = s + x print(s) |
1 1 5 1 |
istruzione eseguita più volte: s = s + x
è l'istruzione dominante di questo programma
è quella dentro il ciclo
non siamo interessati al tempo di esecuzione con input specifico
vogliamo sapere quanto cresce il tempo quando cresce l'input
⇒ valutazione con lista di lunghezza arbitraria
| programma | numero di esecuzioni | |
|---|---|---|
s = 0 for x in a: s = s + x print(s) |
1 n 1 |
lista a lunga n
| programma | numero di esecuzioni | |
|---|---|---|
s = 0 for x in a: s = s + x print(s) |
1 n 1 |
costo 1 + n + 1
O(n)
lista lunga n:
costo asintotico = numero esecuzioni dell'istruzione dominante
| programma | numero di esecuzioni | |
|---|---|---|
s = 0 for x in a: s = s + x print(s) |
1 n 1 |
l'istruzione dominante è quella nel ciclo
sommare i valori a[0] × … × a[i]
per tutti gli indici i
a = [3, 2, -1, 2, 1]
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
per ogni elemento della lista
calcola il prodotto
li somma
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n ??? n 1 |
quante volte viene eseguito p = p * y?
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n ??? n 1 |
l'intero ciclo interno for y... viene eseguito n volte
l'istruzione p = p * y viene eseguita i volte
ogni volta che si esegue il ciclo interno for y...
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n n × (i + 1) n 1 |
quanto vale n × (i + 1)?
i + 1 va da 1 a n
approssimato: n/2
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n n × n / 2 n 1 |
costo totale:
1 + n + n×n/2 + n + 1 = n×n / 2 + 2×n + 2 = n2/2 + 2×n + 2 O(n2)
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n n × n / 2 n 1 |
istruzione eseguita più volte: p = p * y
è quella nel ciclo interno
| programma | numero di esecuzioni | |
|---|---|---|
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
|
1 n n × n / 2 n 1 |
costo totale = numero di esecuzioni dell'istruzione dominante
si guardano le istruzioni dentro i cicli più interni
si vede quante volte vengono eseguite
sempre in funzione della grandezza dell'input
questo dice il costo del programma in notazione O-grande
s = 0
for i,x in enumerate(a):
p = 1
for y in a[0:i+1]:
p = p * y
s = s + p
print(s)
parte fra parentesi: valore di p al passo precedente
s = 0 p = 1 for i,x in enumerate(a): p = p * x s = s + p print(s)
invece di p = (a[0] × … × a[i]) × a[i+1]
si calcola p = (p) × a[i+1]
valore precedente di p
| programma | numero di esecuzioni | |
|---|---|---|
s = 0 p = 1 for i,x in enumerate(a): p = p * x s = s + p print(s) |
1 n n 1 |
costo totale:
1 + n + n + 1 = 2 × n + 2 O(n)
stesso problema
due programmi:
il primo programma è una soluzione più veloce dello stesso problema
uno stesso problema si può risolvere con più programmi
alcuni possono essere più veloci di altri, per es:
si sceglie il programma più veloce
P = insieme dei problemi che si risolvono in tempo polinomiale
consideriamo sempre costo al crescere dell'input
non ha senso valutare il costo di una somma a 64 bit
tre casi possibili:
subset-sum:
itertools.combination(insieme, n)
tutti i sottoinsiemi di n elementi dell'insieme
ciclo da 0 a len(insieme):
tutti i sottoinsiemi
per ognuno si calcola la somma
def verificasomma(numero, insieme):
s = 0
for e in insieme:
s = s + e
return s == numero
costo: O(n)
import itertools
…
def subsetsum(numero, insieme):
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if verificasomma(numero, s):
return s
return None
import itertools
def verificasomma(numero, insieme):
s = 0
for e in insieme:
s = s + e
return s == numero
def subsetsum(numero, insieme):
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if verificasomma(numero, s):
return s
return None
print(subsetsum(11, {3, 6, 1, 4, 2}))
print(subsetsum(1, {7, 9, 3, -2, 5, -10, 4}))
print(subsetsum(10, {5, 9, -3, -5, 11, 3}))
print(subsetsum(10, {3, 4, 11, 2}))
costo?
def subsetsum(numero, insieme):
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if verificasomma(numero, s):
return s
return None
insieme di grandezza n
ciclo esterno: eseguito n volte
ciclo interno: eseguito una volta per ogni sottoinsieme
numero totale di sottoinsiemi: esponenziale in n
costo O(2n)
| programma | schema | |
|---|---|---|
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if verificasomma(numero, s):
return s
return None
|
per ogni sottoinsieme:
se la verifica è positiva:
ritorna il sottinsieme
ritorna che non ci sono soluzioni
|
altri problemi con cui funziona?
per ogni sottoinsieme:
se la verifica è positiva:
ritorna il sottinsieme
ritorna che non ci sono soluzioni
per ogni sottoinsieme di variabili:
se il modello in cui sono vere quelle false le altre soddisfa la formula:
ritorna il modello
ritorna che non ci sono modelli
per ogni sottoinsieme di nodi di un grafo:
se è una coperatura del grafo della grandezza data:
ritorna il sottoinsieme
ritorna che non ci sono coperture
per ogni sequenza di nodi di un grafo:
se è un percorso nel grafo fra i due nodi dati:
ritorna la sequenza
ritorna che non ci sono percorsi
per ogni sequenza di nodi di un grafo:
se è un ordine topologico nel grafo:
ritorna la sequenza
ritorna che non ci sono ordini topologici
sequenze: itertools.permutations(insieme, numero)
sempre numero esponenziale
problemi che si possono risolvere con un ciclo su itertools.combinations() o itertools.permutations() e …
manca una precisazione
problemi che si possono risolvere con un ciclo su itertools.combinations() o itertools.permutations() con verifica in P
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if verificasommadispari(s):
return set(s)
problema in NP
for e in insieme:
if e % 2 == 1:
return {e}
return None
operazione dominante: e % 2 == 1
eseguita len(insieme) volte:
n = len(insieme)
problema in P
for e in insieme:
if e % 2 == 1:
return {e}
return None
operazioni: numero di elementi nell'insieme
vale per tutti?
problema in P ⇒ anche in NP
esempio con input un insieme:
for i in range(0, len(insieme)):
for s in itertools.combinations(insieme, i):
if soluzionepolinomiale(s):
return s
problemi con soluzione "ciclo itertools + verifica in P"
copertura di un grafo, sottoinsieme con somma dispari, soddisfacibilità proposizionale, ordine topologico, somma di un sottoinsieme
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | |
| | | |
| | topo | |
| | | somma |
| +------------+ |
+---------------------------+
alcuni problemi hanno anche un programma che gira in tempo polinomiale
sottoinsieme con somma dispari, ordine topologico
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | |
| | | |
| | topo | |
| | | somma |
| +------------+ |
+---------------------------+
dove sono copertura di un grafo, soddisfacibilità proposizionale, somma di un sottoinsieme?
sono in NP perchè hanno la soluzione itertools+verifica
non si conoscono programmi polinomiali
sono fuori da P?
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | |
| | | |
| | topo | |
| | | somma |
| +------------+ |
+---------------------------+
copertura di un grafo, soddisfacibilità proposizionale, somma di un sottoinsieme
non si conoscono programmi polinomiali
potrebbero essere in P oppure no
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | |
| | | |
| | topo | |
| | | somma |
| +------------+ |
+---------------------------+
non si conoscono algoritmi polinomiali
potrebbero esistere
potrebbe essere in P
oppure no
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | ^ |
| | | | |
| | topo | | |
| | | somma |
| +------------+ |
+---------------------------+
programma polinomiale che converte:
un numero e un insieme di interi
⇓
una formula proposizionale
tale che:
a cosa serve?
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | ^ |
| | | | |
| | topo | | |
| | | somma |
| +------------+ |
+---------------------------+
se sat ha un programma polinomiale
ce l'ha anche somma:
somma di due polinomi: polinomio
se sat è in P, lo è anche somma
+---------------------------+
| NP |
| +------------+ copert |
| | P | | |
| | disp | | |
| | | | V |
| | +-------> sat |
| | +-------> ^ |
| | | | | |
| | topo | | |
| | | somma |
| +------------+ |
+---------------------------+
teorema di Cook (1971): tutti i problemi in NP si riducono a sat
se sat è in P
tutti i problemi in NP sono anche in P
+---------------------------+
| NP |
| +------------+ copert |
| | P | | |
| | disp | | |
| | | | V |
| | +-------> sat |
| | +-------> ^ |
| | | | | |
| | topo | | |
| | | somma |
| +------------+ |
+---------------------------+
se sat è in P
tutti i problemi in NP sono anche in P
altri programmi polinomiali per questi problemi non sono mai stati trovati
ritenuto sempre meno probabile che esistano per tutti
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | |
| | | |
| | topo | |
| | | somma |
| +------------+ |
+---------------------------+
in NP
non si sa se è in P
servono tutte le riduzioni?
+---------------------------+
| NP |
| +------------+ copert |
| | P | |
| | disp | |
| | | |
| | | sat |
| | | | |
| | | | |
| | topo | V |
| | | somma |
| +------------+ |
+---------------------------+
non servono tutte le riduzioni
basta quella da sat
+---------------------------+
| NP |
| +------------+ copert |
| | P | | |
| | disp | | |
| | | | V |
| | +-------> sat |
| | +-------> | |
| | | | | |
| | topo | V |
| | | somma |
| +------------+ |
+---------------------------+
esiste una riduzione da sat a somma
⇓
ogni altro problema in NP si riduce a somma
| NP-hard |
| |
+----------------|----------+ |
| | NP | |
| +------------+ |copert | |
| | P | | | |
| | disp | | | |
| | | | | |
| | | | sat | |
| | | | | |
| | | | | |
| | topo | | | |
| | | |somma | |
| +------------+ +---------------+
+---------------------------+
NP-hard: problemi a cui si riducono tutti gli altri in NP
anche uno solo è in P
⇓
tutti in P
ritenuto improbabile
+---------------------------+
| NP |
| +------------+ copert |
| | P | | |
| | disp | | |
| | | | V |
| | +-------> sat |
| | +-------> ^ |
| | | | | |
| | topo | | |
| | | somma |
| +------------+ |
+---------------------------+
teorema di Cook (1971): tutti i problemi in NP si riducono a sat
dimostrato attraverso macchine di Turing
macchina di Turing = programma
esiste programma = esiste macchina di Turing
-+---+---+---+---+---+---+---+---+---+-
| | | | | 0 | | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
-+---+---+---+---+---+---+---+---+---+-
| | | | | 1 | | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
primo passo, non deterministico:
scrive 0 e 1
nastro a destra
-+---+---+---+---+---+---+---+---+---+-
| | | |0/1| 0 | | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
-+---+---+---+---+---+---+---+---+---+-
| | | |0/1| 1 | | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
secondo passo, non deterministico:
scrive 0 e 1
non importa cosa è stato scritto prima
nastro a destra
-+---+---+---+---+---+---+---+---+---+-
|0/1|0/1|0/1|0/1|0/1| | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
sono stati scritti n bit
tutte le combinazioni possibili
-+---+---+---+---+---+---+---+---+---+-
|0/1|0/1|0/1|0/1|0/1| | | | |
-+---+---+---+---+---+---+---+---+---+-
| | ^ |
+-+ V | +-+
| +-----+ |
| | | |
| +-----+ |
+---------+
sottoinsieme di un insieme di n elementi
⇅
sequenza di n bit
primo bit dice se il primo elemento dell'insieme è nel sottoinsieme
il secondo bit lo dice per il secondo elemento
…
primi n passi:
generati tutti i sottoinsiemi
tempo n
verifica: polinomiale
macchina di Turing non derministica
tempo n + polinomiale = polinomale
macchina di Turing non deterministica, tempo polinomiale
= esiste un sottoinsieme che soddisfa una verifica polinomiale
NP sono tutti i problemi di questo tipo
NP = esiste qualcosa che rende vero qualcosa
| NP-hard |
| |
+----------------|----------+ |
| imsat | NP | |
| +------------+ |copert | |
| | P | | | |
| | disp | | | |
| | | | | |
| | | | sat | |
| | | | | |
| | | | | |
| | topo | | | |
| | | |somma | |
| +------------+ +---------------+
+---------------------------+
imsat: restrizione di sat a certe formule
è in NP
non si sa se è in P
non si sa se è NP-hard
teorema di Ladner:
esistono problemi in NP che non sono in P e non sono NP-hard
se NP non è uguale a P
NP = ciclo + verifica in P
se la verifica non è in P?
se è in NP?
per precisione:
il contrario della verifica in NP
∃∀QBF
dato: formula ∃X ∀ Y . F
dove F è una formula proposizionale
risultato: esiste una valutazione di X tale che per ogni valutazione di Y la formula F è vera?
| NP-hard |
| EA.QBF |
+----------------|----------+ |
| imsat | NP | |
| +------------+ |copert | |
| | P | | | |
| | disp | | | |
| | | | | |
| | | | sat | |
| | | | | |
| | | | | |
| | topo | | | |
| | | |somma | |
| +------------+ +---------------+
+---------------------------+
∃ X ∀ Y . F con Y=\emptyset è ∃ X . F
∃∀QBF
include sat come sottoproblema
∃∀QBF è NP-hard
non è in NP (forse)
ciclo + verifica in NPNP, ecc.
∃∀∃….F
numero qualsiasi di quantificatori
esistono problemi che sicuramente non sono in P
esempio: problema dei ciottoli
esempio di schema:
(X)------+ | | | | |%------|--------(X) | | / | | | / | | | | |%----------+ | | | | | V % | V | ( )-----------|->(( )) ( ) | | ^ | % | +-----------------------------+
la disposizione (linee, cerchi, ecc,) può variare
(X) -----------> ( )
%
|
|
(X)
|
⇒ |
( ) -----------> (X)
%
|
|
(X)
|
possibile solo se il cerchio sotto è occupato
i ciottoli non sono di nessuno
tutti e due i giocatori li possono muovere
il primo giocatore ha una strategia vincente?
= riesce a muovere in modo tale da vincere sempre?
+------------------>( ) | % | | | | | | | | % | (X) (X)------------|---->(( )) | | | ^ | | | | | % % | +------------>( ) ------------+
un esempio di disposizione iniziale
+------------------>(X) | % | | | | | | | | % | ( ) (X)------------|---->(( )) | | | ^ | | | | | % % | +------------>( ) ------------+
una possibile mossa del primo giocatore:
spostare il primo ciottolo in alto
ma…
+------------------>(X) | % | | | | | | | | % | ( ) ( )------------|---->((X)) | | | ^ | | | | | % % | +------------>( ) ------------+
tocca al secondo giocatore
sposta l'altro ciottolo nel nodo finale ⇒ vince
+------------------>( ) | % | | | | | | | | % | (X) (X)------------|---->(( )) | | | ^ | | | | | % % | +------------>( ) ------------+
torniamo all'inizio
+------------------>( ) | % | | | | | | | | % | ( ) (X)------------|---->(( )) | | | ^ | | | | | % % | +------------>(X) ------------+
se il primo giocatore sposta il primo ciottolo in basso…
+------------------>( ) | % | | | | | | | | % | ( ) (X)------------|---->(( )) | | | ^ | | | | | % % | +------------>(X) ------------+
il secondo giocatore non può muovere
il primo ha vinto
dati:
il problema è: esiste una strategia che consente al primo giocatore di vincere sempre?
si può dimostrare che:
ogni programma per verificare se esiste una strategia vincente per il problema dei ciottoli impiega tempo esponenziale
altri problemi hanno questa caratteristica
complessità esponenziale = il migliore programma che li risolve impiega tempo esponenziale nella dimensione dell'input
esempio: strategia vincente nel gioco dei ciottoli
diversi problemi hanno complessità esponenziale
EXPTIME = problemi risolubili in tempo esponenziale
P ⊂ EXPTIME
P ⊆ NP ⊆ EXPTIME
potrebbe essere P=NP
molti ritengono che NP≠P
premio di $1000000 per chi risolve la questione