sabato 19 novembre 2011

Metteva l'amore. Bug, crash, Italietta

La chiamavano bocca di rosa, metteva l'amore, metteva l'amore.
C'era una volta un piccolo, insignificante concorso pubblico. E c'era una volta un anonimo¬ dipendente pubblico lungimirante, che, temendo che il concorso andasse deserto, ha deciso di pubblicizzarlo. Dall'altra parte c'era un ragazzo che, visto pubblicizzato il concorso, ha pensato di iscriversi, più per interesse personale che altro, visto che si tratta di un lavoretto minuscolo. Per la gloria, diciamo.
Lei lo faceva per passione.
Il ragazzo ha così fatto quello che pensava fosse ovvio fare: seguire le istruzioni alla lettera. Tuttavia, ha involontariamente fatto scattare un "bug" nel concorso, che di conseguenza è "crashato", facendo slittare l'assegnazione, con conseguente piccolo disastro burocratico (e anche organizzativo).
Ma la passione spesso conduce / a soddisfare le proprie voglie / senza indagare se il concupito / ha il cuore libero oppure ha moglie.
Come se non bastasse, ha fatto anche saltare alcuni equilibri diciamo "prestabiliti", dato che solitamente solo gli interessati sanno del concorso (pur essendo visibile al pubblico come tutti i concorsi pubblici; ma nessuno va mai a cercarli).
E fu così che da un giorno all'altro / bocca di rosa si tirò addosso / l'ira funesta delle cagnette / a cui aveva sottratto l'osso.
Morale della favola: il lungimirante dipendente pubblico ha "sfidato il sistema" perché è in una posizione in cui non rischia praticamente niente se non occhiatacce. Il ragazzo invece ha peccato di ingenuità, non immaginando che cosa ci fosse dietro un apparentemente innocuo e insignificante concorso pubblico.
Se non altro, fortunatamente per lui, non ci saranno gendarmi ad accompagnarlo alla stazione.

venerdì 18 novembre 2011

Galileianamente

«Non si dovrebbe parlare male dell'ente pubblico in cui si lavora. Ma si vede che lo faccio perché sono di sinistra. O almeno, prima dicevo di essere di centro, ora pare che io sia di sinistra. Non estrema ma sinistra.»

«Certe volte non siamo noi, ma è il sistema di riferimento a spostarsi.»

mercoledì 16 novembre 2011

"Machiavelli" come CSP (seconda parte)

Nella prima parte ho descritto il problema e l'ho formalizzato in termini di programmazione lineare. Ora manca solo di verificare che funzioni.

Non mi dilungherò in questa parte anche perché è quasi tutto spiegato nel codice e sarebbe inutile spiegare passo-passo come sono arrivato a scriverlo.

Per prima cosa, ho scelto il software. Per il problema di soddisfacimento ho usato GLPK, un risolutore lineare che supporta una sintassi molto flessibile e soprattutto è free. Con questo risultato. L'output non è molto leggibile ma la risoluzione è corretta.

Successivamente ho tradotto tutto in uno script Python che simula una partita di Machiavelli, il computer contro sé stesso, con risultati come questo, di cui pubblico un estratto.
Nota: non avendo molta dimestichezza col Python, il codice potrebbe non essere molto "elegante", diciamo.
(...)

Contenuto del tabellone
A♥ A♠ A♣
Q♥ Q♦ Q♠
6♠ 7♠ 8♠ 9♠ 10♠ 
A♣ 2♣    J♣ Q♣ K♣ 

Turno del giocatore 1
Carte del giocatore 1:
    4♦ 4♦ 4♠ 7♥ 8♣ 8♦ 6♦ 10♥ K♦ 8♣ 3♥ 3♣ 2♥
Il giocatore 1 cala le carte: 3♣
Contenuto del tabellone
A♥ A♠ A♣
Q♥ Q♦ Q♠
6♠ 7♠ 8♠ 9♠ 10♠ 
A♣ 2♣ 3♣    J♣ Q♣ K♣ 

Turno del giocatore 2
Carte del giocatore 2:
    4♥ 5♣ 5♦ 9♦ K♥ 7♥ Q♦ 4♠ 6♣ 2♠ 2♦ J♦ 10♥ K♠ 10♠ J♥
Il giocatore 2 cala le carte: J♦ J♥
Contenuto del tabellone
J♥ J♦ J♣
Q♥ Q♦ Q♠ Q♣
A♥ A♠ A♣
6♠ 7♠ 8♠ 9♠ 10♠ 
A♣ 2♣ 3♣    K♣ 

