Lagrangiana

Ogni tanto mi scappa da scrivere un pensiero su altri argomenti, e la Fisica è sempre fonte di ispirazione.

Ogni tanto indosso questa T-shirt che ho acquistato al CERN di Ginevra qualche tempo fa

La lagrangiana del Modello Standard
La lagrangiana del Modello Standard

La formula scritta sulla maglietta è la lagrangiana del modello standard.

Invariabilmente, ogni volta che la indosso, mi chiedono: cos’è una lagrangiana? Oppure cosa vuol dire Modello Standard?

La cosa mi fa piacere perché ragionare di Fisica mi piace sempre un sacco.

La lagrangiana nella Meccanica Classica

La lagrangiana è una funzione inventata dal matematico italo-francese Giuseppe Luigi Lagrange nel XVIII secolo nell’ambito della disciplina denominata Meccanica Analitica, che è un ramo della Fisica Matematica nella quale la Meccanica di Newton viene formulata come una teoria matematica astratta.

Che senso ha astrarre una materia pratica come la meccanica?

La cosa ha senso qualora ci si imbatta nel desiderio di voler evitare di reinventare la ruota ogni volta che ci si presenta un problema leggermente diverso dal precedente e dover ogni volta ristudiarlo da capo ripercorrendo per il 90% la strada fatta in precedenza. Uno degli aspetti potenti della Matematica è la capacità di classificare i problemi e trovare strategie di soluzione comuni per intere classi di problemi. Astrarre vuol dire proprio questo: individuare caratteristiche comuni a problemi in apparenza diversi e arrivare alla soluzione con un unico strumento!

Quando alle superiori (al liceo, o all’itis) ci veniva dato un problema di meccanica (tipo: un rullo è libero di muoversi su un piano inclinato ecc.) non sapevamo ancora che esistessero due tipi di approccio per arrivare alle cosiddette equazioni del moto – delle equazioni differenziali che, una volta risolte, ci dicono come evolverà il sistema date certe condizioni iniziali (possiamo calcolare quanto ci mette il barile a rotolare fino in fondo per esempio).

Un approccio è quello di cercare di scrivere direttamente l’equazione di Newton

F = ma = m \ddot{x}

dove x è la posizione del baricentro del sistema per esempio. Per problemi semplici è sicuramente la via più diretta. Ma quando le cose si fanno complicate (orbita di un satellite attorno ad un corpo celeste, pendoli doppi, etc..), Lagrange ha dimostrato che si perviene alle stesse equazioni per questa via:

si calcola l’energia cinetica del sistema che è una funzione della velocità:

T = T(\dot{x})

poi si calcola l’energia potenziale del sistema (se ad esempio siamo in un problema in cui c’entra la gravità, esiste il potenziale gravitazionale):

V = V(x)

che è funzione della posizione. Si definisce la funzione lagrangiana o, più in breve, lagrangiana:

L(x, \dot{x}) = T - V

e si dimostra che l’equazione differenziale

\frac{d}{dt}\frac{\partial L}{\partial \dot{x}}-\frac{\partial L}{\partial x} = 0

è equivalente alla equazione di Newton che descrive il sistema. In sistemi molto complessi è molto più facile pervenire alle equazioni del moto attraverso la lagrangiana che non “a naso” cercando di costruire direttamente le equazioni di Newton.

La lagrangiana in Meccanica Quantistica

La lagrangiana della maglietta è un formalismo che si usa in teoria dei campi ed è l’estensione a questa disciplina del concetto che Lagrange elaborò per risolvere problemi di Meccanica Classica (lui non la chiamava così, siamo noi che abbiamo attaccato l’aggettivo “classica” per distinguerla dalla Meccanica Relativisitica o dalla Meccanica Quantistica).

A grandi linee ogni riga della maglietta è un pezzo della teoria del Modello Standard (che è il modello che spiega tre delle 4 forze fondamentali dell’Universo: la forza elettromagnetica – che ti fa tenere il cane per il guinzaglio, la forza forte, che tiene insieme i nuclei degli atomi, la forza debole, che permette alle particelle di trasformarsi, e la forza gravitazionale, che ci tiene attacati per terra ma che finora è la grande esclusa dal Modello Standard) che prevede

  • l’Elettromagnetismo (prima riga),
  • l’interazione forte e debole (interazione di un bosone con un fermione: seconda riga),
  • l’interazione dei fermioni con il campo di Higgs (terza riga) che è il campo che dona la massa alle particelle;
  • la quarta riga (fino a -V(\Phi)) descrive l’interazione dei bosoni della forza debole (i bosoni W^{\pm}, Z_0 previsti a Abdus Salam negli anni ’60 e identificati da Carlo Rubbia al CERN nel 1984) con il campo di Higgs
  • l’ultimo termine -V(\Phi) è il potenziale del campo di Higgs

