martedì 19 dicembre 2023

Corso ALGORITMI PER LA PROGRAMMAZIONE: Lezione 1 Concetto di algoritmo


Cos'è il coding?

Il coding, o programmazione, è il processo di creazione di istruzioni che una macchina, come un computer, può seguire per eseguire una determinata attività. Queste istruzioni, scritte in linguaggi di programmazione, guidano il computer nell'esecuzione di compiti specifici. In altre parole, il coding è il modo in cui i programmatori comunicano con i computer per farli eseguire le operazioni desiderate.

La programmazione può assumere molte forme, dall'elaborazione di semplici calcoli matematici all'implementazione di complesse applicazioni software, sistemi operativi o giochi. Il coding è fondamentale per lo sviluppo di tecnologie digitali e ha un impatto significativo sulla nostra vita quotidiana.


Importanza del coding nel mondo moderno

Il coding è diventato una competenza fondamentale nel mondo moderno per diverse ragioni:

Automazione: Il coding consente di automatizzare compiti ripetitivi e complessi, aumentando l'efficienza e riducendo gli errori umani. Questo è fondamentale in settori come l'industria, la finanza e la gestione delle operazioni.

Tecnologia: La tecnologia è onnipresente nella nostra vita quotidiana. Dal nostro smartphone alla smart TV, dall'automobile alla lavatrice, tutti questi dispositivi funzionano grazie a software che è stato scritto da programmatori.

Innovazione: Il coding è alla base dell'innovazione tecnologica. Nuove tecnologie, applicazioni e servizi emergono costantemente grazie alla programmazione. Queste innovazioni cambiano il modo in cui lavoriamo, studiamo e ci divertiamo.

Lavoro e carriera: La domanda di esperti di programmazione è in costante crescita. La programmazione è una competenza altamente richiesta in molte industrie e offre opportunità di carriera ben retribuite.

Risoluzione di problemi: La programmazione è uno strumento essenziale per risolvere problemi complessi in campi come la scienza, la medicina e l'ingegneria.


1-Concetto di algoritmo

Un algoritmo è una serie finita e ben definita di istruzioni o passaggi logici che vengono eseguiti per risolvere un problema o compiere una serie di operazioni. Gli algoritmi sono una parte fondamentale dell'informatica e della programmazione in quanto forniscono una sequenza di passaggi che permettono di raggiungere un obiettivo specifico.


Caratteristiche principali degli algoritmi:


Input e Output: Ogni algoritmo ha un insieme di dati in ingresso (input) e produce un risultato (output) in base a tali dati. Gli input possono variare da casi di prova a casi di prova.

Un semplice esempio:
Ecco un esempio di algoritmo che calcola la somma di due numeri in input e restituisce il risultato come output:

ALGORITMO SommaDueNumeri

Input: Due numeri interi, numero1 e numero2

Output: La somma dei due numeri, risultato

Leggi numero1 dallo standard input

Leggi numero2 dallo standard input

Calcola risultato come la somma di numero1 e numero2

Stampa risultato sullo standard output

Fine

In questo esempio:

L'algoritmo prende in input due numeri interi, numero1 e numero2.

Calcola la somma dei due numeri e la memorizza nella variabile risultato.

Stampa il risultato sullo standard output in modo che l'utente possa vederlo.

Questo è un algoritmo molto semplice che dimostra il concetto di input e output. Gli algoritmi più complessi possono avere input e output più elaborati e affrontare problemi più intricati, ma il principio di base rimane lo stesso: prendere dei dati in input, elaborarli in qualche modo e produrre un risultato in output.


Sequenza di Istruzioni: Gli algoritmi sono composti da una sequenza ordinata di istruzioni o passi che vengono eseguiti uno dopo l'altro.


Un semplice esempio:
Input: Un numero intero numero

Output: Il numero incrementato di 1, risultato

Leggi numero dallo standard input

Calcola risultato come numero + 1

Stampa risultato sullo standard output

Fine

In questo esempio, le istruzioni dell'algoritmo vengono eseguite in sequenza:

L'algoritmo legge il valore di numero dall'input.

Calcola il valore di risultato sommando 1 a numero.

Stampa il valore di risultato sull'output.

Queste istruzioni vengono eseguite in ordine, uno dopo l'altro. Ad esempio, se forniamo il valore numero = 5 come input:

Esecuzione:

Input: 5

Calcolo: risultato = 5 + 1 = 6