Turno del giocatore 0
Carte del giocatore 0:
    3♦ 9♥ 8♥ J♥ 6♠ 8♥ 2♠ 5♥ 8♦ 10♦ 7♦ K♦ 5♥
Il giocatore 0 pesca una carta dal mazzo: 4♣

Turno del giocatore 1
Carte del giocatore 1:
    4♦ 4♦ 4♠ 7♥ 8♣ 8♦ 6♦ 10♥ K♦ 8♣ 3♥ 2♥
Il giocatore 1 pesca una carta dal mazzo: 2♥

Turno del giocatore 2
Carte del giocatore 2:
   4♥ 5♣ 5♦ 9♦ K♥ 7♥ Q♦ 4♠ 6♣ 2♠ 2♦ 10♥ K♠ 10♠
Il giocatore 2 cala le carte: K♥ K♠
Contenuto del tabellone
A♥ A♠ A♣
Q♥ Q♦ Q♠ Q♣
J♥ J♦ J♣
K♥ K♠ K♣
6♠ 7♠ 8♠ 9♠ 10♠ 
A♣ 2♣ 3♣

(...)

lunedì 14 novembre 2011

"Machiavelli" come CSP (prima parte)

Quella che sto per scrivere è un'idea che mi è venuta sul water, a Pasqua, tra una partita di Machiavelli e l'altra, e che ho finalmente sviluppato in questi giorni. Cercavo un modo per scrivere un programma che rimettesse a posto il tavolo da solo (ma soprattutto cercavo un meccanismo per capire in anticipo se una certa mossa era possibile o no). All'epoca non conoscevo ancora gli strumenti per risolverlo, finché non mi sono accorto che è un bell'esempio di problema di soddisfacimento dei vincoli (CSP).

Seguendo il percorso dall'inizio alla fine, formulerò il problema prima a parole, poi usando il formalismo matematico e infine lo tradurrò in un programma.

Il modello matematico che ho scelto è quello della programmazione lineare intera. È sempre bene riportarsi a un modello ben notoanche se il problema può essere più complicato da formulare. Per due motivi molto semplici. Da una parte non ho bisogno di scrivere un risolutore da me, perché per questo tipo di problema ne esistono parecchi in circolazione, con un risparmio di tempo notevole; dall'altra parte il "prodotto finale" normalmente è molto più efficiente rispetto a un sistema costruito direttamente "su misura", essendo ben note e studiate anche le ottimizzazioni che si possono applicare. Sembra una regola di buonsenso piuttosto ovvia – e lo è – ma è molto difficile abituarsi a usarla.

Attenzione: questo post contiene una grande quantità di formule matematiche. Se non le visualizzate correttamente, è colpa delle impostazioni del vostro browser (oppure aggiornate il vostro Internet Explorer 5, perdio!). Se non riuscite a capirle, saltatele. Forse il senso del post rimane chiaro.

Prima formulazione, a parole

Il problema, che in questo caso è un sottoproblema del gioco di carte Machiavelli, è il seguente: dato un insieme di carte mescolate sul tavolo, ricostruire un tabellone valido secondo le regole del Machiavelli, se possibile.

Le regole che ci interessano (non tutte le regole del gioco):
  1. Si usano due mazzi di carte francesi senza jolly; alcune di queste saranno sul tavolo.
  2. Ogni carta sul tavolo deve essere coinvolta o in una scala o in un tris/poker:
  3. un tris/poker è un gruppo di 3/4 carte con lo stesso numero ma di seme diverso;
  4. una scala è una sequenza di almeno tre carte contigue dello stesso seme.
  5. Non ci possono essere carte spaiate (discende dalle precedenti tre).
  6. Regola opzionale: sono accettate anche scale continue (cioè scale che contengono la sequenza KA2)

Alcune regole che possono servirci dopo, scritte già in modo "utile":
  1. Un giocatore può calare delle carte aggiungendole al tavolo se, aggiunte quelle carte al tavolo, riesce a disporre le carte secondo una configurazione valida (secondo le 6 regole precedenti).
  2. Dopo aver calato delle carte, il giocatore può terminare il turno.
  3. Se non aggiunge carte al tavolo, il giocatore deve pescare una carta e cedere il turno.

Formalizzazione

Proprio qui entra in gioco l'intuizione che ho avuto sul cesso. Il tabellone può essere codificato come una matrice binaria tridimensionale 4x13x4, formata cioè da quattro tabelle sovrapposte, fatte così:
A234567 8910JQK

Chiamerò ognuna delle 4 tabelle "piano". I piani pari sono per le scale, i dispari per i tris/poker. I primi due piani sono per il primo "mazzo", gli altri due per il secondo "mazzo". Ogni carta può comparire solo una volta in un "mazzo": o in un tris o in una scala (con "mazzo" non intendo il mazzo fisico, ma la coppia di tabelle).