I bosoni sono le particelle mediatrici della forza (la più famosa è il fotone, che veicola l’interazione elettromagnetca). I fermioni sono le particelle di materia ordinaria (protoni ed elettroni per esempio).

Come vedete in questa lagrangiana non si parla di gravità. includere la gravità nella teoria di campo quantistica è una delle sfide che stanno impegnando i fisici e i matematici da mezzo secolo e ancora non si vede la luce in fondo al tunnel.

Come potete immaginare, la Lagragiana nell’ambito della Meccanica Quantistica ha un ruolo un po’ diverso da quello della Meccanica Classica: invece di fornirci le leggi orarie del moto, o “traiettorie” x(t), ci fornisce l’evoluzione di un campo \Phi(t).

Per chi volesse approfondire, qui è spiegato meglio senza addentrarsi troppo nei dettagli.

Fatemi siete arrivati fin qui se vi è piaciuto l’articolo.

Python in pillole: quando append() produce una lista di elementi tutti uguali

Provando a costruire una lista Python, con un metodo learn di una certa classe P che ritorna una lista

for i in range(1,5):
    w=P.learn()
    print(type(w))
    hist_w.append(w)
    print("hist_w", hist_w)

I valori di w ritornati ad ogni ciclo erano del tipo

[-1.7968410695441766, 1.1, 0.20315893045582345]
[-2.4943448781251716, 1.1, -0.4943448781251717]

ma la lista hist_w al primo ciclo conteneva

[[-1.7968410695441766, 1.1, 0.20315893045582345]]

Al secondo ciclo conteneva

[[-2.4943448781251716, 1.1, -0.4943448781251717],
[-2.4943448781251716, 1.1, -0.4943448781251717]]

e così via, cioè anziche venire aggiunto l’ultimo elemento (anche lui una lista) alla lista, venivano sostituiti tutti gli elementi della lista con l’ultima lista calcolata.

Il problema è che stavo copiando ogni volta il riferimento alla variabile w (che è lo stesso ad ogni ciclo) e non il contenuto della variabile stessa (che cambia ad ogni ciclo):

[<riferimento a w>, <riferimento a w>, …]

Per disinnescare l’errore ho fatto così:

for i in range(1,5):
    w=P.learn()
    print(type(w))
    hist_w.append(w[:])
    print("hist_w", hist_w)

Notate il [:] accanto al nome della variabile.

Git in pillole: il file .gitignore

.gitignore o .git/info/exclude? Vediamo le differenze e il perché a volte Git sembra ignorare le nostre direttive sull’esclusione di alcuni file dal ciclo di vita del software.

Differenza tra .gitignore e .git/info/exclude

Entrambi i file servono a Git per stabilire quali file non entrano nel ciclo di versionamento.

Essi agiscono ad un diverso livello.

Il file .git/info/exclude serve per escludere i file nel proprio clone locale; se qualcuno che collabora con noi non ha escluso gli stessi file che abbiamo escluso noi, potremmo ritrovarci – se facciamo un clone o un fetch – file che sono entrati nel versionamento nell’origin ma che io non ho perché esclusi, o viceversa. Quindi è bene, quando si devono escludere dei file globalmente (per tutti i programmatori) agire sul file .gitignore che invece viene versionato – per cui ogni repository locale se lo trova. Se dobbiamo agire in modo più granulare sui file da versionare (o meno) directory per directory, è possibile scrivere un apposito file .gitgnore in ogni directory in cui serve.

Git sembra ignorare la direttiva di ignorare file

Ho messo dei file da escludere nel file .git/info/exclude (o in .gitignore) ma continuo a vederli come new file quando eseguo il comando git status:

$ git status
Sul branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   app/Http/Controllers/BaseController.php
        new file:   storage/logs/laravel-2020-07-22.log
        new file:   storage/logs/laravel-2020-07-23.log
        ...
  

In particolare non voglio che i file di log entrino nel versionamento. Ho però inserito correttamente la riga

storage/logs*

in entrambi i file .gitignore e .git/info/exclude. Come mai Git continua a mostrarmeli come file da versionare?

