Seguici su Telegram, ne vale la pena ❤️ ➡ @trovalost
Vai al contenuto

Python 3 da professionisti: from zero to hero, il problema del cuoco italiano

Benvenuti! Con questo corso diviso in varie puntate andremo a spiegare i concetti basilari dietro Python, ed impararemo a programmare quel tanto che basta ad avere le competenze minime in questo ambito. Uso questo metodo anche nelle scuole tecniche di informatica e, in genere, riescono a seguire facilmente tutti. Seguiteci anche voi e non rimarrete delusi: il corso è adatto da 0 a 100 anni, from zero to hero.

Introduzione e concetti base di informatica

algoritmo

Algoritmo: Un algoritmo è una sequenza di istruzioni ben definite e ordinate che risolve un problema specifico. È come una ricetta che segue passi precisi per ottenere un risultato desiderato. Un algoritmo deve essere scritto in modo chiaro, preciso, inequivocabile.

problema

Problema: Un problema è una situazione o una sfida che richiede una soluzione. Può essere qualsiasi cosa, dalla risoluzione di un puzzle matematico alla gestione di un compito quotidiano.

modello

Modello: In informatica, un modello è una rappresentazione semplificata o astratta di qualcosa, come un sistema, un processo o un concetto complesso. I modelli aiutano a comprendere e spiegare le caratteristiche e il comportamento di ciò che stanno rappresentando.

bottom up

Bottom up: Il metodo “bottom-up” è un approccio in cui si affrontano le componenti più piccole o elementari di un problema per poi costruire una soluzione completa. Si parte dai dettagli più semplici e si arriva gradualmente a una visione d’insieme.

top down

Top down: Il metodo “top-down” è un approccio in cui si inizia con una visione d’insieme o ad alto livello di un problema e poi si suddivide in sottoproblemi più piccoli. Si parte dall’astratto per arrivare ai dettagli.

deterministico

Deterministico: Un processo o un algoritmo è detto “deterministico” se produce sempre lo stesso risultato quando viene eseguito più volte con gli stessi dati iniziali. Non ci sono elementi casuali o incertezze / probabilità nel processo.

non deterministico

Non deterministico: Un processo o un algoritmo è detto “non deterministico” se può produrre risultati diversi anche quando viene eseguito più volte con gli stessi dati iniziali. Può coinvolgere elementi casuali o incertezze / probabilità che possono influenzare il risultato finale.

Python 3

Python 3 è la terza versione del linguaggio di programmazione Python. È un linguaggio di programmazione ad alto livello, versatile e facile da imparare. È ampiamente utilizzato per lo sviluppo di applicazioni, scripting e analisi dei dati.

Il problema del cuoco italiano

Tanto per avere un problema di riferimento che non sia troppo difficile da immaginare, proviamo a pensare ad un cuoco che deve occuparsi di compiti classici della cucina (cucinare, neanche a dirsi); tra questi c’è quello, facile solo in apparenza, di rispettare i tempi di cottura della pasta.

Prendo questo esempio per un motivo specifico: si tratta di un compito archetipico per l’informatica (archetipico significa che è basilare, alla base di molte altre cose simili, si può applicare a molti altri ambiti e non solo alla cucina); poi lo trovo interessante perché “costringe” chi prova programmare a confrontarsi immediatamente con le difficoltà tipiche di questo mondo – a dispetto della sua semplicità di formulazione).

Ecco come potrebbe essere esteticamente il nostro amatissimo cuoco-informatico.

generated via starryAI - an italian cook like in simpsons and griffing
generated via starryAI – an italian cook like in simpsons and griffin

Nella pratica, il problema del cuoco italiano che vogliamo risolvere si potrebbe formulare come segue:

P = { Dati in input due pacchi di pasta P1 e P2 – diverso da P1 – aventi rispettivamente tempi di cottura T1 e T2 – diverso da T1 – sequenziare correttamente la cottura, e mostrare in output la cronologia delle attività che devono essere svolte. }

Se ad esempio P1 = spaghetti e P2 = penne, potremmo avere che P1 = 8 e P2 = 12, dove P1 e P2 sono espressi in minuti, per il momento. Andiamo a vedere la formulazione base dell’algoritmo risolutivo, un passo alla volta, e poi proviamo a complicarlo un pochino per volta.

Formulazione base dell’algoritmo del cuoco

Dati i tempi di cottura T1 e T2, come facciamo a stabilire se debba essere messa nella pentola, una volta raggiunta la temperatura di bollitura, la pasta P1 oppure la P2? Individuiamo da subito la regoletta base per il sequenziamento:

SE T1 > T2 ALLORA SEQ={P1, P2}

SE T1 < T2 ALLORA SEQ={P2, P1}

Non ci serve la regola T1=T2 e non ci poniamo il problema (anche se da matematici puri, in un certo senso, dovremmo farlo), perchè la formulazione del problema è solida e inequivocabile da questo punto di vista: . Vediamo quella doppia regola funziona: ammesso di avere P1 = spaghetti e P2 = penne, abbiamo visto che P1 = 8 e P2 = 12, per cui 8 è minore di 12 e ricadiamo nel caso B, per cui mettiamo a bollire prima le penne (che hanno un tempo di cottura più lungo, giustamente), e soltanto dopo mettiamo gli spaghetti. Quando mettiamo gli spaghetti? Non è difficile rispondere, basta fare la differenza:

