All posts by Cristiano

Utilità del prodotto cartesiano

Definizione

Il prodotto cartesiano non è altro che l’insieme delle coppie ordinate di due insiemi. Ad esempio se ho due insiemi:
A = {a,b,c}
B = {d,e,f}
AxB = {(a,d),(b,d),(c,d),(c,e),(c,f)

Per tre, invece:
A = {a,b}
B = {c,d}
C = {e,f}
AxBxC = {(a,c,e),(a,d,e),(a,c,f),(a,d,f),(b,c,e),(b,d,e),(b,c,f),(b,d,f)}

Per i database il prodotto cartesiano dei dati è il modo naturale per unire due set differenti.

cartesianoLa caratteristica del prodotto cartesiano risiede nel modo di determinare la griglia: è sufficiente conoscerne gli elementi degli assi. L’immagine seguente, realizzata in Python, mostra proprio il prodotto cartesiano fra i punti di 3 assi, appunto, cartesiani.

Il prodotto cartesiano non voluto

Un esempio di prodotto cartesiano non desiderato è facilmente riscontrabile nell’esempio del negozio, riporto un mock semplificato di due tabelle:

Account:
+-----------------+--------+
+ NOME		  + ACCOID +
+-----------------+--------+
+ Cristiano 	  + 1      +
+ Mario		  + 2      +
++++++++++++++++++++++++++++
Ordini:
+-----------------+---------+
+ ORDINI	  + ACCOID  +
+-----------------+---------+
+ 100€		  + 1	    +
+ 50€		  + 1 	    +
+ 70€		  + 1	    +
+ 50€		  + 1 	    +
+ 30€		  + 2	    +
+ 10€		  + 2 	    +
+++++++++++++++++++++++++++++

Eseguendo una query SQL del tipo

SELECT * FROM Account, Ordini

si ottiene proprio il prodotto cartesiano fra le due, che non è probabilmente il risultato che si vuole ottenere! (Per intenderci otterremo 12 righe).

+-----------------+-------------+
+ ORDINI	  + NOME	+
+-----------------+-------------+
+ 100€		  + Cristiano   +
+ 50€		  + Cristiano   +
+ 70€		  + Cristiano   +
+ 50€		  + Cristiano   +
+ 30€		  + Cristiano   +
+ 10€		  + Cristiano   +
+ 100€		  + Mario	+
+ 50€		  + Mario	+
+ 70€		  + Mario	+
+ 50€		  + Mario	+
+ 30€		  + Mario	+
+ 10€		  + Mario	+
+++++++++++++++++++++++++++++++++

Il motivo per cui la query non produce il risultato che vogliamo, è che viene valutato di default il prodotto cartesiano fra le tabelle interrogate (operazione di CROSS JOIN): l’output è proprio l’insieme di tutte le possibili combinazioni di account e ordini, e non un insieme con il nome dell’account all’intero del db. Si dice che il JOIN cerca il percorso più lungo (o globale) per unire le due tabelle.

SELECT * FROM Account NATURAL JOIN Ordini

In questo modo si otterrà invece il risultato desiderato: i parametri con lo stesso nome e valore verranno utilizzati come fonte per l’unione. La colonna ACCOID verrà riprodotta una sola volta.

+-----------------+-----------------+
+ ORDINI	  + ACCOID	    +
+-----------------+-----------------+
+ 100€		  + Cristiano	    +
+ 50€		  + Cristiano	    +
+ 70€		  + Cristiano	    +
+ 50€		  + Cristiano 	    +
+ 30€		  + Mario	    +
+ 10€		  + Mario	    +
+++++++++++++++++++++++++++++++++++++

Lo scopo di questo articolo non è presentare le JOIN, motivo per cui non mi soffermerò ulteriormente su altri esempi di questo tipo o su come modificare la query nel caso in cui ci siano colonne da unire con nomi diversi.

Quando è utile

In realtà le applicazioni del prodotto cartesiano sono moltissime. L’esempio più chiaro è nella creazione di grafici e report, ovvero ogni qual volta la combinazione di due valori deve dare un unica coppia. Ad esempio:

SELECT Negozi.Negozio, Prodotti.Prodotto FROM Negozi CROSS JOIN Prodotti

In questo modo avremo per ogni negozio i prodotti disponibili in una matrice.

Per fare un altro esempio, la prossima volta che dovete creare tutte le combinazioni di data e ora, potreste fare il prodotto cartesiano fra ore e minuti invece che innestare un ciclo dentro un altro.

App per daltonici: individuare il nome di colore dal valore RGB

Tempo fa mi sono cimentato nello sviluppo di una semplicissima app per daltonici il cui unisco scopo era quello di individuare il nome del colore dominante in una foto. In pratica l’utente che avesse scaricato l’applicazione avrebbe dovuto scattare una foto e la mia app gli avrebbe detto di che colore era.

Il senso? Il mio amico daltonico Davide non può comprarsi vestiti da solo: deve sempre chiedere di che colore sia questo o quel pantalone/maglietta o qualsiasi altra cosa. Non può neppure cucinarsi un hamburger da solo perché non riesce a capire se la carne è cotta dal colore (in realtà potrebbe assaggiarla – ma lui dice che non è lo stesso, giustamente).

Classico esempio del problema della luce
Classico esempio del problema della luce

Dopo alcuni test ho realizzato che effettuare una fotografia, per quanto sia la scelta più intuitiva, non è la scelta migliore per raggiungere il mio obiettivo – infatti il colore della luce influenza il colore percepito dall’occhio della camera – cosicché un colore può apparire in un modo ma essere un altro. La scelta giusta sarebbe utilizzare uno spettrometro… giustamente non incluso in nessuno smartphone, così l’idea si è arenata dopo qualche test sul prototipo.

In ogni caso è stato interessante porsi questa domanda: come posso risalire al nome di un colore semplicemente dal suo valore RGB?

Attacco al problema

La cosa chiara fin da subito è che c’è bisogno di una lista di valori noti a cui confrontare il valore RGB ignoto. Una volta ottenuta la lista, ci si può chiedere (ed è la domanda chiave): come effettuo il confronto?

Sarebbe da pazzi e probabilmente inutile confrontare i singoli valori della terna con quelli disponibili, magari all’interno di uno switch lunghissimo, per ottenere qualcosa… Già solo provarci è da pazzi.

La soluzione più rapida e quella di immaginare la nostra tripletta RGB all’interno dello spazio tridimensionale di assi [(R,0,0),(0,G,0),(0,0,B)]. A questo punto ogni colore non è altro che un vettore! E come sapete passare ad un PC un oggetto matematico è davvero la cosa più facile e veloce.

Allora il procedimento sarà proprio questo. Bisognerà creare una lista di vettori del nostro spazio RGB (che poi è uno spazio tridimensionale normato) contenente i colori che ci vengono in mente (inclusi i grigi) in modo che la distribuzione sia “abbastanza uniforme”. A questo punto prendiamo il vettore-colore sconosciuto e calcoliamo la distanza pitagorica del vettore con quelli in lista. Il nome del colore sarà quello del vettore meno distante. Formulato in JavaScript:

DefaultColors = [
  new Color(0,0,0, "nero"),
  new Color(255,255,255, "bianco"),
  new Color(255,0,0, "rosso"),
  new Color(0,255,0, "verde"),
  new Color(0,0,255, "blue"),
  new Color(255,255, 0, "giallo"),
  new Color(255, 128, 0, "arancione"),
  new Color(102,102,102, "ocra"),
  new Color(75, 153, 0, "verde scuro"),
  new Color(0, 204, 102, "verde acqua"),
  new Color(0, 204,204, "celeste"),
  new Color(0, 51, 102, "blu scuro"),
  new Color(153, 153,255, "lilla"),
  new Color(102, 0, 102, "viola"),
  new Color(204, 0, 204, "fucsia"),
  new Color(255, 152, 255, "lilla"),
  new Color(153, 0, 76, "porpora"),
  new Color(96,96,96, "grigio"),
  new Color(192,192,192, "grigio chiaro"),
  new Color(102, 51, 0, "marrone"),
  new Color(153, 102, 51, "marrone chiaro"),
  new Color(204, 204, 0, "giallo verde"),
  new Color(204, 51, 0, "terra rossa"),
  new Color(51, 153, 51, "smeraldo"),
  new Color(102, 153, 153, "azzurro petrolio"),
  new Color(255, 204, 204, "rosa"),
  new Color(0, 51, 204, "blu nitido")]

function Color(red, green, blue, name) {
  this.Red = red
  this.Green = green
  this.Blue = blue
  if(name != null) { this.Name = name }

  this.Distance = function(color) {
      return Math.sqrt(this.Pow(this.Red - color.Red) + this.Pow(this.Green - color.Green) + this.Pow(this.Blue - color.Blue))
  }

  this.GetApproxName = function () {
      var min = null
      if (this.Name != undefined) return this.Name
      for (i in DefaultColors) {
          if (this.Distance(DefaultColors[i]) < min || min == null) {
              min = this.Distance(DefaultColors[i])
              this.Name = DefaultColors[i].Name
          }
      }
      return this.Name
  }

  this.Darkness = function () {
      toBlackDistance = this.Distance(DefaultColors[0]);
      maxDistance = DefaultColors[0].Distance(DefaultColors[1])
      ratio = 1 - toBlackDistance / maxDistance;
      return ratio.toFixed(2);
  } 
  
  this.Lightness = function () {
      toWhiteDistance = this.Distance(DefaultColors[1]);
      maxDistance = DefaultColors[0].Distance(DefaultColors[1])
      ratio = 1 - toWhiteDistance / maxDistance;
      return  ratio.toFixed(2);
  }

  this.Pow = function(what) {
      return what*what
  }
}

L’esecuzione per esempio potrebbe essere:

var cc = new Color(102, 0, 51)
console.log("nome approx:" + cc.GetApproxName())
console.log("chiarezza:" + cc.Lightness())
console.log("scurezza:" + cc.Darkness())

Ho anche inserito un paio di metodi per scoprire quanto un colore sia chiaro o scuro. Potrebbe esservi utile nel caso vogliate adattare il colore del testo allo sfondo (nel qual caso nella grigli dei colori basterà lasciare anche solo bianco e nero.

Considerazioni finali

Ovviamente lo script è tanto più preciso quanto più la distribuzione dei colori noti (ovvero i vettori della matrice) è egualmente distribuita nello spazio.

Non è il caso del mio esempio: per una migliore (e più lunga) implementazione bisognerebbe dividere ogni asse in, per esempio, 10 parti e quindi effettuare il prodotto cartesiano fra i punti trovati. Adesso si deve inserire per ogni terna il nome del colore corrispondente (a mano!) e avere così lo script ottimizzato.