Git utilizza il file .gitignore nel momento in cui aggiunge i file; una volta aggiunti all’indice dei file da versionare Git continua a considerarli tali, a dipetto di modifiche successive dei file di gitignore. Quindi ci sono due modi per uscire dall’impasse:

  • cancellare fisicamente i file, ma è l’opzione più stupida
    git rm storage/logs/*
  • cancellare la cache:
    git rm --cached storage/logs/*

Infatti l’opzione --cached indica a Git di togliere i file dall’indice ma non dal file system.

Ho trovato queste indicazioni in un paio di risposte di fourm 1 e 2.

Oracle Connection Pooling, ovvero come gestire gli accessi concorrenti a database con una coda.

Negoziare una connessione con un database è una operazione non banale che richiede una consistente attività per la realizzazione del collegamento sicuro, autenticato e la messa a disposizione delle risorse del database all’applicazione che ne fa uso. Ho esperienza di questo fatto soprattutto in ambito Java e Oracle ma le considerazioni da fare sono tutto sommato agnostiche e quindi indipendenti dalle piattaforme utilizzate. Tuttavia gli esempi sono in Java.

In ambito concorrente, come una applicazione web, questa operazione rischia presto di diventare un collo di bottiglia se si rimane nello schema 1 richiesta = 1 nuova connessione a DB.

L’idea è quella di utilizzare una coda (in questo ambito definita come pool) nella quale mantenere un numero finito di connessioni riutilizzabili che vengono in continuazione prelevate dalla coda quando servono e rimesse nella coda al termine del loro utilizzo.

Supponiamo che arrivino due richieste quasi simultanee

  1. Richiesta 1 (es. https://mysite.com/authenticate) la quale richiede collegamento al db per effettuare una autenticazione utente: in questa richiesta viene aperta una nuova connessione al database e viene lanciata la query sulla tabella utenti. Dopo aver acqusito il recordset, l’applicazione può dismettere la connessione invocando il metodo close()
  2. Richiesta 2 (es. https://mysite.com/articles/get/AB123) che deve eseguire una lettura di una tabella. L’applicazione apre una connessione ex novo (con il conseguente impegno di risorse e di tempo), lancia l’interrogazione e recupera il recordset.

Non sarebbe più efficiente per la Richiesta 2, qualora arrivasse dopo la conclusione della prima richiesta, riutilizzare la connessione aperta dalla Richiesta 1? Se la richiesta evitasse di chiamare la close() e invece “mettesse via” la connessione da qualche parte, la richiesta 2 potrebbe riutilizzarla senza dover negoziare un’altra volta username/password, caricamento driver con il DBMS.

Una struttura dati efficiente per mettere via dati che si ritiene di usare di nuovo a breve, può essere una coda, in particolare una coda FIFO. Che nel gergo dei database si chiama Connection Pool. Questa coda conterrà descrittori di risorse DBMS.

Il funzionamento di questa coda è il seguente. Partiamo da una coda vuota.

Arriva il primo processo (una richesta HTTP) che impone all’applicazione di collegarsi al database. Siccome la coda è vuota, viene giocoforza negoziata una connessione nuova con il database, chiamiamola Connessione 1. Quando l’interrogazione è stata fatta e quindi la login è eseguita, la connessione non viene chiusa, bensì viene salvato il descrittore con un push() nella coda, che finora era ancora vuota.

Arriva il secondo processo che necessita di una connessione al DBMS e quello che fa prima di tutto è controllare se nella coda ci sono connessioni disponibili. Supponiamo che la login sia già stata fatta e quindi nella coda ci sia la connessione 1 rilasciata dal primo processo. Tutto ciò che deve fare il processo 2 è lanciare la query senza autenticarsi di nuovo; quindi serviamo due processi con una sola connessione.

Se, viceversa, il processo 2 arriva quando la login è ancora in corso, quindi la connessione 1 è ancora impegnata, in questo caso il processo 2 dovrà aprire una connessione 2. Ma questo meccanismo fa sì che, dopo un po’ di tempo, la probabilità di trovare una connessione libera nella coda sia molto maggiore e quindi il numero di negoziazioni dirette è destinato a calare drasticamente. Ancora meglio, se all’inizializzazione del pool mettiamo già via un certo numero di connessioni nella coda, risparmiamo tempo ad applicazione avviata.

Ogni volta che un processo preleva una connessione dalla coda, fa un pull(), ovvero toglie la connessione dalla coda; quando ha terminato, opera un push() e la rimette nella coda. Se il numero di processi concorrenti è maggiore della dimensione della coda fino a quel momento, viene negoziata una nuova connessione, altrimenti si fa più velocemente un pull().

Questo meccanismo deve essere gestito; ossia si deve sollevare l’applicazione dal negoziare nuove connessioni, perché dovrà essere tutto supervisionato dal processo di gestione della coda, che è un oggetto della classe ConnectionPool. L’applicazione dovrà solo limitarsi a chiedere una connessione al pool, e il pool in ogni caso gliela concederà (sia essa una connessone riciclata o una nuova di zecca), senza che l’applicazione debba interloquire direttamente con il database.

Per rendere poi più efficiente l’uso della memoria, si può fare a meno di allocare una coda dinamica, ma un array di lunghezza fissa, in cui vengono impilate tante connessioni quante ne servono: all’inizio supponiamo che la coda sia vuota, viene fatto il push() di una nuova connessione, marcata con Active e consegnata al processo, che la potrà usare. Quando il processo che l’ha utilizzata la rilascia, il gestore del pool la dichiarerà Idle rimettendola a disposizione. Però non è che la inserisce nuovamente nel pool, semplicemente le cambia lo stato.

Tuttavia nell’implementazione di esempio che vedremo, viene utilizzata un’altra tecnica ancora: vengono impiegati due vettori; quello delle connessioni disponibili (o idle) e quello delle connessioni attive (active).

Il processo successivo che arriva a chiedere connessioni, si farà dare l’ultima connessione idle resasi disponibile (comportamento FIFO).

Esempio applicativo

Questo esempio è liberamente tratto (e semplificato) da Java Made So Easy.

Iniziamo dunque definendo la classe ConnectionPool:

package connectionPooling;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;
 
public class ConnectionPool implements Runnable {
    // i parametri di connessione
    private String driver, url, username, password;
    // la dimensione massima del pool
    private int maxConnections; 
    // le 2 code delle connessioni idle e active
    public Vector<Connection> idleConnections, activeConnections; 

    // il costruttore
    public ConnectionPool(
                  String driver, String url, String username,
                  String password, int maxConnections, int initialConnections
           ) throws SQLException {
           this.driver = driver;
           this.url = url;
           this.username = username;
           this.password = password;
           this.initialConnections = initialConnections;
           this.maxConnections = maxConnections;

           // in questo esempio il pool è mantenuto su due vettori:
           // - quello delle connessioni disponibili (o idle: pigre, nullafacenti);
           // - quello delle connessioni impegnate (o active);
           idleConnections = new Vector<Connection>(initialConnections);
           activeConnections = new Vector<Connection>();
           // qui opzionalmente preparo un certo numero di connessioni già pronte
           for (int i = 0; i < initialConnections; i++) {
                  idleConnections.addElement(makeNewConnection());
           }
    }

Il metodo fondamentale qui è makeNewConnection() con il quale costruiamo una nuova connessione “vera”; ovviamente lo dichiariamo private per impedire al programma chiamante di negoziare connessioni direttamente con il DBMS attraverso di essa:

private Connection makeNewConnection() throws SQLException {
           try {
                  // Carica il database driver
                  Class.forName(this.driver);
                  // Apri la connessione al db
                  Connection connection = DriverManager.getConnection(this.url, this.username,
                               this.password); 
                  return (connection);
           } catch (Exception cnfe) {
                  cnfe.printStackTrace();
                  throw new SQLException(
                               "ConnectionPool:: SQLException encountered:: "
                                             + cnfe.getMessage());
           }
    }

Come visto sopra questo metodo viene chiamato al deploy del connectioonPool per inizializzare un certo numero di connessioni già pronte per l’uso (che passo al costruttore).

Ora dobbiamo gestire il pool, ovvero consegnare al processo che ne fa richiesta una connessione idle, toglierla dal vettore delle idle e metterla in quello delle active:

/**
     * Method to return Connections
     */
    public synchronized Connection getConnection() throws SQLException {
           if (!idleConnections.isEmpty()) {
                  Connection existingConnection = (Connection) idleConnections
                               .lastElement();
                  int lastIndex = idleConnections.size() - 1;
                  // toglie la connessione dal vettore delle pigre:
                  idleConnections.removeElementAt(lastIndex);
                  // ... e la aggiunge al vettore delle attive:
                  activeConnections.addElement(existingConnection);
                  return (existingConnection);
           } else {
                  // se non ci sono più connessioni pigre, ne creo una di nuova: 
                  return makeNewConnection();
           }
    }