T2 – T1 = 12 – 8 = 4,

per cui dopo 4 minuti dall’inizio della cottura delle penne andremo a mettere a bollire gli spaghetti.

Venuta fame, vero? È venuto il momento di individuare gli istanti temporali che vogliamo mettere a fuoco, e vale la pena ragionare prima da alto livello e poi scendere nel dettaglio (approccio top down). Prima domanda: quanto sarà il tempo di cottura complessivo? È importante saperlo perchè in futuro potremo stimare, ad esempio, il tempo medio di attesa di ogni cliente del ristorante. A quanto ammonta il tempo complessivo di cottura? Vediamo:

  • sicuramente 8 minuti non mi bastano, dato che ce ne vorranno almeno 12 per le penne;
  • NON posso farmi tentare dall’idea di sommare i tempi, perchè otterrei un tempo differente che è sovrastimato (12 + 8 = 26 sarebbe il tempo necessario a cuocere una pasta, scolarla e cuocere l’altra in momenti diversi);
  • …e quindi? Il punto iniziale su cui abbiamo ragionato ci fornisce già una risposta valida: ci vorranno 12 minuti per cuocere le due paste con tempi di cottura diversi, per il problema P appena formulato. In generale, la durata della procedura è data da m = max(T1, T2).

A questo punto siamo pronti ad abbozzare il nostro micro-algoritmo in Python 3, sfruttando pochissime strutture dati, giusto per capirci.

Import

Import significa importa, e serve ad includere nel codice determinate librerie standard (e certi studenti pensano, in questi casi: sì, ma a noi cosa che ce ne import…a!). Vediamo passo passo cosa stiamo cercando di spiegarvi: importare significa mettere dentro, allo scopo di riutilizzare in seguito: quindi è come “assumere” un dipendente che faccia un certo compito, nello specifico estrarre numeri casuali. Librerie significa pezzi di software riutilizzabili, standard significa che tutte le versioni di Python ne sono dotate.

Generare due numeri casuali (t1, t2) nel range min, max (con max > min) è semplice come scrivere:

import random # importazione libreria random
t1 = random.randint(min,max) # tempo di cottura casuale pasta 1
t2 = random.randint(min,max) # tempo di cottura casuale pasta 2

Funzione randomica per generare tempi casuali di cottura

Si era detto che queste tre righe di codice erano in grado di generare due numeri casuali:
import random # importazione libreria random
t1 = random.randint(1,100) # tempo di cottura casuale pasta 1
t2 = random.randint(1,100) # tempo di cottura casuale pasta 2

C’è pero’ un piccolo bug in questo approccio: può infatti capitare, in una singola circostanza, che t1 sia uguale a t2, dato che l’algoritmo è non deterministico, ovvero estrae in modo “cieco” i numeri nel range da 1 a 100, nello specifico. E come la risolviamo? Non è difficile: basta dire all’algoritmo di “aspettare” finchè i numeri sono diversi!

import random # importazione libreria random
t1 = random.randint(1,100) # tempo di cottura casuale pasta 1
t2 = random.randint(1,100) # tempo di cottura casuale pasta 2

while (t1==t2): # pessimismo!  
  #ricalcola
  t1 = random.randint(1,100) # tempo di cottura casuale pasta 1
  t2 = random.randint(1,100) # tempo di cottura casuale pasta 2

Chiaro cosa stiamo facendo (…qui la gente risponde “noooooh“, in genere :-) )? Stiamo dicendo: nelle prime tre righe “prova” a generare due numeri casuali tra 1 e 100. Dopo che l’hai fatto, se sono già diversi vai avanti; se invece fossero uguale, malauguratamente, riprova a fare l’estrazione. Ora torna indietro e controlla di nuovo: sono diversi? OK, altrimenti rifai l’estrazione fin quando non hai finito e non hai generato due numeri casuali veramente diversi tra loro.

E come chiedo sempre anche in classe, instancabilmente, ogni settimana…

Differenza tra i tempi

Qui siamo su un compito facile, una differenza tra minuti intera è una differenza tra numeri interi: se il primo numero è maggiore del secondo, sottrai il secondo dal primo per ottenere la differenza di tempo che il cuoco deve considerare.

if ( t1 > t2 ):
  dopo_quanto = t1 - t2
else:
  dopo_quanto = t2 - t1

Vale la pena osservare, senza complicarci la vita per ora, che se i tempi di cottura non fossero interi sarebbe tutto un calcolo differenti, perchè dovremmo ricorrere all’aritmetica a modulo 60 (significa che passati i 59 si deve tornare a 0, e che 1,5 minuti significa 1 minuto e 30 secondi…).

Il clou: mostrare i tempi nel formato orario (HH:mm:ss)