Output: 6

L'algoritmo segue una sequenza di istruzioni chiara e prevedibile, con ciascuna istruzione che viene eseguita in successione per produrre il risultato desiderato. Questo esemplifica la proprietà di sequenza di istruzioni negli algoritmi.


Finitezza: Un algoritmo deve avere un numero finito di passaggi o istruzioni. Deve terminare dopo un certo numero di operazioni e non deve essere infinito.

La finitezza è una delle caratteristiche fondamentali degli algoritmi. Essenzialmente, indica che un algoritmo deve essere composto da un numero finito di istruzioni o passaggi e deve terminare entro un certo limite di tempo o dopo un certo numero di operazioni.

Questa caratteristica è cruciale perché garantisce che un algoritmo, una volta avviato su un insieme di dati di input, raggiunga uno stato finale o una soluzione desiderata dopo un numero definito di operazioni. Un algoritmo deve essere progettato in modo tale da terminare l'esecuzione e restituire un risultato in un tempo ragionevole, anche se potrebbe richiedere un tempo maggiore per eseguire operazioni complesse o elaborare grandi quantità di dati.

L'importanza della finitezza negli algoritmi risiede in diversi aspetti:

Prevedibilità: La finitezza consente di prevedere e gestire il tempo di esecuzione dell'algoritmo. Questo è essenziale nell'ambito dell'efficienza e della valutazione delle risorse necessarie per l'esecuzione.

Controllo: Un algoritmo finito può essere controllato e analizzato per identificare eventuali errori, inefficienze o possibili miglioramenti.

Evitare cicli infiniti: Garantisce che un algoritmo non entri in un ciclo infinito, ossia un loop che non termina mai, evitando così blocchi nel processo di esecuzione.

Fattibilità computazionale: La finitezza è una delle condizioni per stabilire se un problema è computazionalmente risolvibile. Problemi con algoritmi che non terminano possono essere considerati non risolvibili da un computer.

Ad esempio, se consideriamo un algoritmo di ricerca lineare in una lista per trovare un elemento specifico, l'algoritmo terminerà una volta trovato l'elemento o dopo aver verificato tutti gli elementi nella lista, garantendo la finitezza.


Un semplice esempio:

Ecco un esempio di algoritmo che rispetta la proprietà di finitezza, ovvero l'algoritmo termina in un numero finito di passi e non entra in un ciclo infinito:

Input: Un numero intero positivo n

Output: La somma dei primi n numeri interi positivi, somma

Leggi n dallo standard input

Inizializza la variabile somma a zero

Inizializza la variabile i a 1

Inizia un ciclo mentre i è minore o uguale a n

Aggiungi il valore di i a somma

Incrementa i di 1

Stampa somma sullo standard output

Fine

In questo esempio, l'algoritmo calcola la somma dei primi n numeri interi positivi. L'uso del ciclo mentre garantisce che l'algoritmo terminerà in un numero finito di passi. Indipendentemente dal valore di n, l'algoritmo attraverserà un numero finito di iterazioni per calcolare la somma.

Ad esempio, se forniamo il valore n = 5 come input:

Esecuzione:

Input: 5

Calcolo: Il ciclo eseguirà le seguenti iterazioni:

somma = 0 + 1 = 1

somma = 1 + 2 = 3

somma = 3 + 3 = 6

somma = 6 + 4 = 10

somma = 10 + 5 = 15

Output: 15

L'algoritmo termina dopo aver eseguito le cinque iterazioni necessarie per calcolare la somma dei primi cinque numeri interi positivi, dimostrando così la proprietà di finitudine.


Precisione: Ogni passo dell'algoritmo deve essere definito in modo chiaro e preciso, senza ambiguità. Gli algoritmi devono essere facilmente comprensibili e interpretati da un computer o da una persona.

La precisione è un altro elemento essenziale nelle caratteristiche degli algoritmi. Implica che ogni passaggio dell'algoritmo deve essere definito in modo chiaro, preciso e senza ambiguità, sia per una macchina (computer) che per un essere umano che lo interpreta o lo implementa.

Le istruzioni all'interno di un algoritmo devono essere definite in termini che non lasciano spazio a interpretazioni multiple o ambiguità. L'obiettivo della precisione è rendere l'algoritmo comprensibile sia per la macchina che lo esegue che per il programmatore o l'analista che lo legge o lo sviluppa.