Poniamo attenzione sul fatto che il metodo getConnection() è definito come synchronized, per cui molte richieste pressoché simultanee verranno arbitrate in modo che le connessioni siano consegnate distintamente secondo il loro ordine di arrivo.

Può accadere, alla fine, che i processi esauriscano tutte le connessioni del pool: in questo caso il vettore delle connessioni active sarà pieno e quello delle idle sarà vuoto; si potrebbe gestire anche questa situazione ma, in questa fase in cui cerchiamo di capire come funziona il meccanismo, semplicemente notifichiamo che il pool è pieno.

                  if ((totalConnections() >= maxConnections) {
                        throw new SQLException("Connection limit reached");
                  }

dove totalConnection() è un count degli elementi del vettore activeConnections più il count degli elementi del vettore idleConnections.

Alla fine il programma chiamante utilizzerà il metodo getConnection() per comunicare con il pool, mentre la classe ConnectionPool utilizzerà il metodo (privato) makeConnection() per comunicare con il DBMS.

Così abbiamo operato la separazione tra applicazione e DBMS.

Ci manca solo un metodo per liberare una connessione (cioè toglierla dal vettore delle connesioni active e rimetterla in quello delle connessione idle), lo chiamiamo free():

  /**
     * Method to free the Connections
     */
    public synchronized void free(Connection connection) {
           activeConnections.removeElement(connection);
           idleConnections.addElement(connection);
    }

Alla fine siamo pronti ad utilizzare questa infrastruttura nell’applicazione; dobbiamo creare un oggetto istanza della classe ConnectionPool che mettterà a disposizione connessioni già pronte prelevabili con il metodo getConnection():

package connectionPooling;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
/** Copyright (c), Ankit Mittal JavaMadeSoEasy.com */

public class PreparedStatementUseConnectionPooling {

    public static void main(String... arg) throws SQLException {
            ConnectionPool connectionPool = new ConnectionPool(
                        "oracle.jdbc.driver.OracleDriver",
                        "jdbc:oracle:thin:@localhost:1521:orcl", 
                        "foo", 
                        "bar",
                        10, 5);
 
           Connection dbConn = connectionPool.getConnection();
           System.out.println("Sono pronte le connessioni dalla classe ConnectionPool");
           
           PreparedStatement prepStmt = dbConn
                        .prepareStatement("select ID, NAME from EMPLOYEE");
           
           ResultSet rs = prepStmt.executeQuery();
           while (rs.next()) {
                  System.out.print(rs.getInt("ID") + " ");
                  System.out.println(rs.getString("NAME"));
           }
 
           if (rs != null)
                  rs.close(); // close resultSet
           if (prepStmt != null)
                  prepStmt.close(); // close PreparedStatement
 
           connectionPool.free(dbConn);
           System.out.println("Rilasciata la connessione dalla classe ConnectionPool");
    }
}
 

Si noti che il programma chiamante, dopo la connessione impartisce comandi direttamente al DBMS; il ConnectioPool si intromette tra di loro solo al momento dell’acquisizione di nuove connessioni (o della rimozione di un connessione che non serve più).

Per ulteriori dettagli implementativi si faccia riferimento all’articolo citato all’inizio.

Adesso il funzionamento di un pool dovrebbe essere almeno un pelino più chiaro. Almeno per me lo è stato.

Dati COVID-19 nuovamente online

Ho sistemato un errore che si era manifestato nel cruscotto dei dati relativi alla pandemia tuttora in corso.

L’errore si è verificato in seguito ad una modifica del tracciato dati messo online dalla Protezione Civile e aveva temporaneamente reso non operativo il cruscotto.

Potete nuovamente consultarli (dati nazionali e regionali) a questo link:

Python in pillole – parte 3: disegnare frecce

Python programming skill terza parte: disegnamo in modalità testo come ultimo pattern quattro frecce:

right arrow
*                    
* *                  
* * *                
* * * *              
* * * * *            
* * * *              
* * *                
* *                  
*                    
                     
left arrow
                     
                  *  
                * *  
              * * *  
            * * * *  
          * * * * *  
            * * * *  
              * * *  
                * *  
                  *  
upward arrow
                     
                     
                     
                     
                     
        *            
      * * *          
    * * * * *        
  * * * * * * *      
* * * * * * * * *    
downward arrow
* * * * * * * * *    
  * * * * * * *    
    * * * * *    
      * * *    
        *    
           

Esercizio 7 – disegnare una freccia verso destra (right arrow)

"""
print right arrow
*
* *
* * *
* *  
* 

e.g.
  0 1 2 3 4 5 6 7 8 9         i  test    N-1-i
0 *                           0, j<=0    9
1 * *                         1, j<=1    8
2 * * *                       2, j<=2    7
3 * * * *                     3, j<=3    6
4 * * * * *                   4, j <5    5
5 * * * *                     5, j <4    4
6 * * *                       6, j <3    3
7 * *                         7, j <2    2
8 *                           8, j <1    1
9                             9, j <0    0
"""
print("right arrow")
for i in range(N):
    for j in range(N):
        if j <= i and j < N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Ho stampato un tracing e ho riportato tutti i valori delle variabili per le varie configurazioni disegnate. Come si vede, si tratta di combinare le condizioni che compaiono negli esercizi 3 e 6 (BLT e TLT), solo che sostituisco la condizione j <= N - 1 - i con j < N - 1 - i in modo da non disegnare la diagonale BLTR che risulterebbe col disegnare una freccia “spuntata”.

Esercizio 8 – disegnare una freccia verso sinistra (left arrow)

"""
print left arrow
    *
  * *
* * *
  * *  
    * 
    
e.g.
  0 1 2 3 4 5 6 7 8 9      i  test    N-1-i
0                   *      0, j>=9    9*
1                 * *      1, j>=8    8*
2               * * *      2, j>=7    7*
3             * * * *      3, j>=6    6*
4           * * * * *      4, j>=5    5*
5             * * * *     *5, j> 5    4
6               * * *     *6, j> 6    3
7                 * *     *7, j> 7    2
8                   *     *8, j> 8    1
9                         *9, j> 9    0 
"""
print("left arrow")
for i in range(N):
    for j in range(N):
        if j >= i and j > N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Per girare la freccia a sinistra è necessario ribaltare entrambe le condizioni di test:

  • da j<=i a j>=i
  • da j <= N - 1 - i a j > N - 1 - i

L’assenza dell’= sulla seconda condizione, al solito, evita il disegno della doppia punta.

Possiamo indovinare che anche per le frecce up e down si dovrà fare lo stesso…

Esercizio 9 – disegnare una freccia verso l’alto (upward arrow)

"""
print upward arrow
    *  
  * * *
* * * * * 

e.g.
  0 1 2 3 4 5 6 7 8 9
0
1
2
3
4                       N-1-i  test  i    
5         *                 4 <= j < 5
6       * * *               3 <= j < 6
7     * * * * *             2 <= j < 7
8   * * * * * * *           1 <= j < 8
9 * * * * * * * * *         0 <= j < 9

"""
print("upward arrow")
for i in range(N):
    for j in range(N):
        if N - i - 1 <= j < i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Qui è ormai evidente: è l’intersezione dei due esercizi BLT e BRT (esercizi 3 e 5). L’ultimo esercizio è la freccia verso il basso e, ormai, tutti gli arcani dovrebbero essere svelati e possiamo dire “com’era facile!”

Esercizio 10 – disegnare una freccia verso il basso (downward arrow)

"""
print downward arrow
* * * * * 
  * * *
    *  

e.g.
  0 1 2 3 4 5 6 7 8 9     i   test  N-1-i
0 * * * * * * * * *       0   <=j<  9
1   * * * * * * *         1   <=j<  8
2     * * * * *           2   <=j<  7
3       * * *             3   <=j<  6
4         *               4   <=j<  5
5
6
7
8
9 
"""
print("downward arrow")
for i in range(N):
    for j in range(N - i):
        if i <= j < N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Spero abbiate trovato divertenti questi piccoli esercizi (sì… anche se alla fine sono un po’ silly).

Alle prossime pillole!

Python in pillole – parte 2: disegnare triangoli

Python programming skill secomda parte: un po’ più laborioso è disegnare questi quattro triangoli. Ma neanche tanto, vedrete.

Bottom Left Triangle

BLT
*  
* *  
* * *  
* * * *  
* * * * *  
* * * * * *  
* * * * * * *  
* * * * * * * *  
* * * * * * * * *  
* * * * * * * * * *  

Top Right

TRT
* * * * * * * * * *  
  * * * * * * * * *  
    * * * * * * * *  
      * * * * * * *  
        * * * * * *  
          * * * * *  
            * * * *  
              * * *  
                * *  
                  * 

Bottom Right

BRT
                  *  
                * *  
              * * *  
            * * * *  
          * * * * *  
        * * * * * *  
      * * * * * * *  
    * * * * * * * *  
  * * * * * * * * *  
* * * * * * * * * *  

e infine Top Left

TLT
* * * * * * * * * *  
* * * * * * * * *    
* * * * * * * *      
* * * * * * *        
* * * * * *          
* * * * *            
* * * *              
* * *                
* *                  
*         

Li ho messi in quest’ordine perché sono due metà del quadrato diviso dalla stessa diagonale (TLBR nel primo, TRBL nel secondo).

Esercizio 3 – disegnare un triangolo BLT

print("BLT")
for i in range(N):
    for j in range(N):
        if i >= j:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Qui è evidente che l’esercizio 3 si ricava dall’esercizio 1 semplicemente sostituendo la condizione i=j con i>=j ma anche estendendo la scansione di j a tutto l’intervallo 0..N-1.

Esercizio 4 – disegnare un triangolo TRT

print("TRT")
for i in range(N):
    for j in range(N):
        if i <= j:
            print('*'),
        else:
            print(" "),
    print(" ")  # a capo

Anche qui è quasi ovvio, la condizione i <= j sostituisce la precedente i >= j (con la condizione = specifichiamo che va disegnata anche la diagonale).

Esercizio 5 – disegnare un triangolo BRT

print("BRT")
for i in range(N):
    for j in range(N):
        if j >= N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Anche qui basta sostituire la condizione j = N - 1 - i con la relazione >=, a partire dall’esercizio 2.

Esercizio 6 – disegnare un triangolo TLT

print("TLT")
for i in range(N):
    for j in range(N):
        if j <= N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")

Ormai avete capito la regola, non serve spaccarsi la testa.

I pattern generati da questi programmi sono riportati in alto, all’inizio dell’articolo, nello stesso ordine.

Ok, abbiamo finito. Con la prossima e ultima parte disegneremo frecce.

Python in pillole: esercizi su cicli annidati.

Python programming languagre

Oggi comincerò a pubblicare alcuni esercizi di Python sui cicli. Si assomigliano tutti, ognuno presenta una difficoltà leggeremente diversa, e sono basati sulla geometria. Fondamentalmente l’esercizio è indipendente dal linguaggio usato, è più un esercizio di algoritmi, ma per concisione e immediatezza d’uso ho preferito il Python,

Lo scopo, banale se volete, è quello di disegnare linee e triangoli in modalità testo, ma è un’esercizio che, a fronte di un risultato ludico – il vedere comparire un disegno -, permette di impratichirsi con la corretta valorizzazione delle variabili di ciclo.

È una pratica che fa anche un po’ anni ’80 (per chi ci è passato, e per chi non ci è passato potrà avere un’idea di quanto poco ci bastava per divertirci :-)).