Sembra facile sulla carta, ma non lo è: noi finora abbiamo ragionato su durate espresse in modo relativo, 7 minuti, 12 minuti ecc. Ma se invece volessimo visualizzare gli istanti temporali assoluti, ovvero alle 12:35 metti gli spaghetti e alle 12:43 metti gli spaghetti? Non sarebbe male – e soprattutto sarebbe utile al povero cuoco, a cui verrebbe comodo sapere ora e minuti precisi in cui fissare le proprie alert per una cottura perfetta.

Prima di tutto, Python 3 permette di calcolare l’istante temporale dell’ora, del presente, che nel momento in cui scrivo è il seguente.

t0 = 16:40:23

Come si fa? Prendiamo la variabile t0 (si legge “t con zero” e rappresenta l’istante temporale di partenza dell’osservazione di un fenomeno) e assegnamo, mediante operatore assegnamento, il valore della funzione time.time(), ovvero:

t0 = time.time()

time prima del punto sarà la libreria che import(eremo), mentre invece time() è la funzione automatica (già pronta all’uso) che stiamo richiamando. Attenzione che t0 è un numero, in questo momento, mentre a noi interessa una data e un’ora. Facile, anche qui, convertire, usando:

time.strftime(argomenti)

dove gli argomenti saranno due:

  • il primo è il formato della data, in questo caso “%I %M %p”per dire ore, minuti, secondi;
  • il secondo è time.localtime(t0).

Quindi:

time.strftime("%I %M %p",time.localtime(t0))

che andremo a stampare con print:

print ( time.strftime("%I %M %p",time.localtime(t0)) )

Ammesso che t0 siamo espresso in secondi, dovremo aggiungere dopo_quanto che sarà la differenza di secondi che abbiamo visto prima, quindi tempo finale (tf):

tf = t0 + 60 * dopo_quanto

Quindi per questa prima fase avremo:

import time
import random

import random # importazione libreria
t1 = random.randint(5,20)

# tempo di cottura casuale pasta 1
t2 = random.randint(5,20)
# tempo di cottura casuale pasta 2
while (t1==t2): # pessimismo!
t1 = random.randint(5,20) # tempo di cottura casuale pasta 1
t2 = random.randint(5,20) # tempo di cottura casuale pasta 2

print ("t1=",t1)
print ("t2=",t2)

# devo cuocere entrambe nella stessa pentola
# senza farle scuocere: l'algoritmo prevede
# che uno debba mettere prima il tempo di cottura maggiore
# poi quello minore dopo

if ( t1 > t2 ):
dopo_quanto = t1 - t2
else:
dopo_quanto = t2 - t1

#

t0 = time.time()
tf = t0 + 60 * dopo_quanto
print ( time.strftime("%I %M %p",time.localtime(t0)), " metto pasta 1" )
print ( time.strftime("%I %M %p",time.localtime(tf)), " metto pasta 2" )

E il seguente sarà il codice completo che trovate qui.

Conclusioni

Il problema che abbiamo visto possiede delle caratteristiche molto semplici, ma disconferma lo stereotipo secondo il quale i problemi semplici debbano avere una soluzione necessariamente semplice: è falso, al limite può essere vero nella vita di ogni giorno e in alcuni casi specifici, ma nell’informatica di Python non vale quasi mai. Anche interessante capire che abbiamo inserito un sacco di dettagli strada facendo, E che la nostra scelta è stata anche avveduta perché se avessimo inserito dati in più o non avessimo considerato l’unità di misura avremmo fallito nel nostro intento.

È chiaro per tutti?

 

Quello che ci resta (se ci resta9 di questa prima lezione è subito detto:

  1. È importante assicurarsi che la formulazione del problema sia correttamente posta: E all’inizio non avessi specificato che i tempi di cottura dovevano essere diversi sarei potuto incappare in bug molto subdoli, oltre che difficili da rilevare.
  2. Il modo migliore per familiarizzare con variabile assegnamenti e via dicendo è quello di vederli nella pratica: invece di utilizzare i classici algoritmi che in genere sono abbastanza astrusi per un principiante, siamo andati a prendere un problema della vita di ogni giorno che tutti abbiamo affrontato almeno una volta.
  3. Il problema si presta al margine di miglioramento notevole: possiamo far vedere il cronometro in tempo reale, possiamo creare uno che ci avvisano automatico data in ingresso il tipo di pasta o meglio i tempi di cottura della pasta, possiamo fare 1000 varianti e creare la nostra personalizzata il modo in cui più ci aggrada. Amò restando però quello che abbiamo detto nei punti precedenti.

Grazie e alla prossima!

👇 Da non perdere 👇



Questo portale esiste da 4493 giorni (12 anni), e contiene ad oggi 5125 articoli (circa 4.100.000 parole in tutto) e 15 servizi online gratuiti. – Leggi un altro articolo a caso
5/5 (1)

Ti sembra utile o interessante? Vota e fammelo sapere.

Questo sito contribuisce alla audience di sè stesso.
Il nostro network informativo: Lipercubo - Pagare online (il blog) - Trovalost.it