Ciò significa che ogni istruzione o passaggio deve essere formulato in modo chiaro e ben definito, con regole specifiche e comportamenti prevedibili. Inoltre, le variabili, i comandi e le operazioni devono essere descritti in un modo che sia logicamente coerente e che non lasci spazio a interpretazioni ambigue.


Ad esempio, se consideriamo un algoritmo che calcola la somma di due numeri interi, ogni passaggio dovrebbe essere preciso e chiaro. Potrebbe essere rappresentato come:

Prendi il primo numero intero.

Prendi il secondo numero intero.

Somma i due numeri.

Restituisci il risultato della somma.

Questo esempio presenta istruzioni chiare e dirette che non lasciano spazio a interpretazioni diverse o incerte.


La precisione è essenziale perché un algoritmo ambiguo o poco chiaro può portare a errori di interpretazione o a risultati non corretti quando viene eseguito. Un algoritmo preciso e ben definito è più facilmente comprensibile e interpretabile, facilitando la comprensione, la manutenzione e l'esecuzione corretta delle istruzioni da parte del computer.


Determinismo: Gli algoritmi sono deterministici, il che significa che se vengono dati gli stessi input in circostanze identiche, genereranno sempre lo stesso output.


Un semplice esempio:

Ecco un esempio di algoritmo che dimostra il determinismo, ovvero il fatto che dati gli stessi input in circostanze identiche, l'algoritmo genererà sempre lo stesso output:

Input: Un numero intero input

Output: Il doppio del numero di input, output

Leggi input dallo standard input

Calcola output come il doppio di input

Stampa output sullo standard output

Fine

In questo esempio, se fornissimo lo stesso valore di input, ad esempio 5, all'algoritmo più volte, otterremmo sempre lo stesso risultato:

Esecuzione 1:

Input: 5

Calcolo: output = 2 * 5 = 10

Output: 10

Esecuzione 2:

Input: 5

Calcolo: output = 2 * 5 = 10

Output: 10

Indipendentemente da quante volte eseguiamo l'algoritmo con lo stesso input, otterremo sempre lo stesso output di 10. Questo dimostra la proprietà del determinismo degli algoritmi: gli stessi input in circostanze identiche generano sempre lo stesso output.


Risoluzione di problemi: Gli algoritmi sono utilizzati per risolvere problemi specifici. Possono essere progettati per eseguire una vasta gamma di compiti, dall'ordinamento di una lista all'ottimizzazione di processi complessi.

La loro capacità di risolvere problemi è dovuta alla loro natura di sequenze di istruzioni o passaggi ben definiti e strutturati, progettati per eseguire operazioni specifiche per ottenere un risultato desiderato.


Ecco alcuni esempi di come gli algoritmi vengono utilizzati nella risoluzione di problemi:


Ordinamento e ricerca: Algoritmi come il QuickSort, MergeSort, o Binary Search sono progettati per ordinare liste di elementi o trovare elementi all'interno di una lista, migliorando l'efficienza di queste operazioni.

Risoluzione di problemi matematici: Algoritmi come l'algoritmo di Euclide per il calcolo del massimo comune divisore o l'algoritmo di Dijkstra per la ricerca del percorso più breve in un grafo sono utilizzati per risolvere problemi matematici specifici.

Apprendimento automatico e intelligenza artificiale: Algoritmi di apprendimento automatico come le reti neurali, gli algoritmi genetici o gli algoritmi di clustering sono progettati per l'apprendimento e l'analisi dei dati.

Gestione di grandi quantità di dati: Algoritmi di compressione dei dati come LZW o algoritmi di ricerca e indicizzazione come l'indice invertito vengono utilizzati per gestire grandi quantità di informazioni in modo efficiente.

Ottimizzazione di processi: Algoritmi di ottimizzazione come l'algoritmo genetico o l'algoritmo del gradiente sono utilizzati per migliorare e ottimizzare processi o soluzioni.

La capacità degli algoritmi di risolvere problemi specifici è fondamentale in vari campi, tra cui l'informatica, la matematica, l'ingegneria, le scienze naturali e molti altri settori. La progettazione di algoritmi efficienti e precisi per affrontare problemi specifici è cruciale per ottenere soluzioni accurate e ottimali.


Efficienza: Gli algoritmi efficienti sono in grado di risolvere il problema nel minor tempo possibile e utilizzando meno risorse.