Parte I – disegnare diagonali

I primi esercizi sono abbastanza semplici: tutti quanti hanno in comune la stessa struttura: due cicli for annidati e un test per stabiire se stampare un asterisco * o uno spazio. Per ognuno di questi ho riscritto più volte il codice in modo da approdare ad una forma il più simmetrica e concisa possibile (senza fare uso della funzione abs() che qui tornerebbe utile).

Esercizio 1 – disegnare una diagonale TLBR (Top Left Bottom Right)

Per prima cosa aggiungiamo ad ogni file l’intestazione

#!/usr/bin/python

N = 10

che dichiara l’interprete da usare per poter invocare il programma come un script da linea di comando:

$ ./diagonals.py

e inizializza a 10 l’ampiezza del pattern da visualizzare.

"""
print TLBR diagonal
*  
  *  
    *  
      *  
        * 
"""
print("TLBR")
# remember: range(N) goes form 0 to N-1
for i in range(N):
    for j in range(i + 1):
        if j == i:
            print('*'),
        else:
            print(" "),
    print(" ")  # a capo

Questo è abbastanza semplice: occorre un ciclo esterno per disegnare le righe (uso la variabile i) e uno per scandire le colonne (uso la variabile j)

La condizione che discrimina se stampare uno spazio o un asterisco è che ascisssa i e ordinata j siano uguali.