Chiaramente, se si giocasse con un numero diverso di mazzi cambierebbe il numero di piani, che deve essere il doppio del numero dei mazzi, ma il modello sarebbe lo stesso.

In ogni casella della matrice ci sarà un 1 se la carta corrispondente è impegnata in quel piano, 0 se non lo è.

Userò anche una tabella ausiliaria, la "proiezione" dei piani, ottenuta "schiacciando verticalmente" tutti i piani. Ogni casella della proiezione conterrà la somma delle corrispondenti caselle nei 4 piani, ossia il numero di carte che compaiono sul tavolo, per ogni combinazione di numero e seme. La proiezione è l'unica informazione che abbiamo quando il tavolo è completamente scombinato e dobbiamo riordinarlo secondo le regole, pertanto sarà l'input del nostro problema.

Ora, le formule. Chiamato \(x_{i,j,k}\) il valore della matrice per la riga (seme) \(i\), la colonna (numero) \(j\) e il piano \(k\), riscrivo le regole in termini di valori della matrice.

La regola 2 diventa: ci può essere al massimo un 1 nelle caselle che appartengono alla stessa riga, stessa colonna, stesso "mazzo" (coppia di piani). In formule:$$x_{i,j,2m} + x_{i,j,2m+1} \leq 1 \quad (\forall i \in \{0..3\},\ \forall j \in \{0..12\},\ \forall m \in \{0,1\})$$dove \(m\) è il numero del mazzo, che comprende quindi i due piani \(2m\) e \(2m +1\).

La regola 3: nei piani dei tris/poker, la somma degli elementi per ogni colonna può essere solo 0, 3 o 4.
$$\sum_{i=0}^3 x_{i,j,k} = 0 \lor \sum_{i=0}^3 x_{i,j,k} \geq 3 \quad (\forall j \in \{0..12\},\ \forall k \in \{1,3\})$$Scritto così, il vincolo non può essere usato in un risolutore lineare perché contiene un operatore logico. Per rendere questo vincolo lineare, devo introdurre una famiglia di variabili ausiliarie, \(y_{j,k}\), ossia una variabile per ogni colonna dei piani dispari, che deve valere 1 se la somma della colonna corrispondente è diversa da 0 e può valere 0 altrimenti. Tale corrispondenza deve essere garantita da un vincolo (lineare ovviamente). Eccolo:$$y_{j,k} \geq \frac{1}{4}\sum_{i=0}^3 x_{i,j,k} \quad (\forall j \in \{0..12\},\ \forall k \in \{1,3\})$$A questo punto posso riscrivere la regola 3:$$\sum_{i=0}^3 x_{i,j,k}\geq 3y_{i,j} \quad (\forall j \in \{0..12\},\ \forall k \in \{1,3\})$$

La regola 4 è più complessa: nei piani delle scale, per ogni riga, devono valere le seguenti condizioni:
  • per ogni sequenza di tre caselle consecutive, non si può presentare la sequenza 010;
  • per ogni sequenza di quattro caselle consecutive, non si può presentare la sequenza 0110.
Condizioni queste che devono essere valutate considerando ogni riga della tabella estesa con degli 0 a sinistra e a destra, oppure (se si accetta la regola 6, quella delle scale continue) considerando che la riga si ripeta infinitamente.

Come prima, devo formalizzare questa regola come vincolo lineare.
Prima parte:$$(1-x_{i,j-1,k}) + x_{i,j,k} + (1-x_{i,j+1,k}) \neq 3$$ ossia $$-x_{i,j-1,k} + x_{i,j,k} -x_{i,j+1,k} \neq 1$$
Poiché tutte le \(x\) sono variabili binarie, la parte sinistra della diseguaglianza non può mai essere maggiore di 1, quindi riscrivo:$$-x_{i,j-1,k} + x_{i,j,k} -x_{i,j+1,k} \leq 0 \quad (\forall i \in \{0..3\},\ \forall j \in \{0..12\},\ \forall k \in \{0,2\})$$con la seguente sostituzione: \(x_{i,j,k}:=0\) se \(j\lt 0\) o \(j\gt 12\). Altrimenti, se si accetta la regola 6, la sostituzione è \(x_{i,j,k}:=x_{i,j\%12,k}\) dove con \(\%\) indico l'operatore di resto ("modulo").

