Scarica Risposte aperte algoritmi e strutture e più Prove d'esame in PDF di Fisica solo su Docsity!
- A cosa serve l’analisi algoritmica? L’analisi algoritmica intende studiare le specifiche di un algoritmo per trarre conclusioni sulle prestazioni della sua implementazione. L’analisi permette di confrontare quindi gli indici di performance di differenti approcci al problema e stabilire se la soluzione rispetta i vincoli sulle risorse necessarie prima di codificarlo.
- Annoverare un esempio di algoritmo basato sulla tecnica del divide et impera Molti algoritmi si basano su questa tecnica. Un algoritmo molto conosciuto che si basa su Divide et Impera è il MergeSort.
- A cosa serve un albero binario di ricerca? E’ utilizzato spesso per implementare insiemi dinamici di oggetti e tabelle di look-up che permettono di cercare un elemento basandosi su una chiave di ricerca.
- A cosa serve l’algoritmo di Huffman? L’algoritmo di Huffman è un algoritmo di compressione. E’ un algoritmo che associa ad ogni carattere una codifica prefix-free binaria costruita tramite un albero di ricerca (dizionario). Questo albero a sua volta è costruito in base alle occorrenze dei caratteri. Quindi al carattere con più occorrenze si associa il binario più corto ed il binario più lungo a quello con minore occorrenze. I simboli binari utilizzati devono essere prefix- free a lunghezza variabile. La codifica di Huffman è una codifica entropica e ne abbiamo tre varianti: fissa, statica e dinamica.
-A cosa serve una coda prioritaria? Può servire in una torre di controllo o per unire gli alberi più piccoli.
- Cosa si intende per pattern matching? Per pattern matching si intende il problema di determinare se una string S contenga o no una stringa P di dimensione minore o uguale a S. Vi sono vari algoritmi per ricercare di cui il più elementare è l’algoritmo NAIVE. L’algoritmo NAIVE è un algoritmo di forza bruta che scansiona tutta la stringa e per ogni elemento verifica se la sottostringa coincide. Un diverso algoritmo di PatternMatching è l’algoritmo di Boyer-Moore. Si pre-processa il pattern e fa il matching del pattern a partire dalla coda effettuando poi salti multipli utilizzando spostamenti totali o parziali.
- Che problema risolve l’algoritmo di Boyer-Moore? Risolve il problema di pattern-matching. Questo algoritmo pre-processa il pattern da ricercare e analizza la sorgente partendo dalla coda e facendo quindi dei salti totali o parziali.
-Cosa è una espressione regolare? Le espressioni regolari (Regular Expression) sono stringhe di testo appositamente codificate, usate come pattern per la corrispondenza (cioè matching) di insiemi di stringhe. E’ composta da caratteri che possono essere meta-caratteri o letterali. E’ quindi un pattern che può essere applicato ad un testo. Java fornisce nativamente un pacchetto per la creazione e la manipolazione di espressioni regolare java.util.regex
- Cosa vuol dire se l’entropia del messaggio è pari a 0? Motivare la risposta Motivare la risposta Se si facessero mille lanci con una moneta con due teste l’entropia del messaggio che contiene il risultato dei lanci sarebbe 0. Infatti l’entropia è in un certo senso la misura dell’informazione realmente contenuta in un messaggio. Entropia 0 implica un messaggio di dimensione minima ed una informazione predicibile.
-Cosa si intende per entropia (di informazione)? In teoria dell’informazione l’entropia di una sorgente di messaggi è l’informazione media contenuta nel messaggio. L’informazione tanto è più grande quanto meno probabile ed impredicibile è l’informazione. Maggiore sarà l’entropia di informazione e maggiore sarà la dimensione del messaggio.
- Che strategia algoritmica adotta l’algoritmo di Huffman? Nella costruzione dell’albero di codifica prefix-free utilizza una strategia greedy.
-Cosa è una coda prioritaria? Una coda prioritaria è un ADT. La pila adotta una strategia LIFO, la coda una strategia FIFO mentre la coda prioritaria adotta una strategia Least-In-First-Out ossia il primo elemento rimosso è il più piccolo.
-Come implementeresti una coda prioritaria? Si potrebbe implementare con una lista non ordinata. Ovviamente i metodi min e removeMin opereranno in tempo O(n). Si possono avere eventualmente migliori performance per questi metodi con una lista ordinata che tuttavia è più onerosa nella fase di inserimento.
- Cosa è un Plugin in Eclipse? Eclipse è una piattaforma progettata per sviluppare applicazioni stand- alone e web. È stata pensata come un IDE, un ambiente di sviluppo integrato. La piattaforma in se non fornisce una grande quantità di funzionalità all utente finale ma favorisce un rapido sviluppo di funzionalità integrate basate su un modello a plung-in
-Cosa è un ambiente di sviluppo integrato (IDE)? Un ambiente di sviluppo integrato (IDE )è un ambiente di programmazione che è stato impacchettato come un programma applicativo. Tipicamente è composto da: Un editor di codice Un compilatore Un debugger Una interfaccia utente (GUI) Un IDE può essere una applicazione stand-alone, o può essere incluso come parte di una o più applicazioni (framework) esistenti e compatibili. Un IDE fornisce un framework user- friendly per molti linguaggi di programmazione moderni (ad esempio, Java, C++, ecc)
- Che differenza c’è tra un linguaggio di programmazione interpretato ed uno compilato? Nei linguaggi interpretati (PHP, HTML) il codice sorgente viene interpretato al volo dall'interprete. Sono linguaggi molto immediati e portabili. Tuttavia gli errori si possono scoprire solo al momento dell’esecuzione. I linguaggi interpretati nascono dalla necessità di sviluppare programmi in grado di essere eseguiti su qualsiasi computer, indipendentemente dal S.O. installato. Questi linguaggi vengono tradotti istruzione per istruzione cosa che porta inevitabilmente ad un alto consumo di memoria ed ad una velocità d'esecuzione più lenta rispetto ai linguaggi compilati. L'essere tradotti nel momento della esecuzione comporta la loro traduzione in Assembly prima di girare sulla macchina, perché tradurre un linguaggio di alto livello direttamente in codice macchina (linguaggio binario) è un operazione impossibile. Il codice deve perciò essere tradotto in Assembly tramite l'apposito compilatore che poi lo ritraduce in binario tramite un assemblatore. Ma il linguaggio Assembly varia da macchina a macchina, perché ogni famiglia di processori ha un codice Assembly differente. E' necessario perciò scrivere quindi lo stesso programma nei diversi dialetti Assembly, per utilizzarlo su diversi processori, operazione questa dispendiosa in termini di tempo e risorse, quindi vengono preferiti linguaggi di alto livello che garantiscono la portabilità del codice. I linguaggi compilati (C, C++, Delphi) vengono verificati dal compilatore e quindi ogni istruzione viene trasformata nel corrispondente linguaggio macchina che potrà essere eseguito dal processore. Hanno il vantaggio di avere ottime prestazioni. Quando si scrive un programma, questo per funzionare deve essere letto dalla macchina. La macchina però è in grado solo di leggere sequenze di zeri e di uno e quindi nasce la necessità di un vero e proprio traduttore. I linguaggi di programmazione compilati, vengono letti immediatamente dalla macchina perché prima cono compilati, cioè trasformati in linguaggi macchina e poi successivamente inviati al processore. Se si desidera effettuare delle modifiche al programma, si dovrà modificare il codice sorgente rieffettuando la compilazione da capo sostituendo il vecchio il file. Java è un linguaggio intermedio ossia interpretato e compilato. Il codice sorgente viene tradotto nel formato bytecode che non dipende dalla macchina di destinazione. Queste istruzioni saranno poi interpretate dalla macchina virtuale (JVM) della macchina host (specifica per ogni Sistema Operativo).
-Cosa si intende per linguaggio intermedio In linguaggio in cui il codice sorgente viene trasformato in un linguaggio molto vicino al linguaggio macchina, in modo da poter essere eseguito da una macchina virtale. Il bit code ad esempio, è un linguaggio intermedio tra linguaggio sorgente (in questo caso java) e linguaggio macchina.
- Che differenza c’è tra classe ed oggetto? Una classe è un tipo di dato di cui da una descrizione strutturale e funzionale. L’oggetto invece è un’area di memoria allocata che contiene un aggregato di variabili e metodi definiti nella classe di cui l’oggetto è una istanza. Viene detta "classe" la struttura di un oggetto, cioè la dichiarazione dell'insieme delle entità che lo compongono. Un oggetto è come se fosse il "risultato" di una classe, è il prodotto che esce dalla forma "settata" nella classe. In realtà un oggetto è una istanza di una classe, quindi oggetto o istanza (eventualmente di occorrenza). La classe è composta da due parti: Attributi (detti anche dati membri), si tratta dei dati che rappresentano lo stato dell'oggetto; Metodi (detti anche funzioni membri), si tratta delle operazioni applicabili agli oggetti. Se si definisce la classe "animali", gli oggetti "cane", "cavallo" ecc saranno istanze di questa classe. Potranno eventualmente esistere più oggetti "cane", differenziati ad esempio dalla razza, o dal colore.Due istanze di classi possono avere tutti i loro attributi uguali senza essere un solo ed unico oggetto. Questo è il caso nel mondo reale dove, ad esempio, due gusti di gelato possono essere praticamente identici eppure distinti. Infatti, se mescolati, sarebbe impossibile distinguerli.
-Come si importano i package in Java? Il linguaggio Java adotta un approccio particolarmente utile per l'organizzazione delle classi all'interno dei programmi.Ogni classe pubblica a sé stante che viene definita in Java deve risiedere in un file distinto. Il nome del file deve coincidere con quello della classe, completato con l'estensione .java: ad es. una classe dichiarata come public class Window deve trovarsi nel file Window.java. Tale file può contenere anche le definizioni di altre classi, nessuna delle quali, però, può avere visibilità pubblica.Per agevolare l'organizzazione di grandi archivi di codice, Java consente la creazione di gruppi di definizioni di tipi di dati (come classi e enumerazioni) tra loro correlati, nella forma dei cosiddetti pacchetti (package).Perché la definizione di un tipo di dato appartenga a un pacchetto di nome nomePacchetto , il file contenente il suo codice deve appartenere a una cartella (o directory) che abbia lo stesso nome, cioè nomePacchetto, e deve iniziare con la riga: package nomePacchetto;Per convenzione, i nomi dei pacchetti devono essere scritti con lettere minuscole.Ad es potremmo definire un pacchetto architecture, che definisca classi come Window (finestra), Door (porta) e Room (stanza).Le definizioni di tipi di dati pubblici che si trovino in un file privo di un'esplicita dichiarazione package vanno a confluire in quello che viene chiamato pacchetto standard o di default (default package).Si può fare riferimento a un tipo di dato definito all’interno di un pacchetto usando il suo nome completo: ad es, se la classe Scanner è definita nel pacchetto java.util, per cui la potremmo utilizzare scrivendo java.util.Scanner e potremmo, in un nostro progetto, dichiarare e costruire un nuovo esemplare di tale classe usando un enunciato come questo: java.util.Scanner input = new java.util.Scanner(System.in) ;Scrivere nomi così lunghi per i tipi di dati però può essere difficoltoso.Per questo motivo, in Java è possibile utilizzare la parola chiave "import" per includere nel file su cui si sta lavorando, classi di altri pacchetti o anche interi pacchetti, con tutte le loro classi.Per importare una singola classe definita in uno specifico pacchetto scriviamo, all’inizio del file, la riga seguente: import nomePacchetto.nomeClasse;Questo a patto di ricordare che, con la chiamata import non è ammessa l’importazione di una classe se un’altra classe con lo stesso nome è presente nel file che si sta scrivendo oppure vi è stata importata da un altro pacchetto.Ad es. non potremmo importare entrambe le classi architecture.Window e gui.Window, per poi usare il nome incompleto Window, perché, è chiaro, sarebbe ambiguo.
-Cosa è un package in java? I package sono uno strumento per organizzare programmi complessi ed evitare conflitti di nomi.Il linguaggio Java adotta un approccio particolarmente utile per l'organizzazione delle classi all'interno dei programmi.Ogni classe pubblica a sé stante che viene definita in Java deve risiedere in un file distinto. Il nome del file deve coincidere con quello della classe, completato con l'estensione .java: ad es. una classe dichiarata come public class Window deve trovarsi nel file Window.java. Tale file può contenere anche le definizioni di altre classi, nessuna delle quali, però, può avere visibilità pubblica. Per agevolare l'organizzazione di grandi archivi di codice, Java consente la creazione di gruppi di definizioni di tipi di dati (come classi e enumerazioni) tra loro correlati, nella forma dei cosiddetti pacchetti (package). Perché la definizione di un tipo di dato appartenga a un pacchetto di nome nomePacchetto, il file contenente il suo codice deve appartenere a una cartella (o directory) che abbia lo stesso nome, cioè nomePacchetto, e deve iniziare con la riga: package nomePacchetto; Per convenzione, i nomi dei pacchetti devono essere scritti con lettere minuscole. Ad es potremmo definire un pacchetto architecture, che definisca classi come Window (finestra), Door (porta) e Room (stanza). Le definizioni di tipi di dati pubblici che si trovino in un file privo di un'esplicita dichiarazione package vanno a confluire in quello che viene chiamato pacchetto standard o di default (default package). Un package può essere pensato come ad una cartella del file system che raggruppa classi coerenti tra di loro. Il programma sarà costituito da un insieme di packages. I packages si importano utilizzando la direttiva import.
- Cosa sono i metodi di accesso di una classe? Talvolta è necessario consentire ad istanze di altre classi di accedere, in lettura o scrittura, allo stato di un oggetto e questo si fa attraverso i metodi di accesso. La pratica corretta in questi casi è:
- Mantenere " private " la definizione della variabile che rappresenta lo stato.
- Definire un metodo pubblico " getter " che ritorni il valore del campo, per consentire l'accesso in lettura.
- Definire un metodo pubblico " setter " che assegni un valore (passato a parametro) al campo, per consentire l'accesso in scrittura.
- Convenzionalmente, il metodo getter prende il nome get<nome_campo>, e il metodo setter prende il nome set<nome_campo> Per rafforzare l’incapsulamento dei dati, le definizioni di variabili di una classe dovrebbero essere private. Si definisce invece un metodo Getter (accesso in sola lettura) per avere il valore della variabile ed un metodo Setter per assegnare il valore della variabile (accesso in sola scrittura).
- Cosa si intende per incapsulamento dei dati? Si definisce incapsulamento la tecnica di nascondere il funzionamento interno (deciso in fase di progetto) di una parte di programma, in modo da proteggere le altre parti del programma dai cambiamenti che si produrrebbero in esse nel caso in cui questo funzionamento fosse difettoso, oppure di decidesse di implementarlo in modo diverso.
-Cosa si intende per ereditarietà? Si intende un meccanismo per il quale una classe eredità caratteristiche da un’altra classe. Utilizzando la direttiva extends si può definire una classe che eredità da un’altra metodi e variabili. Se la super classe appartiene allo stesso pacchetto ne deriva tutti gli elementi se appartenenti a pacchetti diversi ne deriva tutti gli attributi i public e protected.
-Cosa si intende per polimorfismo? Si intende la caratteristica di un oggetto che a run-time può esibire comportamenti e funzionalità diverse. Il polimorfismo è strettamente legato all’ereditarietà. Le caratteristiche dell’oggetto possono essere anche solo parzialmente definite all’atto della compilazione.
- Cosa sono le interfacce in java (costrutto interface)?
- Le interfacce formalizzano il concetto di interfaccia di una classe ossia i metodi che una classe deve esporre. L’interfaccia definisce un tipo e i metodi che le classi che implementano devono avere. Sostanzialmente le interfacce definiscono un protocollo al quale le classi devono attenersi.
- Interface I_Bycicle
- I_Bicycle x;
- X = new Bycicle();
- Come avviene la rimozione di un elemento in una lista?
- Si accede al nodo precedente. Si fa puntare come nodo successivo di questo elemento al nodo che punto l’elemento da eliminare.
- Se deve essere eliminata la coda l’operazione è onerosa.
- Come avviene l’inserzione di un elemento in una lista?
- Se in testa si alloca il nodo, si fa puntare il nodo alla testa della lista, si aggiorna il puntatore alla testa della lista.
- Se in coda si alloca il nodo, si raggiunge la coda e si fa puntare il nuovo nodo.
-Cosa è una lista concatenata semplice (o collegata o semplicemente concatenata) E’ un insieme di elementi definiti nodi che formano una sequenza lineare. Ogni nodo contiene un riferimento ad un valore/oggetto ed un riferimento ad un altro nodo. Si ha una testa che riferisce al secondo elemento della lista ed una coda che non riferisce a nessun altro elemento (null). E’ una struttura alternativa ad un array.
-Come avviene la ricerca di un elemento in una lista? Si attraversa la lista partendo dal primo elemento e testando via via tutti gli elementi fino a che l’elemento viene individuato o si raggiunge la coda della lista.
-Cosa è una coda (queue)? E’ un ADT ossia un tipo di dato astratto. Realizza una politica di tipo FIFO (first in – first out) e si può implementare utilizzando come struttura dati un array o una lista collegata.
-Che differenze ci sono tra una pila ed una coda? La pila realizza una politica LIFO mentre la coda una politica FIFO. Entrambi tuttavia possono essere realizzati con le stesse strutture dati quali array e liste collegate.
- Cosa è una Pila (stack)? E’ un ADT ossia un tipo di dato astratto. Realizza una politica LIFO (last in – first out) e si può implementare utilizzando come struttura dati un array o una lista collegata.
-Che somiglianze ci sono tra una pila ed una coda? Sono due ADT che possono essere realizzati con le stesse strutture dati ossia array e liste collegate.
- Cosa è una lista posizionale? E’ un tipo di dato astratto che realizza un contenitore di posizioni, ciascuna delle quali memorizza una posizione.
- Cosa è una collisione in una hash table? Quanti modi conosci per risolvere tali collisioni? Si verifica una collisione quando la funzione di hash per chiavi diversa restituisce lo stesso valore e quindi indice. Vi sono due tecniche per gestire le collisioni: tecniche di indirizzamento aperto e tecniche di concatenazione separata. Con la prima qualora si verifichi una collisione si cerca una diversa posizione utilizzando scansione lineare, quadratica o con una doppia funzione di hash. Nella seconda invece si struttura ogni bucket come una lista.
-Cosa è il tipo di dato astratto Mappa? A che serve? Il tipo di dato astratto Mappa (MAP) è appositamente progettato per essere in grado di archiviare e recuperare dei valori sulla base di chiavi di ricerca che li identificano univocamente. Le chiavi devono essere univoche. Sono utilizzate in molte applicazioni pratiche (DNS o dizionari).
- Cos’è un albero binario? Per albero binario si intende un albero in cui ogni foglia abbia al massimo due figli. Se le foglie dell’albero binario hanno 0 o 2 figli si parla di albero binario proprio. In un albero binario il numero di foglie è al massimo (n+1)/2 dove n sono i nodi dell’albero.
-Come realizzeresti un albero binario (a livello di struttura dati) Lo realizzerei con delle liste concatenate dove ogni elemento fa riferimento ad un elemento padre, ad un elemento figlio sx ed un elemento figlio destro oltre ovviamente al dato stesso. Da qui ne definirei proprio l’implementazione della classe foglia e della classe albero che di fatto punta alla foglia root.
- Chiarire la differenza tra tipo di dato astratto e struttura dati. Esemplificare. •Farei un esempio di ADT Pila e struttura dati che la implementa tramite array
-Che legami ci sono tra problemi-algoritmi-programmi? Problema è dare una risposta ad una domanda. La risposta al problema in genere prevede l’elaborazione di un dato di input che porta ad avere un dato di output. Si può identificare come un task da eseguire. L’algoritmo è la descrizione rigorosa delle azioni da compiere per risolvere un problema di qualsiasi genere. Programma è un testo (cioè sequenza di istruzioni) scritto in accordo alla sintassi e semantica di un linguaggio di programmazione.
- Cosa si intende per programmi Testo (ossia sequenza di istruzioni) scritto in accordo con la sintassi e la semantica di un linguaggio di programmazione
-Cosa si intende per problema? Problema è dare una risposta ad una domanda. La risposta al problema prevede l’elaborazione di un dato di input che porta ad ottenere un dato di output.
-Cosa si intende per algoritmo? L’algoritmo è la descrizione rigorosa delle azioni da compiere per risolvere un problema.
- Cosa misuriamo generalmente quando parliamo di complessità di un algoritmo? Misuriamo il numero di istruzioni elementari in linguaggio macchina per pesare in maniera indipendente dalle prestazioni della macchina la complessità
-Chiarire la differenza tra problema (computazionale) e istanza di un problema (computazionale). Esemplificare I problemi possono essere visti come funzioni che mettono in relazione degli ingressi o dati di input e delle uscite o dati di output. Una specifica configurazione dei dati di input invece è una istanza del problema.
-Cosa si intende per analisi asintotica? L’analisi asintotica misura l’efficienza di un algoritmo (o di una sua implementazione come programma) al crescere della dimensione dei dati di input
-Come misuriamo l’efficienza di un algoritmo? In base alla quantità di risorse (tempo, spazio) richieste dalla sua esecuzione.
- Come si fa a stabilire se l’algoritmo A è effettivamente più veloce dell’algoritmo B? E’ sufficiente analizzare il tempo di esecuzione asintotico dei due algoritmi
- E’ vero che maggiore è l’entropia di informazione e minore sarà la lunghezza del messaggio? Motivare la risposta NO,In teoria dell’informazione l’entropia di una sorgente di messaggi è l’informazione media contenuta in ogni messaggio emesso. L’informazione è tanto più grande quanto meno probabile era ed è una misura di impredicibilità. L’entropia di un messaggio in un certo senso è una misura di quanta informazione è contenuta realmente. Per cui maggiore sarà l’entropia di informazione e maggiore sarà la lunghezza del messaggio.
- E’ vero che minore è l’entropia dell’informazione e minore sarà la lunghezza del messaggio? Motivare la rispsta SI. In teoria dell’informazione l’entropia di una sorgente di messaggi è l’informazione media contenuta in ogni messaggio emesso. L’informazione è tanto più grande quanto meno probabile era ed è una misura di impredicibilità. L’entropia di un messaggio in un certo senso è una misura di quanta informazione è contenuta realmente. Per cui maggiore sarà l’entropia di informazione e maggiore sarà la lunghezza del messaggio.
- In quanto tempo è possibile selezionare contemporaneamente il minimo, il mediano ed il massimo di una sequenza di n elementi? In un tempo O(n). supponiamo che per ogni valore si faccia 3 confornti avrò O(3n) -> O(n)
- Illustrare le differenze qualificanti di una esplorazione in profondità e in ampiezza di un grafo La ricerca in profondità (DFS) imita l’esplorazione di un labirinto di Tremaux. Si può pensare come un modello in cui l’esploratore prosegue la sua marcia fino ad un vicolo cieco e quindi ritorna sui suoi passi.La ricerca in ampiezza (BFS) invece procede in turni suddividendo i vertici in livelli. E’ come avere una squadra di esploratori che esplorano un livello o passo per volta e quando è tutto esplorato passano al livello successivo.
- Illustrare le differenze tra l’algoritmo di Prim e quello di Kruskal. L’algoritmo Prim e l’algoritmo Kruskal sono entrambi esempi di algoritmi che utilizzano una strategia Greedy. Entrambi questo algoritmi permettono di determinare un MST (Minimum Spanning Tree – Minimo albero ricoprente) di un grafo che si assume connesso e i cui pesi degli archi siano positivi. L’algoritmo di Prim individua un albero che cresce ad ogni passo. L’algoritmo di Kruskal fonde di volta in volta coppie di alberi fino a determinarne uno che si estende su tutto il grafo. L’algoritmo di Prim parte da un nodo e ad ogni iterazione sceglie un arco di minimo peso che collega al gruppo dei nodi già selezionati uno di quelli non già facenti parte del gruppo. L’algoritmo di Kruskal partiziona i vertici in cluster e mantiene un MST per ogni cluster. L’idea è quella di fondere i cluster più vicini e i corrispondenti MST. Ad ogni passo fondo i cluster la cui connessione è a costo minimo.
-In quanto tempo è possibile ricercare una chiave in un albero binario di ricerca di n elementi? Il caso peggiore prevede 𝑂(𝑛). Il caso migliore ma anche il caso medio prevede 𝑂(log𝑛)
-Perché la complessità di un problema e il costo computazionale di un algoritmo vengono misurate usando la notazione asintotica?
Ci permettono di stabilire come la complessità aumenta all’aumentare dei dati di ingresso e quindi stabilire se la scelta dell’algoritmo risulta corretta in base alle stime dei dati che saranno utilizzati
-Perché è utile la notazione asintotica? Perché ci permette di stimare gli ordini di grandezza delle funzioni che vogliamo calcolare. L’analisi asintotica invece misura l’efficienza di un algoritmo al crescere della dimensione dei dati di ingresso.
-Perché è importante calcolare l'occupazione di memoria di un algoritmo? Perché se un algoritmo richiede troppo spazio di memoria potrebbe generare problemi che non trovano spazio di allocazione e potrebbero non produrre alcun risultato, o potrebbe non lasciare risorse sufficienti per la gestione normale della macchina.
-Quali sono i principi fondamentali della programmazione ad oggetti
La programmazione orientata a oggetti è un paradigma in cui un programma viene ideato e strutturato come un insieme di oggetti che interagiscono tra loro. Questi oggetti sono "modellati" dalla cosiddetta "classe" che dona all'oggetto attributi e funzioni specifici. I concetti fondamentali della programmazione a oggetti, oltre la "Classe", sono:
- Ereditarietà: Consente di definire nuove classi, o sottoclassi, come specializzazione di altre classi preesistenti. Consente di realizzare relazioni di funzione tra sottoclassi di una classe principale che abbiano comportamenti della stessa specializzazione. Permette di riadattare facilmente una classe alle nuove esigente riutilizzando codice già scritto e testato. - Incapsulamento: è la identificazione su un oggetto di 2 componenti. 1) L' interfaccia , cioè l'insieme dei metodi con cui l'utente accede all'oggetto 2) l' Implementazione che definisce le funzionalità dell'oggetto e dei dati che tratta. L'incapsulamento offre la possibilità di definire l’accessibilità di metodi con gli attributi private e public. - Polimorfismo: permette di invocare le operazioni su uno specifico oggetto donandogli la proprietà di assumere forme diverse nel tempo. Questo garantisce di avere a disposizione del codice che può essere modificato facilmente quando si estendono le funzionalità del programma. - Quanti dati di tipo primitivo conosci in java? Quali? Esistono 8 tipi primitivi più il tipo void che rappresenta la mancanza di un dato di ritorno da un metodo. I primitivi sono: - boolean: che può assumere due valori: false e true. - short: assume valori di 16 bit - byte: assume valori di 8 bit - int: assume valori di 32 bit - long: assume valori di 64 bit - float: a virgola mobile assume valori di 32 bit - double: a virgola mobile assume valori di 64 bit - char: assume valori di 16 bit
- Quanti modificatori di accesso conosci in Java? Quali? I modificatori di accesso sono parole riservate che forniscono al compilatore informazioni sulla natura del codice, dei dati e delle classi contenuti nei file sorgente. Fra tutti i modificatori alcuni possono essere raggruppati in una singola categoria che ne specifica il comportamento, essi sono i modificatori di accesso: private, protected, public e mancanza di modificatore private classe nessun modificatore class e package protected class, package e subclass public class, package, subclass, world I modificatori di accesso regolano la possibilità di accedere ad una Classe, ad un metodo o ad un attributo, da parte di una Classe o di un metodo, esterno od interno alla Classe stessa. In genere, solo le variabili di Classe, ma non è obbligatorio, hanno un modificatore che ne specifica la visibilità, le variabili dichiarate all'interno dei metodi non hanno modificatori di visibilità, d'altra parte esse sono visibili solo all'interno del metodo stesso.
- Quale situazione rappresenta il caso peggiore dell’operazione di ricerca in una lista? Ordinata al contrario.
- Quali sono le proprietà che deve soddisfare una buona funzione di hash? Perché? La funzione di hash è “buona” se minimizza le collisioni. Minimizzare le collisioni implica avere migliori performance in operazione di get, put e remove.
-Quali sono le tecniche più comuni di gestione delle collisioni in tabelle hash? Due tecniche: tecnica dell’indirizzamento aperto e tecnica di concatenazione separata.
-Quante foglie possono esserci al più in un albero binario di n nodi? Al più possono esserci (n+1)/2 foglie i nodi n8 sono nodi esterni senza figli e prendono il nome di "foglie" totale nodi =1+2+4+8 = 15 Nella ipotesi che i nodi n4 facciano ciascuno 2 nodi figli il numero di foglie massimo = (15+1)/2 = 16/2=
- Quanti confronti sono sufficienti nel caso peggiore per trovare l’elemento più piccolo in una sequenza di n elementi? N-1 confronti perché nel caso peggiore, ad esempio in un array di n interi non ordinato o ordinato al contrario la ricerca e confronto sequenziale deve passare tutti i valori fino a che non trova all'ultimo posto il valore cercato o non lo trova affatto. Quindi se gli elementi sono "n" l algoritmo dovrà scalare almeno n-1 elementi.
- Qual è la complessità dell’algoritmo di ricerca sequenziale, in funzione del numero di elementi n? O(n) Caso peggiore: l’elemento ricercato è l’ultimo dell’array o non è presente; Caso medio: se ogni elemento è equamente distribuito, ciascun elemento ha la stessa probabilità di essere trovato (O(N/2) —> O(N)); Caso migliore: l’elemento ricercato è il primo dell’array;
- Qual è la complessità dell’algoritmo di ricerca binaria, in funzione del numero di elementi n? O(log n) Caso peggiore: l’elemento ricercato non è presente nell’array. Ad ogni iterazione la dimensione degli elementi analizzati è la metà verrano effettuati log2(n) confronti; Caso medio: verranno effettuati circa la metà dei confronti del caso peggiore, quindi log2(n)/ 2;Caso migliore: l’elemento ricercato si trova nella posizione centrale dell’array quindi viene trovato al primo confronto; Il metodo di ricerca binaria ha una profondità di ricorsione O(log (n)) perché ad esempio, se la sequenza di dati contiene 65536 elementi nel caso di ricerca sequenziale, nel caso peggiore che si stia cercando proprio il valore 65536-esimo, l’algoritmo impiegherebbe 65536 confronti, mentre quello binario, nel caso di array ordinato,
Log2 65536 = 16 confronti.
-Quanti confronti esegue la ricerca sequenziale nel caso medio? (n+1)/
-Quali vantaggi introduce la ricorsione? E’ un costrutto molto elegante e semplice da capire oltre che implementare. Permette di scrivere in poche righe di codice algoritmi per la risoluzione di problemi anche molto complessi.
-Quali svantaggi introduce la ricorsione? In genere le prestazioni sono il grande svantaggio dei metodi ricorsivi che utilizzano una grande quantità di memoria.
-Quali sono gli algoritmi più efficienti, quelli ricorsivi o quelli iterativi? Quando è possibile bisogna ricorrere ad algoritmi iterativi che in genere sono più efficienti soprattutto in termini di utilizzo di memoria.
- Quali svantaggi introduce l’approccio greedy? Gli algoritmi Greedy trovano buone soluzioni (talvolta ottime) su certi problemi mentre su altri no. L’algoritmo greedy cerca la soluzione ottima localmente e quindi non trona indietro sui suoi passi ossia non mette in discussione il passo fatto al passo successivo. Tende quindi a fermarsi sui massimi locali che non necessariamente tendono al massimo complessivo del problema.
- Quali vantaggi introduce l’approccio greedy? Hanno strutture sub-ottime ossia trovano soluzioni ottime ai sottoproblemi.
- Quali vantaggi introduce l’approccio Divide et Impera? E’ particolarmente adatto a risolvere problemi difficili dato che frantuma il problema iniziale in sotto-problemi più semplici.Permette di strutturare e scoprire algoritmi particolarmente efficienti. E’ adatto al calcolo parallelo dato che suddivide il problema in sotto problemi. Usa efficientemente la memoria dato che un sotto- problema quando diventa abbastanza piccolo può essere risolto in cache
- Quali svantaggi introduce l’approccio divide et impera? E’ una tecnica ricorsiva e quindi porta con se i svantaggi della ricorsione. Non tutti i linguaggi supportano esplicitamente la ricorsione. Si deve fare attenzione che la memoria di stack sia sufficiente, La scelta dei casi base può portare a soluzioni con efficienze molto diverse.
- Quanti confronti vengono eseguiti dalla ricerca binaria nel migliore dei casi? Nel migliore dei casi nella ricerca binaria il primo test individua il candidato e quindi con un unico confronto si arriverebbe alla soluzione.
- Quando è preferibile utilizzare una lista di adiacenza e quando una matrice di adiacenza, per memorizzare un grafo? Una rappresentazione matriciale spreca molta memoria se il grafo è sparso e quindi in questo caso è più opportuna una lista delle adiacenze. L’utilizzo in questi casi di una matrice di adiacenza comporta inefficienza. Se il grafo è denso invece le costanti di proporzionalità di una matrice di adiacenza possono anche essere inferiori a quelli della lista o delle mappe. Se poi gli archi non hanno dati ausiliari (per esempio il grafo non è pesato) una matrice di adiacenza booleana può utilizzare un solo bit per cella. Peraltro se non è pesata la matrice è simmetrica e quindi si possono memorizzare solo i dati sopra la diagonale. Nelle matrici di adiacenza anche l’aggiunta e la rimozione di vertici è poco efficiente. Quindi si preferisce la rappresentazione tramite liste quando il grafo è sparso mentre è conveniente l’utilizzo della matrice quando il grafo è denso o se occorre alta efficienza nel rilevare se vi è un arco fra due vertici.
- Quante e quali strutture dati conosci per memorizzare grafi?
•Lista di archi
- Lista di adiacenze. Si rappresenta con un vettore di liste. Una lista per ogni vertice,
- Mappa di adiacenze
- Matrice di adiacenze
- Quanti archi contiene un minimo albero ricoprente di un grafo connesso? Un minimo albero ricoprente di un grafo connesso G=(V,E) contiene n-1 archi dove n è il numero di vertici appartenenti a V.
- Si facciano esempi di operatori in Java
- Abbiamo operatori di assegnazione, confronto, bitwise, condizionali, aritmetici e di autoincremento e autodecremento.
- Tra gli operatori aritmetici + - * / % << >> >>>
- / divisione
- % modulo
- << shift a sinistra
shift a destra
unsigned shift a destra
- Esempio 0x00000006 << 3
- Si facciano esempi di espressioni in Java c=(a+b)/15.51;
c=a*2+b;
c=1/(a-1)/(b+4);
c=Math.sqrt(aa - bb+x);
- Si faccia un esempio di classe java che realizzi l’incapsulamento dei dati: Andiamo a scrivere una classe che rappresenta un modello di oggetto di una certa fabbrica public class Fabbrica { private String modello; // rende l'attributo modello (come stringa) privato, cioè visibile solo all'interno della classe Fabbrica public void setModello(String n) { // attributo pubblico setModello (come stringa) per assegnare il valore a modello modello=n; } public String getModello() { // attributo pubblico getModello (come stringa) per recuperare il valore dell'attributo modello return modello; } } Usando il private si rendono gi attributi della classi visibili solo ed esclusivamente all'interno della classe, dunque potranno essere manipolati solo dai metodi della stessa classe.
- Si faccia un esempio di classi Java che realizzano il principio di ereditarietà:
- Class SuperClass {}
- Class InfClass extends SuperClass{}
- Si realizzi una interfaccia in java con almeno due metodi Interface I_Bycicle{ void changeGear(int gear); void brake();}
-Si definisca un tipo di dato astratto con due almeno due metodi:
- Una lista è una sequenza ordinata di elementi A1, A2, …, An
- Può contenere elementi di tipo arbitrario ma tutti dello stesso tipo
- Una lista deve supportare almeno questi metodi I. Creazione di una lista vuota II. Inserimento di un elemento III. Estrazione di un elemento IV. Restituzione del numero di elementi V. Svuotamento della lista
- Si faccia un esempio pratico in cui c’è bisogno di utilizzare array non monodimensionali. Possiamo definire un array come una matrice di N dimensioni. Nel caso in cui la dimensione sia 1, l'array può essere visto come un elenco.Nel caso in cui le dimensioni siano 2, l'array può essere pensato come una tabella di X righe e Y colonne. In generale si può pensare ad un array bidimensionale come una matrice o come un "array di array". Si può estendere questa idea teorizzando una matrice di qualsiasi dimensione realizzata come array di array di array. Esempio: tipo [ ][ ] Array = new tipo[Righe][Colonne] int [ ][ ] matrice = new int[12][25]; La variabile "matrice" sarà un array di 12 oggetti ciascuno dei quali è un array di 25 numeri interi. Si può fare riferimento a un elemento RC-esimo, riga-colonna, di un array di array con: nomeArray[R][C] dove R è l'indice Riga e C è l'indice Colonna
- Si definisca una lista come tipo di dato astratto Una lista prevede size, isEmpty, get(i), set(i,e), add(i,e), remove(i). Si può pensare come ad una Pila o Coda dove tuttavia gli elementi si possono inserire e rimuovere da posizioni arbitrarie. Non è una struttura necessariamente ordinata.
- Si parli delle tabelle di Hash. Le tabelle di hash sono una struttura dati che implementa l’ADT MAP. Tramite una funzione di HASH si associa ad ogni chiave un valore intero che a sua volta viene associato ad una posizione [0,N-1]. La funzione è una funzione di funzione che opera come detto una codifica ed una compressione. La funzione di hash deve minimizzare le collisioni (stesso indice associato a chiavi diverse). Le collisioni vengono gestite con due tecniche: tecnica di indirizzamento aperto (utilizzando scansioni lineari, quadratiche o doppie funzioni di hash) e tecniche di concatenazione separata.
- Si definisca il tipo di dato astratto Albero E’ un ADT che memorizza gli elementi in maniera gerarchica. Si compone di nodi che hanno una relazione genitore e figlio con l’eccezione della Root o Radice che sta in cima e non ha genitori. SE un nodo non ha figli viene definito foglia (o esterno). Un albero in cui ogni foglia ha al più 2 figli viene definito binario. Se ha 0 o 2 figli viene definito binario proprio. Profondità di un nodo numero di antenati. Altezza dell’albero massima profondità dei suoi nodi. Livello gruppo di foglie con la stessa profondità.
-Si definisca formalmente la notazione O-grande (big Oh)
Date due funzioni f(n) e g(n), si dice che f(n) è O(g(n)) se esistono le costanti positive c ed n0 tali che f(n)<=c*(g(n)) per ogni n>n0 8.
- Si definisca formalmente Omega-Grande Siano f(n) e g(n) funzioni che mappano interi positivi su reali positivi. Diciamo che f(n) e Omega(g(n)) se esiste c0 e n0>=1 tali che f(n)>=c*g(n). Consente di dire se una funzione è asintoticamente “maggiore o uguale di”
-Si definisca formalmente Theta-Grande F(n) è Theta(g(n)) se f(n) è O(g(n)) e f(n) è Omega(g(n)). Ci permette di dire se due funzioni crescono alla stessa velocità