Finito. Una piccola ottimizzazione: la scansione delle colonne non viene fatta per tutta la riga ma solo fino al raggiungimento della condizione di plot (chiamiamola così).

L’ouput è il seguente

TLBR
*  
  *  
    *  
      *  
        *  
          *  
            *  
              *  
                *  
                  *  

Esercizio 2: – disegnare una diagonale TRBL (Top Right Bottom Left)

Si tratta semplicemente di disegnare l’atra diagonale del rettangolo:

"""
print TRBL diagonal
        *  
      *  
    *  
  *  
* 
"""
print("TRBL")
for i in range(N):
    for j in range(N - i):
        if j == N - 1 - i:
            print("*"),
        else:
            print(" "),
    print(" ")  # a capo

Il risultato è questo

TRBL
                  *  
                *  
              *  
            *  
          *  
        *  
      *  
    *  
  *  
*  

La condizione di plot è che l’indice di colonna sia uguale a N-1 (che è il massimo valore a cui arriva l’indice di riga) meno l’indice di colonna; è il numero di colonne che mancano da quella più a destra. È il duale del precedente in cui l’indice di riga era uguale all’indice di colonna (che è ovviamente il numero di colonne da quella più a sinistra). Quindi scritta così è evidente la simmetria che ci permette per sostituzione di passare da un programma all’altro. Ultima cosa, la scansione della riga non si estende a tutte le colonne ma solo fino alla condizione di plot, come nell’esercizio 1.