Analogamente ottengo la formula per la seconda parte della regola:$$-x_{i,j-1,k} + x_{i,j,k} + x_{i,j+1,k} - x_{i,j+2,k} \leq 1$$ $$(\forall i \in \{0..3\},\ \forall j \in \{0..12\},\ \forall k \in \{0,2\})$$con le stesse sostituzioni.

Ora manca solo l'ultima formula, la famiglia di vincoli che lega la matrice alla proiezione (la nostra informazione di partenza): la somma "trasversale" su ogni combinazione di riga e colonna deve essere uguale alla casella analoga nella proiezione. Dette \(c_{i,j}\) le caselle della proiezione, il vincolo è$$\sum_{k=0}^3 x_{i,j,k} = c_{i,j}\quad (\forall i \in \{0..3\},\ \forall j \in \{0..12\})$$

Riepilogo

Costanti
\(c_{i,j} \in \{0..2\},\quad i \in \{0..3\},\ j \in \{0..12\}\)

Variabili
\(x_{i,j,k} \in \{0,1\}, \quad i \in \{0..3\},\ j \in \{0..12\},\ k \in \{0..3\}\)
\(y_{j,k} \in \{0,1\}, \quad j \in \{0..12\},\ k \in \{1,3\}\)

Vincoli
Regola 2:$$x_{i,j,2m} + x_{i,j,2m+1} \leq 1 \quad (\forall i \in \{0..3\},\ \forall j \in \{0..12\},\ \forall m \in \{0,1\})$$
Regola 3:$$y_{j,k} \geq \frac{1}{4}\sum_{i=0}^3 x_{i,j,k} \quad (\forall j \in \{0..12\},\ \forall k \in \{1,3\})$$ $$\sum_{i=0}^3 x_{i,j,k}\geq 3y_{i,j} \quad (\forall j \in \{0..12\},\ \forall k \in \{1,3\})$$
Regola 4: $$-x_{i,j-1,k} + x_{i,j,k} -x_{i,j+1,k} \leq 0$$ $$-x_{i,j-1,k} + x_{i,j,k} + x_{i,j+1,k} - x_{i,j+2,k} \leq 1$$ $$(\forall i \in \{0..3\},\ \forall j \in \{0..12\},\ \forall k \in \{0,2\})$$

Senza regola 6: \(x_{i,j,k}:=0\) se \(j\lt 0\) o \(j\gt 12\).
Con regola 6: \(x_{i,j,k}:=x_{i,j\%12,k}\).

Consistenza:$$\sum_{k=0}^3 x_{i,j,k} = c_{i,j}\quad (\forall i \in \{0..3\},\ \forall j \in \{0..12\})$$

Nella prossima parte mostrerò come questa formulazione si può tradurre in un programma.

domenica 13 novembre 2011

Implicazione

Uno dei concetti della logica matematica (e anche della logica classica) che vengono fraintesi più spesso è l'implicazione. Questa freccina, che di solito viene tradotta con "se... allora", crea sempre molti grattacapi ai nuovi arrivati nel mondo della logica.
Principalmente, gran parte del problema sta nel fatto che leggendo \(A \rightarrow B\) come Se A allora B, intendiamo molto di più di quello che la freccina dice, e scattano dei meccanismi mentali che ci portano fuori strada. Nel linguaggio comune, infatti, dicendo Se A allora B, posso indicare tre concetti diversi:
  1. Causalità. A causa B.
  2. Successione temporale. A precede B.
  3. Implicazione materiale (condizione necessaria). A non si può presentare senza B.[1]
Nella logica matematica, invece, l'unica interpretazione corretta è la 3, e i significati 1 e 2 si perdono. Facciamo qualche esempio.

Se A è "gli asini volano" e B "io sono Lady Gaga", la formula \(A \rightarrow B\) ("se gli asini volano allora io sono il re di Francia") è vera, anche se le due parti sono totalmente scollegate, e non è detto che se gli asini volassero allora io sarei Lady Gaga. Anzi, il fatto che gli asini volino non potrebbe causare in alcun modo il mio essere Lady Gaga. Non c'è nessuna relazione di causalità tra A e B, ma la formula \(A \rightarrow B\) continua ad essere vera.
Lo stesso vale anche quando A e B sono entrambe vere, come in "se Roma è in Italia allora 1+1=2".

Ci sono controesempi simili anche per la successione temporale. "Se nasce un bambino allora c'è stato un concepimento" è un'implicazione vera, anche se l'antecedente (A) è nel presente e il conseguente (B) è nel passato. D'altra parte, se c'è un concepimento non necessariamente nasce un bambino. Questo accade perché il concepimento è solo condizione necessaria ma non sufficiente per la nascita di un bambino.