L'efficienza degli algoritmi è una caratteristica chiave che indica la capacità di un algoritmo di risolvere un problema nel minor tempo possibile e utilizzando meno risorse, come memoria, potenza di calcolo o tempo di esecuzione. Gli algoritmi efficienti sono cruciali per migliorare le prestazioni e ottimizzare il consumo di risorse.


Esempio:

Immaginiamo di dover cercare un elemento specifico in un elenco non ordinato di numeri. Un approccio ingenuo potrebbe essere utilizzare un'iterazione lineare per scorrere l'intera lista fino a trovare l'elemento desiderato. Questo potrebbe richiedere un tempo proporzionale alla lunghezza della lista.

python

Copy code

def ricerca_lineare(lista, elemento):

    for i in range(len(lista)):

        if lista[i] == elemento:

            return i  # restituisce l'indice se l'elemento è trovato

    return -1  # restituisce -1 se l'elemento non è presente nella lista

Questa è una ricerca lineare che può richiedere un tempo proporzionale alla lunghezza dell'elenco. Se la lista è molto grande, ciò potrebbe richiedere molto tempo per trovare l'elemento desiderato.


Un altro approccio più efficiente potrebbe essere utilizzare la ricerca binaria, ma è importante notare che questa funziona solo su un elenco ordinato.

python

Copy code

def ricerca_binaria(lista, elemento):

    inizio = 0

    fine = len(lista) - 1

    while inizio <= fine:

        medio = (inizio + fine) // 2

        if lista[medio] == elemento:

            return medio  # restituisce l'indice se l'elemento è trovato

        elif lista[medio] < elemento:

            inizio = medio + 1

        else:

            fine = medio - 1

    return -1  # restituisce -1 se l'elemento non è presente nella lista

La ricerca binaria, se applicata a un elenco ordinato, è più efficiente in termini di tempo rispetto alla ricerca lineare. Poiché riduce l'area di ricerca a metà ad ogni iterazione, il tempo di esecuzione è logaritmico rispetto alla lunghezza dell'elenco.

In questo esempio, l'algoritmo di ricerca binaria è più efficiente di quello di ricerca lineare in quanto può trovare l'elemento desiderato in meno tempo quando l'elenco è ordinato. Ciò dimostra come un algoritmo più efficiente può ridurre significativamente il tempo necessario per risolvere un problema specifico.


Gli algoritmi possono essere descritti in diversi modi:


Pseudocodice: Un modo informale di descrivere un algoritmo utilizzando una combinazione di linguaggio naturale e strutture di controllo della programmazione.

Il pseudocodice è un modo informale e non specifico di descrivere un algoritmo utilizzando un linguaggio che si avvicina al linguaggio di programmazione, ma è più simile al linguaggio naturale. Viene utilizzato per esprimere l'idea di un algoritmo senza preoccuparsi dei dettagli sintattici specifici di un particolare linguaggio di programmazione.

Il pseudocodice usa una combinazione di elementi del linguaggio naturale e strutture di controllo tipiche dei linguaggi di programmazione per rappresentare in modo chiaro e comprensibile gli step logici dell'algoritmo.


Esempio:

Supponiamo di dover scrivere un algoritmo per sommare gli elementi di una lista di numeri. Ecco come potrebbe apparire il pseudocodice per questo algoritmo:

scss

Copy code

PROCEDURE somma_lista(lista_di_numeri)

    SOMMA = 0  // Inizializzo la variabile SOMMA a zero

        FOR ogni NUMERO IN lista_di_numeri

        SOMMA = SOMMA + NUMERO  // Aggiungi il numero corrente a SOMMA

    END FOR

        RETURN SOMMA  // Restituisce il valore totale della somma

END PROCEDURE

Nell'esempio sopra:

PROCEDURE somma_lista(lista_di_numeri) indica l'inizio di una procedura che somma gli elementi di una lista.

SOMMA = 0 inizializza la variabile SOMMA a zero.

Il ciclo FOR attraversa ogni numero nella lista di numeri e aggiunge ciascun numero alla variabile SOMMA.

RETURN SOMMA restituisce il valore totale della somma.

Questo pseudocodice descrive in modo informale e chiaro come l'algoritmo dovrebbe funzionare senza preoccuparsi della sintassi specifica di un linguaggio di programmazione particolare. Questa forma di rappresentazione rende più facile comprendere e progettare l'algoritmo prima di tradurlo in un linguaggio di programmazione effettivo.


