Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


[Romani] Algoritmica, Sintesi del corso di Progettazione e analisi di algoritmi

Sintesi di algoritmica, corso di informatica umanistica sostenuto dal professor Romani, con sintesi della teoria per punti, programmi in python di ordinamenti, alberi, e sulla teoria, verifiche con relative statistiche.

Tipologia: Sintesi del corso

2019/2020

In vendita dal 31/01/2020

psychod
psychod 🇮🇹

4.4

(12)

22 documenti

1 / 47

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
ALGORITMICA
INDICE
1) Risposte teoria!
2) Risposte extra!
3) Ordinamenti!
4) Alberi!
5) Algoritmi teoria!
6) Statistiche verifiche!
7) Verifiche !
RISPOSTE TEORIA
1. Problema decisionale soddisfattibilità!
Data una arbitraria espressione E costruita sulle variabili logiche a1,…,an con gli operatori
AND, OR, NOT, si stabilisce un insieme di valori a1,…,an tale che E(a1,…,an)=TRUE.
Quindi il problema si risolve con un algoritmo che data una condizione, verifica che la
stessa sia soddisfatta dati i parametri E e il suo indice.!
Appartiene a NP, ovvero l’insieme dei problemi decisionali risolvibili in tempo polinomiale
con algoritmo non deterministico.!
2. Problema P, NP, NP-completo e NP-hard!
Si indica con P l’insieme di tutti i problemi decisionali risolvibili in tempo polinomiale da
un algoritmo deterministico, NP se con un algoritmo non deterministico. Con NP-
completo si indica un problema decisionale da cui è ridotto polinomialmente DSAT(DSAT
p) con p NP. Si dice NP-hard un qualsiasi problema riducibile polinomialmente
(DSAT(DSAT p).!
3. Algoritmi di ordinamento con complessità caso medio e caso peggiore!
Gli algoritmi di ordinamento hanno la funzione di ordinare un array secondo una data
relazione di ordine totale. Sono numerici se l’ordinamento è crescente o decrescente. Se
l’insieme è formato da stringhe su un dato alfabeto A, si parla di ordinamento alfabetico e
lessicografico. Si basano sui confronti. Alcuni esempi sono Bolle: scorrimento array con
scambi di scarsa ecienza, Selezione: divisione dell’array con ricerca dell’elemento
minimo, Mergesort: divisione dell’array in altri due di eccessivo carico di memoria,
Quicksort: divisione dell’array mediante un “perno” per stabilire massimo e minimo, e
l’Heapsort che ordina un dato heap - albero quasi perfettamente bilanciato con gli ultimi
nodi tendenti a sinistra - ed è conveniente per tale occasionale necessità.!
Bolle e Selezione, sia nel caso medio, sia nel caso peggiore hanno complessità
quadratica, Mergesort e Heapsort hanno sia nel caso peggiore sia nel caso medio
complessità logaritmica. Quicksort invece ha nel caso peggiore complessità quadratica e
nel caso medio logaritmica. (p.57 per caso medio e peggiore). !
4. Descrizione algoritmi di ricerca con complessità!
di 1 23
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f

Anteprima parziale del testo

Scarica [Romani] Algoritmica e più Sintesi del corso in PDF di Progettazione e analisi di algoritmi solo su Docsity!

ALGORITMICA

INDICE

  1. Risposte teoria
  2. Risposte extra
  3. Ordinamenti
  4. Alberi
  5. Algoritmi teoria
  6. Statistiche verifiche
  7. Verifiche RISPOSTE TEORIA
  1. Problema decisionale soddisfattibilità Data una arbitraria espressione E costruita sulle variabili logiche a 1 ,…,an con gli operatori AND, OR, NOT, si stabilisce un insieme di valori a 1 ,…,an tale che E(a 1 ,…,an)=TRUE. Quindi il problema si risolve con un algoritmo che data una condizione, verifica che la stessa sia soddisfatta dati i parametri E e il suo indice. Appartiene a NP, ovvero l’insieme dei problemi decisionali risolvibili in tempo polinomiale con algoritmo non deterministico.
  2. Problema P, NP, NP-completo e NP-hard Si indica con P l’insieme di tutti i problemi decisionali risolvibili in tempo polinomiale da un algoritmo deterministico, NP se con un algoritmo non deterministico. Con NP- completo si indica un problema decisionale da cui è ridotto polinomialmente DSAT(DSAT ⦣p) con p ∈ NP. Si dice NP-hard un qualsiasi problema riducibile polinomialmente (DSAT(DSAT ⦣p).
  3. Algoritmi di ordinamento con complessità caso medio e caso peggiore Gli algoritmi di ordinamento hanno la funzione di ordinare un array secondo una data relazione di ordine totale. Sono numerici se l’ordinamento è crescente o decrescente. Se l’insieme è formato da stringhe su un dato alfabeto A, si parla di ordinamento alfabetico e lessicografico. Si basano sui confronti. Alcuni esempi sono Bolle: scorrimento array con scambi di scarsa efficienza, Selezione: divisione dell’array con ricerca dell’elemento minimo, Mergesort: divisione dell’array in altri due di eccessivo carico di memoria, Quicksort: divisione dell’array mediante un “perno” per stabilire massimo e minimo, e l’Heapsort che ordina un dato heap - albero quasi perfettamente bilanciato con gli ultimi nodi tendenti a sinistra - ed è conveniente per tale occasionale necessità. Bolle e Selezione, sia nel caso medio, sia nel caso peggiore hanno complessità quadratica, Mergesort e Heapsort hanno sia nel caso peggiore sia nel caso medio complessità logaritmica. Quicksort invece ha nel caso peggiore complessità quadratica e nel caso medio logaritmica. (p.57 per caso medio e peggiore).
  4. Descrizione algoritmi di ricerca con complessità

Ricerca di uno specifico elemento detto chiave in un insieme di dati omogenei detto tabella. Tre diversi casi: ricerca in insiemi di chiavi prestabiliti (dizionari, elenchi…) con struttura prestabilita, ricerca e inserzione (rubriche), ricerca con inserzioni e cancellazioni. La ricerca può essere stabilita per gli array, le liste, gli alberi binari con caso peggiore, gli heap (O(n)), gli array ordinati, gli alberi binari con caso medio e gli alberi AVL (O(log n)). Complessità nello schema a pag.49.

  1. Ordine di grandezza funzione con notazione O(f(n)) e Ω(f(n)) Valutazione della velocità di crescita della funzione di complessità al tendere di n all’infinito paragonandola con funzioni dall’espressione semplice. Con la notazione O(f(n))

si indica l’insieme di tutte le funzioni g(n) per cui esistono due costanti c e n∘ tali che

g(n)<=cf(n) per ogni n> n∘.

Si scrive g(n) ∈ O(f(n)) e si legge g(n) è di ordine 0 di f(n). Con la notazione Ω(f(n)) si indica l’insieme di tutte le funzioni g(n) per cui esistono due

costanti C e n∘ tali che g(n)≥cf(n) e per ogni n>n∘. Si scrive g(n) ∈ Ω(f(n)) e si legge g(n) è

di ordine Ω di f(n).

  1. Algoritmi di complessità lineare Sono lineari gli algoritmi sugli array, le liste, gli alberi binari nel caso peggiore e gli heap. Tutto il resto ha crescita logaritmica.
  2. Descrivere algoritmo di visita simmetrico relativo ad un albero T Visitare un albero significa esaminare sequenzialmente tutti i suoi nodi. Nella visita in ordine simmetrico si visita il sottoalbero sinistro, quindi si esamina la radice e infine si visita il sottoalbero destro.
  3. Algoritmo ricorsivo e in quali casi non usarlo La ricorsione consiste nella possibilità di avere funzioni che richiamano sé stesse. Costituisce un potente strumento per la descrizione di algoritmi e la scrittura dei programmi, dato che permette di risolvere problemi complessi con semplicità ed eleganza. Inoltre, spesso, la formulazione ricorsiva degli algoritmi agevola la determinazione di limitazioni superiori alla loro complessità. Tuttavia, un’implementazione ricorsiva rischia di creare un enorme spreco di tempo: se la funzione deve essere chiamata troppo spesso, accade che gli stessi risultati intermedi vengano calcolati un numero sempre crescente di volte, portando a una pesante inefficienza.
  4. Tabelle hash: gestione collisioni, funzione, differenza con le tabelle ad accesso diretto Funzione che assegna l’indirizzo alla chiave. Strategia per la gestione delle collisioni attraverso un’uniforme distribuzione delle chiavi al suo interno. È utile per associare le etichette a insiemi di dati. È più efficiente dell’albero binario e ha un tempo di ricerca basso. La funzione HASH(o) calcola un valore intero a partire da un oggetto. Dipende dalla sessione. Prima o poi si dovrà inserire una chiave in un posto già occupato provocando una collisione e la si risolve assegnando opportunamente i posti liberi. L’algoritmo cerca sempre uno spazio libero e se non lo trova, scala fino a trovarlo. La tabella è veloce e dipende quest’ultima dalla percentuale di occupazione; è ragionevole al 75% per il tempo di ricerca. Quelle ad accesso diretto occupano più memoria e difficilmente sono praticabili (pag.42 p.2)

Nella visita a livelli, si parte dalla radice e si scende di un livello alla volta. I due algoritmi di visita principali sono il depth first, che consiste nello scendere sempre più in profondità, e il breadth first che è un metodo di ricerca che visita tutti i successori di un nodo prima di scendere in profondità.

  1. Metodi di calcolo I metodi di calcolo sono tre: non deterministico, dove si usano costrutti ipotetici e inesistenti come il CHOICE che in un passo solo verifica più alternative, il parallelo che ha un numero prefissato di agenti di calcolo fisici che interagiscono tra loro, e infine il probabilistico, in cui si effettuano scelte guidate dal caso e di cui ne fanno parte gli algoritmi Montecarlo, Las Vegas e Atlantic City.
  2. Problemi decisionali Oltre al DSAT, sono problemi decisionali:
  • (^) Del ciclo Hamiltoniano (DHAM): dato un grafo g di n vertici stabilire se esiste in g un ciclo che attraversa tutti i vertici esattamente una volta.
  • (^) Delle scatole (DBIN): dato un insieme A di n interi positivi e due interi h e k, stabilire se esiste una partizione di A in k sottinsiemi disgiunti tale che la somma degli elementi in ogni sottoinsieme sia minore e uguale a h.
  • (^) Dello zaino (DKNA): dato un insieme A di n interi positivi e due interi positivi c e z, stabilire se esiste un sottoinsieme di A i cui elementi abbiano somma compresa tra c e z.
  • (^) Delle equazioni diofantine quadratiche(DEDQ): dati 3 interi positivi a,b,c di n cifre, stabilire se l’equazione ax2+by+c=0 ha radici.
  • (^) Del numero cromatico (DCOL): dato un grafo g di n vertici e un intero positivo k stabilire se g può essere colorato con k colori in modo che non vi siano nodi adiacenti con lo stesso colore.
  • (^) Delle primalità (DPRI): stabilire se un numero intero positivo di n cifre è primo o può essere scomposto in fattori primi.
  1. Codifica di Huffman Dara una distribuzione di probabilità {p1,p2,…,pk} e supponendo p1<=p2<=…<=pk, le due parole più lunghe del codice che verrà generato saranno associate a p1 e p2. Usiamo un bit per distinguere tra i due eventi e consideriamo l’evento (accade p1 o p2). Tale evento ha probabilità p1+p ORDINAMENTI #ORDINAMENTO DA ZERO A 10 E=[9,4,5,6,2,1,3,4,5,7,0,5,5,4] def ordinamento(A): B=[0]*(len(A)) for x in A: B[x]=B[x]+ k= for i in range (0,len(A)): for j in range (0,B[i]):

A[k]=i k=k+ return A #SE LA SOMMA DI DUE ELEMENTI DELL'ARRAY E' K OVVIAMENTE IN UN ARRAY ORDINATO E=[0,1,2,3,4,5,6,7,8,9] def somma(A,k): i= j=len(A)- while i<j: if A[i]+A[j]==k: return true if A[i]+A[j]<k: i=i+ else: j=j- somma(E,5) #ORDINAMENTO ZERO E UNO def ordina(A): x= for j in A: if j==0: x=x+ for i in range(x): A[i]= for i in range (len(A)-x): A[x+i]= return A A=[0,0,1,1,0,1,0,1,1,1,0] print(ordina(A)) #ELEMENTI IN COMUNE IN 2 ARRAY ORDINATI def comuni(A,B): n=len(A) k= i= j= while i<n and j<n: if A[i]=B[j]: k=k+ j=j+ i=i+ elif A[i]<B[j]: i=i+ else:

#QUELLI MAGGIORI DOPO (GUARDA IL TESTO ORIGINALE PER CHIARIMENTI)

E=[9,5,6,2,1,3]

def riordina(A,x): i=0 #INDICE CHE TI PARTE DALL'INIZIO DELL'ARRAY n=len(A) j=n-1 #INDICE CHE PARTE DAL FONDO while i<j: while iA[i]: #Se il valore indicato dall' indice i è più piccolo rispetto a X, vai avanti perché è nella posizione giusta i=i+ while j>-1 and A[j]>x: #Se il valore indicato dalla j è più grande di X, vai indietro con la j perché quel valore è nella posizione giusta. j=j- if i<j: #Ma se X è più grande e j più piccolo allora qualcosa non va, quindi scambi tra di loro i due elementi. A[i],A[j]=A[j],A[i] i=i+ j=j- return A riordina(E,4) ALBERI #ALBERI C1 = nodo(3, foglia(-33), nodo((3), foglia(-10), foglia(22))) C2 = nodo(55, nodo(32, foglia(98), foglia(-17)),nodo(65, foglia(42), foglia(78))) #FUNZIONI PREIMPOSTATE def nodo(x, s, d): return [x, s, d] def foglia(x): return [x, None, None] def vuoto(): return None def radice(A): return A[0] def sinistro(A): return A[1] def destro(A): return A[2]

def isVuoto(A): return A is None def isFoglia(A): return isVuoto(sinistro(A)) and isVuoto(destro(A)) ###ESERCIZI DEL LIBRO### #somma elementi non foglia def sommaNF(A): if isVuoto(A): return 0 if isFoglia(A): return 0 return radice(A) + sommaNF(sinistro(A)) + sommaNF(destro(A)) #somma elementi a distanza dispari/pari dalla radice def sommaP(A): if isVuoto(A): return 0 return radice(A)+sommaD(sinistro(A))+sommaD(destro(A)) def sommaD(A): if isVuoto(A): return 0 return sommaP(sinistro(A))+sommaP(destro(A)) #Programma che calcola il massimo degli elementi a distanza pari dall radice def maxP(A): if isVuoto(A): return 0 return max(radice(A), maxD(sinistro(A)), maxD(destro(A))) def maxD(A): if isvuoto(A): return 0 return max(maxP(sinistro(A)), maxP(destro(A))) #ESERCIZI DEI COMPITI #somma elemti negativi *** 1 *** def sommaNEG(A): if isVuoto(A): return 0 if radice(A)<0: return radice(A)+ sommaNEG(sinistro(A)) + sommaNEG(destro(A)) return sommaNEG(sinistro(A)) #somma elementi non foglia 2

def sommadispari(C): if isVuoto(C): return 0 return sommapari(sinistro(C))+sommapari(destro(C)) #somma delle foglie 9 def sommaF(A): if isVuoto(A): return 0 if isFoglia(A): return radice(A) return sommaF(sinistro(A))+sommaF(destro(A)) #somma degli elementi 11 def sommaS(A): if isVuoto(A): return 0 else: return radice(A)+ sommaS(sinistro(A)) + sommaS(destro(A)) #massimo dei nodi e se vuoto deve sputare 4717 12 13 def massimo(A): if isvuoto(A): return 4717 if isVuoto (destro(A)): return radice(A) return massimo(destro(A)) #SOMMA DEL MASSIMO E DEL MINIMO DEGLI ELEMENTI VERSIONE 1 def massimo(A): if isvuoto(A): return 0 if isVuoto (destro(A)): return radice(A) return massimo(destro(A)) def minimo(A): if isvuoto(A): return 0 if isVuoto (sinistro(A)): return radice(A) return minimo(sinistro(A)) def sommamm(A): if isvuoto(A): return 0

return minimo(A)+massimo(A) #VERSIONE 2 def sommamm(A): if isvuoto(A): return 0 return sommamm(max(destro(A)))+sommamm(min(sinistro(A))) ALGORITMI TEORIA def fattoriale(n): if n == 0: return 1 return n * fattoriale (n-1) print (" 12! ", fattoriale(12)) def binario(n): if n==0: return " " return binario(n//2)+str(n%2) def binomiale (n,k): if k==0 or k==n: return 1 return binomiale(n-1,k-1)+binomiale(n-1,k) #triangolo di tartaglia n=int(input("Enter number of rows: ")) a = [] #è un array che non ha nemmeno caselle for i in range(n): a.append([]) #aggiungo una riga del triangolo all'array a[i].append(1) if i > 0: for j in range(1,i): #agisce solo quando i=2, poiché range(1,1) si blocca subito a[i].append(a[i-1][j-1]+a[i-1][j]) if(n!=0): a[i].append(1) print(a[i]) #torre di hanoi def muovi(sorgente, destinazione): print(" muovi da ", sorgente, " a ", destinazione) def hanoi (n, sorgente, destinazione, ausiliario): if n==1: muovi(sorgente, destinazione) else:

else: i = y+ return False #albero binario def ricercaalberobinario(x, A): if isVuoto(A): return False if x== radice(A): return True if x < radice(A): return ricercaalberobinario (x, sinistro(A)) return ricercaalberobinario (x, destro(A)) def inserzione (x, A): if isVuoto(A): return foglia(x): if x < radice(A): return nodo(radice(A), destro (A), inserzione(x), sinistro(A)) return nodo(radice(A), inserzione(x, destro(A)), sinistro(A)) def massimo(A): if isVuoto(destro(A)): return radice(A) return massimo(destro(A)) def scambia(a, i, j): if i!=j: a[i], a[j] = a[j], a[i] def padre(i): return (i-1)// def primofiglio(i): return 2*(i+1)- def heaptest(h, i): if i==0: return True return h[i] <= h[padre(i)] def heapinser(h, x): h.append(x) i=len(h)- while not heaptest(h, i): scambia(h, i, padre(i)) i = padre(i) def heapricostr(h, i, j): f1 = primofiglio(i) f2 = f1+ if f1<j:

k = f if f2<j: if h[f2]>h[f1]: k = f if not heaptest(h, j): scambia(h, k, padre(k)) heapricostr(h, k, j) def heapcostr(h): for i in range(len(h)-1, -1, -1): heapricostr(h, i, len(h)) def heapmassimo(h): max, h[0] = h[0], h.pop() heapricostr(h, 0, len(h)) return max #tabella di accesso diretto def pos(x): return x & 0x0000ffff def insert(a, x): a[pos(x)] = True def search(a, x): return a[pos(x)] #tab = 2**16 * [False] #insert(tab,10) #insert(tab,100) #insert(tab,35) #tabella di hash (chiave nulla= stringa vuota, cella vuota per inserire) def hashincr(n, h, j): return (h + j) % n def hashinsert(tab, x): h = hash(x)%n i = h for j in range(1, n): if tab[i] == x: return True if tab[i] == " ": tab[i]=x return True i = hashincr(n, h, j) return False def hashsearch(tab, x): h = hash(x)%n i=h for j in range(1, n):

j = c+ while i <= c and j <= r: if v[i] <= c and j <= r: temp[k] = v[i] i = i + 1 else: temp[k] = v[j] j = j+ k = k + 1 while 1 <= c: temp[k] = v[i] i = i + 1 k = k + 1 while j <= r: temp[k] = v[j] j= j + 1 k = k + 1 for k in range(1, r+1): v[j] = temp[k] def quicksort(v, 1, r):#due vettori con valori min e max del perno if 1 >= r: return i = 1 j = r x = v[(1 + r) // 2] while i<j: while x > v[i]: i = i + 1 while v[j] > x: j = j - 1 if i <= j: scambia(v, i, j) i = i + 1 j = j - 1 if 1 < j: quicksort(v, i, j) if i < r: quicksort(v, i, r) def heapsort(v): heapcostr(v) for i in range (1, n): scambia(v, 0, n-1) heapricostr(v, 0, n-1) #ricerca in grafi visitati = [] def goal(x): return x == 0

def DFR(nodo):#deap first visitati.append(nodo) if goal(nodo): return True for x in grafo[nodo]: if visitati.count(x) == 0: if DFR(x): return True return False def DF(nodo):#iterativo con uso pila p = [nodo] while len(p) > 0: nodo = p.pop() if visitati.count(nodo) == 0: visitati.append(nodo) if goal(nodo): return True for x in reversed(grafo[nodo]): p.append(x) return False def BF(nodo):#breadth first q = deque([nodo]) while len(q) > 0: nodo = q.popleft() if visitati.count(nodo) == 0: visitati.append(nodo) if goal(nodo): return True for x in grafo[nodo]: q.append(x) return False import heapq def euristica(nodo):#trova il cammino migliore pq = [] heapq.heappush(pq, nodo) while len(pq) > 0: nodo = heapq.heappop(pq) if visitati.count(nodo) == 0: visitati.append(nodo) if goal(nodo): return True for x in grafi[nodo]: heapq.heappush(pq, x) return False #distanza di hamming def HD(s, t):

codice(sinistro(t), s+"0", res) codice(destro(t), s+"1", res) import heapq def huffmann(h): n = len(h) for i in range(n): h[i] = [h[i][1], foglia(h[i][0])] heapq.heapify(h) for i in range(n-1): e1 = heapq.heappop(h) e2 = heapq.heappop(h) heapq.heappush(h, [e1[0]+e2[0], nodo("", e1[1],e2[1])]) if h[0][0]==1.0: print(" verifica OK, la probabilità è 1 ") res=[] codice(h[0][1], "", res) return sorted(res) def primiscemo(n): P =[True](n+1) P[0]=False P[1]=False for i in range(3,n+1): for j in range(2,1): if i%j == 0: P[i]=False return P def primiscemotto(n): P =[True](n+1) P[0]=False P[1]=False for i in range(3,n+1): j= while jj<=i: if P[j] and i%j == 0: P[i]=False break j=j+ return P def eratostene(n): P =[True](n+1) P[0]=False P[1]=False i= while i1<=n: while not P[i]: i=i+ k=ii

while k<n: P[k]=False k=k+ i=i+ return P def chars(S):#caratteri D={} for i in range(len(S)): c=S[i] D[c]= "" return sorted(list(d.keys())) def token(S, sep):#frasi for c in sep: S=S.replace(c," ") T=S.split() return T def words(S, sep):#vocabolario di frequenza D={} for s in tokens(S, sep): D[s]=D.setdefault(s, 0)+ 1 = sorted(list(D.keys())) W=[] for s in 1: W.append([D[s],s]) return sorted(W, reverse=True) #lista di più file di testo import glob def lista(dir): return glob.glob("path/*.txt") dir = "path/testi/" filenames = list(dir) print(filenames) def charsOneFile(file, D):#lista caratteri f = open(file,"r") line = f.readline() while line: for i in range(len(line)): c=line[i] D[c]="" line = f.readline() f.close() def charsAllFiles(fn): d={} for file in fn: print(file)