Per evitare di fraintendere la formula, consiglio sempre di leggere \(A \rightarrow B\) come se fosse \(B \lor \lnot A\) ("B o non A") anche se è meno immediato da capire. Viceversa, tradurre un "se... allora" con un'implicazione è legittimissimo.

Tutta un'altra serie di problemi nasce dalla nostra innata e spiccata capacità di trarre conclusioni a cazzo (gli accademici le chiamano questi ragionamenti paralogismi e gli errori fallacie).

«Come dico sempre: nessun giudizio reale è bianco o nero.»
«Ma tesovo, come sei tranchant, non è mica detto che se un giudizio non è bianco o nero deve essere per forza reale!»
«Che cazzo stai dicendo?»
«Perché odi gli arabi?»
«Secondo te? Chi ha abbattuto le torri gemelle?»
«Ma che cazzo c'entra?»

Di queste, tuttavia, parlerò in un'altra occasione.


[1] Non entrerò nella diatriba filosofica e nella distinzione tra implicazione materiale e implicazione logica. È una distinzione che non serve quasi mai, e soprattutto nella logica "tradizionale" i due concetti sono strettamente legati, tanto da potersi considerare la stessa cosa.

lunedì 7 novembre 2011

Yes I love the international environment and balle varie

Poco più di un anno fa mi era stato proposto di entrare in un programma – di cui non farò il nome – nella mia università, dalla presentazione altisonante, che avrebbe comportato un certo impegno ma con benefici consistenti. L'immancabile trabocchetto era che bisognava passare una rigorosissima¬ selezione in cui metà dei candidati – studenti di diverse facoltà, tutti con medie abbastanza alte – sarebbe stata mandata a casa.

Dopo la "lettera motivazionale" (che per me è stata un parto plurigemellare) bisognava fare un colloquio individuale – in inglese – con il responsabile di questo progetto.
Nella sala d'aspetto c'erano una trentina di ragazzi, nessuno dei quali era veramente motivato a unirsi per amore del progetto o dei suoi corsi, e alcuni non erano nemmeno delle cime (ma questo magari lo dico solo per cattiveria). Quasi nessuno, inoltre, aveva la più pallida idea di quali fossero i corsi, che era una delle cose che venivano chieste al colloquio, nella forma di un ruffianissimo "Quale dei nostri corsi ti ha incuriosito di più?". Io, modestamente, una letta al programma me l'ero data, così ho potuto sparare un paio di titoli a caso.

Il resto del colloquio sembrava promettente. Mi sono sentito perfino quasi a mio agio, quando in risposta a una domanda ho detto che non avevo intenzione di andare all'estero all'ultimo anno – cosa che sapevo non essere vista di buon occhio, perché rischiava di interferire col programma.

Risultato: non sono stato preso. (Altrimenti non sarei qui a lamentarmene.) Eppure la lettera motivazionale era da A+, come ho visto sul foglio che c'era sulla scrivania dell'esaminatore, e la mia media era delle più alte. Non saprò mai il vero motivo della mia esclusione, il che aggiunge frustrazione alla frustrazione. Posso solo azzardare qualche ipotesi, dopo mesi e mesi che mi ci sono arrovellato.
Forse mi sono esposto troppo quando ho detto che i titoli dei corsi secondo me non erano abbastanza rappresentativi del contenuto (in realtà stavo pensando che i corsi facevano pisciare il culo, ma questa parte non l'ho data a vedere); forse sono sembrato superficiale quando ho lasciato intendere che la maggior parte del mio entusiasmo fosse per le lezioni in inglese e per l'"ambiente internazionale".

Forse – ed è più probabile – non ho leccato abbastanza diligentemente il culo. Correggetemi se sbaglio.

domenica 6 novembre 2011

Essere schietti paga (statisticamente)

Lo dico per esperienza: essere diretti, senza tanti giri di parole ma soprattutto parlare quando c'è un problema, anziché rispondere con ripicche o tacere finché il problema non diventa ingestibile.

Solo qualche volta essere diretti e senza filtro (non nel senso di dire tutto ciò che passa per la testa ma nel senso di non nascondere le proprie opinioni) non è la scelta migliore – l'ideale sarebbe riuscire a capire quando è bene esserlo e quando no – tuttavia in media essere schietti paga.


D'altra parte ci sono quelli che ripetono:

«Io le cose le dico in faccia» LALLERO!

Quante volte sento ripetere questa frase! Ma soprattutto, quante volte chi la pronuncia sa essere schietto solo a parole! Alcuni affermano di essere sempre diretti e poi quando c''è un problema si tirano indietro e anzi sono gli ultimi a prendere la parola.