Dal punto di vista algebrico, nell’esecizio 1 si implementa il plot della funzione y=x (i = j nel nsotro programma), con il vincolo che l’asse delle y crescenti sia diretto verso il basso.

Nell’esercizio 2 si disegna la funzione y=N-x (i = N-j nel nostro codice), con la medesima convenzione.

Vi lascio anche i riferimenti per le puntate successive:

Fine! 🙂

Acronimi AdSense

CTR pagina

Click-Through Rate = numero di click sui banner di una pagina / numero di visualizzazioni di quella pagina. Espresso in percentuale

Es: +4,02% significa che se la pagina è stata visualizzata 100 volte ha collezionato 4 click sui banner.

CPC

Cost Per Click = quanto si viene mediamente remunerati per un click su un Ad. Espresso in Euro.

Es: 0,13 € : ci sono iserzionisti che pagano pochi centesimi al click e chi anche 20 centesimi

RPM

Revenue Per Mile = Ricavo ogni 1000 impressioni per pagina. Espresso in Euro

Es: 5,20€ per 1000 impressioni/pagina

Impressione

1 Impressione = il download di un banner sul dispositivo del navigante. Dipende da quanto il navigante si sofferma nella pagine. Se c’è un’alta frequenza di rimbalzo vuol dire che il navigante si ferma poco in un singola pagina e quindi non tutte gli Ad vengono scaricati e ho poche impressioni per pagina.