Diagrammi di flusso: Rappresentazione grafica di un algoritmo, che utilizza simboli e frecce per mostrare la sequenza delle operazioni.

I diagrammi di flusso sono una rappresentazione visiva degli algoritmi, utilizzati per illustrare la sequenza delle operazioni e le decisioni prese durante l'esecuzione di un algoritmo. Questi diagrammi sono composti da simboli grafici collegati da frecce che mostrano il flusso di controllo.

I principali simboli utilizzati nei diagrammi di flusso includono:

Ovale: Rappresenta l'inizio o la fine dell'algoritmo.

Rettangolo: Indica un'operazione o un'istruzione da eseguire.

Rombo: Rappresenta una decisione o un'alternativa, spesso con una domanda sì/no.

Parallelogramma: Indica l'input o l'output di dati.

Frecce: Mostrano il flusso di controllo tra i vari simboli, indicando la direzione dell'esecuzione dell'algoritmo.


Esempio:

Supponiamo di voler creare un diagramma di flusso per determinare se un numero è pari o dispari.

Diagramma di flusso:

css

Copy code

[Inizio] -> [Input del numero]

           |

           V

     [Verifica se il numero è pari?]

     |                 |

     |  Sì             | No

     |                 |

     V                 V

[Stampa "Il numero è   [Stampa "Il numero èpari"]                dispari"]

     |                 |

     V                 V

    [Fine]           [Fine]

In questo diagramma di flusso:

[Inizio] e [Fine] sono rappresentati da ovali che indicano l'inizio e la fine dell'algoritmo.

[Input del numero] è rappresentato come un parallelogramma, indicando l'input di un dato (il numero da verificare).

[Verifica se il numero è pari?] è un rombo che rappresenta una decisione. Se il numero è pari, l'algoritmo stampa "Il numero è pari"; altrimenti, stampa "Il numero è dispari".

Le frecce mostrano il flusso di controllo dall'input alla verifica e poi alle diverse uscite a seconda della decisione presa.

Questo diagramma di flusso fornisce una rappresentazione visiva chiara del processo decisionale per determinare se un numero è pari o dispari, seguendo le istruzioni specificate dal flusso dell'algoritmo.


Codice di programmazione: Una rappresentazione concreta di un algoritmo utilizzando un linguaggio di programmazione specifico.

Il codice di programmazione è una rappresentazione concreta e dettagliata di un algoritmo, scritto utilizzando un linguaggio di programmazione specifico. È una forma di istruzioni precise e dettagliate che un computer può comprendere ed eseguire per risolvere un problema o svolgere determinate operazioni.


Esempio:

Supponiamo di voler scrivere un algoritmo in linguaggio Python per determinare se un numero è pari o dispari. Ecco come potrebbe apparire il codice:

python

Copy code

def verifica_pari_dispari(numero):

    if numero % 2 == 0:

        print("Il numero è pari")

    else:

        print("Il numero è dispari")

# Esempi di utilizzo della funzione verifica_pari_dispari

verifica_pari_dispari(10)

verifica_pari_dispari(7)

In questo esempio:

La funzione verifica_pari_dispari() prende un numero come input e utilizza un'istruzione condizionale if-else per verificare se il numero è divisibile per 2. Se il resto della divisione è zero, il numero è pari e viene stampato un messaggio corrispondente. Altrimenti, se il resto della divisione è diverso da zero, il numero è dispari e viene stampato un altro messaggio.

Successivamente, vengono eseguiti due esempi chiamando la funzione verifica_pari_dispari() con i numeri 10 e 7 per verificare se sono pari o dispari.

Questo codice di programmazione Python rappresenta in modo concreto l'algoritmo che determina se un numero è pari o dispari. È scritto utilizzando la sintassi specifica di Python e può essere eseguito su un interprete Python per ottenere i risultati desiderati.

Il codice di programmazione è essenziale poiché fornisce istruzioni precise e dettagliate che vengono eseguite dal computer, consentendo di trasformare un algoritmo astratto in un programma eseguibile che risolve un problema specifico.


Gli algoritmi sono alla base della risoluzione dei problemi computazionali. Sono utilizzati in vari campi, dall'informatica e alla matematica, all'ingegneria e alle scienze naturali, per affrontare diverse sfide e trovare soluzioni efficienti ed efficaci.


Nessun commento:

Posta un commento