Pillole Java: Gestione della memoria

Architettura Hotspot JVM

La Java Virtual Machine è un software che fornisce un ambiente operativo virtuale per applicazioni Java.

L’architettua Hotspot è composta di tre elementi

  • il motore di esecuzione:
    • è composto di un compilatore a tempo immediato (just in time o JIT compiler) che traduce in bytecode il programma di testo (compilazione) al momento in cui deve essere utilizzato
    • e di un garbage collector (che è ciò su cui ci focalizziamo), un collettore di spazzatura che fa la raccolta differenziata degli oggetti usati e li rimuove con una specifica politica
  • Runtime data area, o area dati di esecuzione che è una memoria organizzata in cui vengono caricati i metodi, i threads, lo heap (che è la zona che ci interessa) e altre zone. In particolare lo heap (mucchio) è la zona riservata ai dati trattati dall’applicazione
  • il class loader che è un regista che carica in memoria le classi e i metodi quando richiesti
Gestione della memoria in Java: Architettura HotSpot JVM
Gestione della memoria in Java: Architettura HotSpot JVM

Lo heap

Lo heap è una zona di memoria RAM nella quale Hotspot gestisce i dati dell’applicazione. Viene implementata una vera e propria raccolta differenzaiata in base all’età di occupazione. L’età di occupazione è calcolata con un contatore per ogni oggetto che, ogni volta che viene inizializzato, incremento il contatore. Ogni volta che imposto la variabile a null, lo decremento di 1. Quando il contatore raggiuunge 0 è tempo di buttare via l’oggetto. Quindi programmaticamente è buona norma impostare a null una variabile se non la si vuole più usare, perché così riesco a raggiungere l’evento minor garbage collection che determina lo spostamento o l’eliminazione fisica dell’area di memoria che contiene la variabile.

I dati vengono diversificati in tre zone “temporali”:

  • Young Generation
  • Old Generation
  • Permanent Generation
Struttura dello Heap (Wikipedia)

La zona Young Generation è dove vengono allocati tutti gli oggetti che vengono utilizzati per la prima volta; è a sua volta suddivisa in Eden (oggetti appena creati) e Survivor. Il raccoglitore differenziato (minor garbage collector) sposta gli oggetti dall’Eden alla zone di Sopravvivenza e da qui alla Old Generation. Uno spostamento da Young a Old è considerato un evento di minor garbage collcection.

Gli oggetti un po’ più datati vivono nella Old generation; vengono spostati da qui dal racccoglitore differenziato maggiore ed eliminati, Questo evento è condierato una major garbage collection.

Nella zona Permament Generation vengono collezionati metodi e classi, che sono considerati metadati. Anche l’eliminazione da parte del GC degli oggetti di questa zona è considerata major.

Quindi gli oggetti vengono spostati gradualmente da una zona all’altra fino alla loro eliminazione completa.

Tipi di GC

I garbage collectors di Hotspot sono 4:

  • serial
  • parallel
  • CMS
  • G1

Questi si comportano sostanzialmente in modo diverso, ma dobbiamo pensare che ci sono processi per così dire “attivi” che portano avanti l’esecuzione del programma principale e processi per così dire “passivi” che invece si occupano di fare pulizia. A volte i secondi devono interrompere i primi e questa modalità è detta “Stop-the-world mode GC”.

Serial è un unico thread che si occupa di eliminare molti garbage uno alla volta.

Parallel si occupa di eventi minori (come lo spostamento da un contentore all’altro) e ogni garbage è spostato da un thread separato, quindi si gestiscono più segmenti alla volta.

CMS (Concurrent Mark-sweep) vengono marcati e cancellati più segmenti tramite thread paralleli per minimizzare l’arresto del programma principale (per minimizzare gli Stop-the-world).

G1 (Garbage First) è un’alternatva concorrente di CMS.

Compattazione

Il modo di gestire la cancellazione degli oggetti non più usati è allo stesso modo critica: si possono adottare diverse tecniche, la più ingenua è cancellare loggetto sic et simpliciter (tecnica Mark-Sweep), ma questo reimpirebbe la memoria di “buchi” e sarebbe diffficile riutilizzae lo spazio reso disponibile.

Mark-sweep

Quindi ci sono dei metodi più evoluti, come il Copy o il Mark-Compact che riallocano i dati da cancellare in modo da renderli contigui e creare delle zone libere più grandi possibili e continue (senza buchi).

Mark-Compact

Bibliografia