<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>El vol de l&#039;home ocell &#187; complexitat computacional</title>
	<atom:link href="http://www.elvoldelhomeocell.net/archives/tag/complexitat-computacional/feed" rel="self" type="application/rss+xml" />
	<link>http://www.elvoldelhomeocell.net</link>
	<description>Vull tenir dret a volar.</description>
	<lastBuildDate>Thu, 26 Apr 2012 00:57:47 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Correcció automàtica en l&#8217;Oracle: Trie</title>
		<link>http://www.elvoldelhomeocell.net/archives/1553</link>
		<comments>http://www.elvoldelhomeocell.net/archives/1553#comments</comments>
		<pubDate>Sat, 05 Nov 2011 16:20:41 +0000</pubDate>
		<dc:creator>Joan</dc:creator>
				<category><![CDATA[Informàtica]]></category>
		<category><![CDATA[Jo]]></category>
		<category><![CDATA[Programació]]></category>
		<category><![CDATA[Projectes]]></category>
		<category><![CDATA[algorismes]]></category>
		<category><![CDATA[cerca aproximada]]></category>
		<category><![CDATA[complexitat computacional]]></category>
		<category><![CDATA[correcció automàtica]]></category>
		<category><![CDATA[estructures de dades]]></category>
		<category><![CDATA[friki]]></category>
		<category><![CDATA[oracle de woody allen]]></category>
		<category><![CDATA[trie]]></category>

		<guid isPermaLink="false">http://www.elvoldelhomeocell.net/?p=1553</guid>
		<description><![CDATA[Continuem amb la segona alternativa que vaig fer servir per a permetre la correcció automàtica, o si voleu cerca aproximada. Ja us vaig comentar en l&#8217;anterior entrada els problemes que presenta la cerca dicotòmica amb cadenes ordenades per ordre lexicogràfic &#8230; <a href="http://www.elvoldelhomeocell.net/archives/1553">Continua llegint <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Continuem amb la <a href="http://www.elvoldelhomeocell.net/archives/1515" target="_blank">segona</a> alternativa que vaig fer servir per a permetre la <a href="http://en.wikipedia.org/wiki/Spell_checker" target="_blank">correcció automàtica</a>, o si voleu <a href="http://en.wikipedia.org/wiki/Approximate_string_matching" target="_blank">cerca aproximada</a>.</p>
<p>Ja us vaig comentar en l&#8217;anterior entrada els problemes que presenta la cerca dicotòmica amb cadenes ordenades per ordre lexicogràfic per a buscar alternatives a una cerca que conté algun error. Semblava doncs que l&#8217;ordre lexicogràfic no era suficient per a establir la relació que jo necessitava entre els noms de les persones, així que la segona idea que vaig tenir és la d&#8217;utilitzar la famosa <a href="http://en.wikipedia.org/wiki/Levenshtein_distance" target="_blank">distància de Levenshtein</a>.</p>
<p>Sols comentar que quan estava escrivint aquest post em vaig adonar que tenia un greu error en el meu algorisme, de fet vaig haver de canviar-lo. Això em va servir per a entendre bé aquest nou algorisme que explique ací i millorar alguns aspectes en la implementació del meu programa. Eixe és el motiu pel qual m&#8217;ha dut quasi una setmana publicar aquesta entrada.</p>
<p>Avisar-vos també que aquest post és molt més llarg i pesat que l&#8217;anterior, això sí, molt més interessant també. Els algorismes que ací descric no són cosa meua i poden haver errors en la descripció de l&#8217;algorisme o la seva complexitat. Evidentment, aquest blog no tracta de ser una font de referència i no deuríeu de confiar excessivament en els detalls que s&#8217;expliquen ja que poden ser imprecisos o inclús erronis. Si us interessa el tema i voleu informació en la que pugueu confiar més, sols heu de cercar els termes en articles de la <a href="http://en.wikipedia.org" target="_blank">Wikipedia</a> o els algorismes en publicacions de <a href="http://scholar.google.com/" target="_blank">Google Scholar</a>.</p>
<p>Fet aquest avís, comencem.</p>
<p><strong id="damerau-levenshtein">Distància de Damerau-Levenshtein</strong></p>
<p>La <a href="http://en.wikipedia.org/wiki/Levenshtein_distance" target="_blank">distància de Levenshtein</a> mesura la distància d&#8217;edició entre dues cadenes de text en termes d&#8217;insercions, esborrats i substitucions. Això és, el nombre mínim d&#8217;aquestes operacions que són necessàries per transformar una cadena <em>A</em> en una altra cadena <em>B</em>. Si volem comptar també les transposicions com a operacions, la mètrica s&#8217;anomena distància de <a href="http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance" target="_blank">Damerau-Levenshtein</a>. L&#8217;algorisme utilitzat per a computar aquesta mètrica és un clàssic de qualsevol curs de <a href="http://en.wikipedia.org/wiki/Dynamic_programming" target="_blank">programació dinàmica</a>. </p>
<p>Intentaré explicar l&#8217;algorisme de manera breu. Imaginem que volem calcular la distància d&#8217;una cadena <em>X</em> a una cadena <em>Y</em>. Llavors és construeix una matriu <em>D</em> de <em>|X|+1</em> files i <em>|Y|+1</em> columnes. L&#8217;element <img src='http://s.wordpress.com/latex.php?latex=D_%7Bij%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{ij}' title='D_{ij}' class='latex' /> indica quina és la distància de la subcadena <img src='http://s.wordpress.com/latex.php?latex=x_1%20%5Cldots%20x_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_1 \ldots x_i' title='x_1 \ldots x_i' class='latex' /> a la subcadena <img src='http://s.wordpress.com/latex.php?latex=y_1%20%5Cldots%20y_j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y_1 \ldots y_j' title='y_1 \ldots y_j' class='latex' />. La fila 0 i la columna 0 representen les distàncies de la cadena <img src='http://s.wordpress.com/latex.php?latex=%5Cepsilon&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\epsilon' title='\epsilon' class='latex' /> a <img src='http://s.wordpress.com/latex.php?latex=y_1%20%5Cldots%20y_j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y_1 \ldots y_j' title='y_1 \ldots y_j' class='latex' /> i de la cadena <img src='http://s.wordpress.com/latex.php?latex=x_1%20%5Cldots%20x_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_1 \ldots x_i' title='x_1 \ldots x_i' class='latex' /> a <img src='http://s.wordpress.com/latex.php?latex=%5Cepsilon&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\epsilon' title='\epsilon' class='latex' /> respectivament (<img src='http://s.wordpress.com/latex.php?latex=%5Cepsilon&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\epsilon' title='\epsilon' class='latex' /> és la cadena buida, aquella que no conté cap símbol).</p>
<p>Doncs bé, la fórmula per a calcular cada element de la taula és la següent.<img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/formula_d.png" alt="" title="formula_d" width="300" height="120" class="aligncenter size-full wp-image-1605" /></p>
<p><img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/formula_s.png" alt="" title="formula_s" width="138" height="43" class="aligncenter size-full wp-image-1606" /></p>
<p><img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/formula_t.png" alt="" title="formula_t" width="213" height="43" class="aligncenter size-full wp-image-1607" /></p>
<p>Ací teniu un exemple de com quedaria matriu per a les cadenes <em>hola</em> i <em>poal</em>.</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20D%20%26%20%5Cepsilon%20%26%20p%20%26%20o%20%26%20a%20%26%20l%20%5C%5C%20%20%5Chline%20%5Cepsilon%20%26%200%20%26%201%20%26%202%20%26%203%20%26%204%20%5C%5C%20%20%5Chline%20h%20%26%201%20%26%201%20%26%202%20%26%203%20%26%204%5C%5C%20%20%5Chline%20o%20%26%202%20%26%202%20%26%201%20%26%202%20%26%203%5C%5C%20%20%5Chline%20l%20%26%203%20%26%203%20%26%202%20%26%202%20%26%202%5C%5C%20%20%5Chline%20a%20%26%204%20%26%204%20%26%203%20%26%202%20%26%202%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c|c|c|c|c|c|}  \hline D &amp; \epsilon &amp; p &amp; o &amp; a &amp; l \\  \hline \epsilon &amp; 0 &amp; 1 &amp; 2 &amp; 3 &amp; 4 \\  \hline h &amp; 1 &amp; 1 &amp; 2 &amp; 3 &amp; 4\\  \hline o &amp; 2 &amp; 2 &amp; 1 &amp; 2 &amp; 3\\  \hline l &amp; 3 &amp; 3 &amp; 2 &amp; 2 &amp; 2\\  \hline a &amp; 4 &amp; 4 &amp; 3 &amp; 2 &amp; 2\\  \hline  \end{array}  ' title='  \begin{array}{|c|c|c|c|c|c|}  \hline D &amp; \epsilon &amp; p &amp; o &amp; a &amp; l \\  \hline \epsilon &amp; 0 &amp; 1 &amp; 2 &amp; 3 &amp; 4 \\  \hline h &amp; 1 &amp; 1 &amp; 2 &amp; 3 &amp; 4\\  \hline o &amp; 2 &amp; 2 &amp; 1 &amp; 2 &amp; 3\\  \hline l &amp; 3 &amp; 3 &amp; 2 &amp; 2 &amp; 2\\  \hline a &amp; 4 &amp; 4 &amp; 3 &amp; 2 &amp; 2\\  \hline  \end{array}  ' class='latex' /></p>
<p>Si mirem l&#8217;element <img src='http://s.wordpress.com/latex.php?latex=D_%7B4%2C4%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{4,4}' title='D_{4,4}' class='latex' /> de la taula, veiem que la distància de Damerau-Levenshtein entre les dues cadenes és 2 (transformacion: <em>hola</em>-><em>pola</em>-><em>poal</em>). Fixeu-vos que és menor que la distància de Levenshtein (que no admet transposicions) i que seria 3.</p>
<p>La gràcia d&#8217;aquest algorisme és que pot anar construint-se fila a fila i columna a columna, de manera que el cost temporal de l&#8217;algorisme per a dues cadenes de longituds <em>m</em> i <em>n</em> és <img src='http://s.wordpress.com/latex.php?latex=O%28m%20%5Ccdot%20n%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(m \cdot n)' title='O(m \cdot n)' class='latex' />, a l&#8217;igual que el cost espacial.</p>
<p>El problema d&#8217;aquesta mètrica és el cost: no és lineal. No era una alternativa adequada simplement computar per a cada cerca la seva distància de Damerau-Levenshtein amb els noms de les persones i quedar-me amb aquelles amb un mínim error. Suposant que tenim <em>n</em> persones i la longitud dels seus noms és aproximadament <em>l</em>, el cost temporal d&#8217;aquesta cerca seria <img src='http://s.wordpress.com/latex.php?latex=O%28n%20%5Ccdot%20l%5E2%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n \cdot l^2)' title='O(n \cdot l^2)' class='latex' />, un cost massa alt si el comparem amb el de la cerca dicotòmica (<img src='http://s.wordpress.com/latex.php?latex=O%28l%20%5Ccdot%20%5Clog_2%20n%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(l \cdot \log_2 n)' title='O(l \cdot \log_2 n)' class='latex' />).</p>
<p><strong id="damerau-levenshtein-ukkonen">Millora d&#8217;Ukkonen</strong></p>
<p>El primer que hem de tenir clar és que no necessitem saber la distància exacta entre dues paraules. Imaginem que un usuari s&#8217;equivoca escrivint la paraula que busca i escriu <em>ocel</em>. Què serà més probable, que l&#8217;usuari estigués buscant una paraula que està a una distància 1 (p.ex: <em>ocell</em>) o que estigués buscant una paraula a una distància 5 (p.ex: <em>tomaca</em>)? Realment, sols estem interessats en saber si la distància entre dues cadenes <em>X</em> i <em>Y</em> és menor o igual que un cert llindar <em>k</em>.</p>
<p>Si estem calculant la distància entre dues cadenes i tenim una fila per a la qual, cap element és menor o igual que <em>k</em>, llavors cap element de les següents files serà menor o igual que <em>k</em>.</p>
<p>A més, siga <img src='http://s.wordpress.com/latex.php?latex=C_i%20%3D%20j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='C_i = j' title='C_i = j' class='latex' />, l&#8217;última columna <em>j</em> per a la fila <em>i</em> tal que <img src='http://s.wordpress.com/latex.php?latex=D_%7Bi%2Cj%7D%20%5Cleq%20k&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{i,j} \leq k' title='D_{i,j} \leq k' class='latex' />, llavors quan construïm la fila <em>i</em> sols haurem de fer-ho fins la columna <img src='http://s.wordpress.com/latex.php?latex=C_%7Bi-1%7D%2B1&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='C_{i-1}+1' title='C_{i-1}+1' class='latex' />, ja que els elements de les columnes posteriors tindran un valor <img src='http://s.wordpress.com/latex.php?latex=D_%7Bi%2Cj%7D%20%3E%20k&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{i,j} &gt; k' title='D_{i,j} &gt; k' class='latex' />.</p>
<p>Ací teniu un exemple de com quedaria la matriu per a les cadenes <em>hola</em> i <em>poal</em>, amb un valor de <em>k</em> igual a 1.</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7C%7Cc%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20C%20%26%20D%20%26%20%5Cepsilon%20%26%20p%20%26%20o%20%26%20a%20%26%20l%20%5C%5C%20%20%5Chline%201%20%26%20%5Cepsilon%20%26%200%20%26%201%20%26%20%20%26%20%20%26%20%20%5C%5C%20%20%5Chline%201%20%26%20h%20%26%201%20%26%201%20%26%202%20%26%20%20%26%20%5C%5C%20%20%5Chline%202%20%26%20o%20%26%202%20%26%202%20%26%201%20%26%20%20%26%20%5C%5C%20%20%5Chline%200%20%26%20l%20%26%203%20%26%203%20%26%202%20%26%202%20%26%20%5C%5C%20%20%5Chline%200%20%26%20a%20%26%20%20%26%20%20%26%20%20%26%20%20%26%20%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c||c|c|c|c|c|c|}  \hline C &amp; D &amp; \epsilon &amp; p &amp; o &amp; a &amp; l \\  \hline 1 &amp; \epsilon &amp; 0 &amp; 1 &amp;  &amp;  &amp;  \\  \hline 1 &amp; h &amp; 1 &amp; 1 &amp; 2 &amp;  &amp; \\  \hline 2 &amp; o &amp; 2 &amp; 2 &amp; 1 &amp;  &amp; \\  \hline 0 &amp; l &amp; 3 &amp; 3 &amp; 2 &amp; 2 &amp; \\  \hline 0 &amp; a &amp;  &amp;  &amp;  &amp;  &amp; \\  \hline  \end{array}  ' title='  \begin{array}{|c||c|c|c|c|c|c|}  \hline C &amp; D &amp; \epsilon &amp; p &amp; o &amp; a &amp; l \\  \hline 1 &amp; \epsilon &amp; 0 &amp; 1 &amp;  &amp;  &amp;  \\  \hline 1 &amp; h &amp; 1 &amp; 1 &amp; 2 &amp;  &amp; \\  \hline 2 &amp; o &amp; 2 &amp; 2 &amp; 1 &amp;  &amp; \\  \hline 0 &amp; l &amp; 3 &amp; 3 &amp; 2 &amp; 2 &amp; \\  \hline 0 &amp; a &amp;  &amp;  &amp;  &amp;  &amp; \\  \hline  \end{array}  ' class='latex' /></p>
<p>Si consultem el valor de l&#8217;element <img src='http://s.wordpress.com/latex.php?latex=D_%7Bi%2Cj%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{i,j}' title='D_{i,j}' class='latex' /> aquest sols és vàlid si <img src='http://s.wordpress.com/latex.php?latex=C_i%20%3E%200&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='C_i &gt; 0' title='C_i &gt; 0' class='latex' />. Si no és així, sabem que <img src='http://s.wordpress.com/latex.php?latex=D_%7Bi%2Cj%7D%20%3E%20k&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D_{i,j} &gt; k' title='D_{i,j} &gt; k' class='latex' />.</p>
<p>Doncs bé, tenint en compte aquesta millora, el cost temporal de l&#8217;algorisme es redueix a <img src='http://s.wordpress.com/latex.php?latex=O%28k%5E2%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(k^2)' title='O(k^2)' class='latex' />, segons diu la <a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.37.2019&#038;rep=rep1&#038;type=pdf" target="_blank">publicació</a> d&#8217;on vaig treure l&#8217;algorisme.</p>
<p>Així i tot, no resulta interessant aplicar aquest algorisme sobre tots els noms ja que el cost estaria en <img src='http://s.wordpress.com/latex.php?latex=O%28n%20%5Ctimes%20k%5E2%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n \times k^2)' title='O(n \times k^2)' class='latex' />. Per a <em>k</em> xicotetes és molt menor que <img src='http://s.wordpress.com/latex.php?latex=O%28n%20%5Ctimes%20l%5E2%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n \times l^2)' title='O(n \times l^2)' class='latex' /> de la idea original, però que encara es pot millorar.</p>
<p>Necessitava tenir els noms de les persones representats d&#8217;alguna manera per a estalviar-me treball reduir aquest cost. I ací és on entren en joc els <em><a href="http://en.wikipedia.org/wiki/Trie" target="_blank">tries</a></em>.</p>
<p><strong id="trie">Trie</strong></p>
<p>Un <em>trie</em> no és més que un arbre de prefixes. El node arrel representa el prefixe de la cadena buida (o <img src='http://s.wordpress.com/latex.php?latex=%5Cepsilon&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\epsilon' title='\epsilon' class='latex' />). Cada aresta de l&#8217;arbre s&#8217;etiqueta amb un símbol de l&#8217;alfabet <img src='http://s.wordpress.com/latex.php?latex=%5CSigma&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\Sigma' title='\Sigma' class='latex' />, de manera que el node representa el prefix format per la concatenació de tots els símbols en les arestes des de l&#8217;arrel fins a l&#8217;esmentat node.</p>
<p>Vegem-ho més clar en un exemple. Suposem que tenim els noms de <a href="http://en.wikipedia.org/wiki/Alan_Turing" target="_blank">Alan</a>, <a href="http://en.wikipedia.org/wiki/Andrew_S._Tanenbaum" target="_blank">Andrew</a>, <a href="http://en.wikipedia.org/wiki/Leslie_Lamport" target="_blank">Leslie</a>, <a href="http://en.wikipedia.org/wiki/Johan_H%C3%A5stad" target="_blank">Johan</a> i <a href="http://en.wikipedia.org/wiki/John_Hopcroft" target="_blank">John</a>. El trie que representaria tots aquests noms és el següent.<br />
<img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/trie1.png" alt="" title="trie1" width="350" height="633" class="aligncenter size-full wp-image-1564" /></p>
<p>Com vegeu, les fulles de l&#8217;arbre corresponen al nom complet d&#8217;alguna de les persones: <em>allan</em>, <em>john</em>, <em>johan</em>, <em>andrew</em> i <em>leslie</em> (recorreguent l&#8217;arbre en amplada).</p>
<p>Un dels avantatges d&#8217;utilitzar un arbre de prefixes és que el cost de buscar una persona no depèn del nombre de persones. Amb una cadena de longitud <em>l</em>, el cost de la cerca és <img src='http://s.wordpress.com/latex.php?latex=O%28l%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(l)' title='O(l)' class='latex' />. Simplement hem de descendir de nivell en nivell en l&#8217;arbre per a cada lletra del nom. </p>
<p><strong id="radixtree">Compressió de camins</strong></p>
<p>El problema dels tries, com ja podreu deduir de la imatge anterior és l&#8217;espai que ocupen en memòria. Suposant de nou que totes les cadenes tenen la mateixa longitud <em>l</em>, en el pitjor dels casos, cap de les <em>n</em> cadenes codificades en l&#8217;arbre tindrà un prefixe comú i en aquest cas el nombre de nodes és <img src='http://s.wordpress.com/latex.php?latex=O%28n%20%5Ccdot%20l%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n \cdot l)' title='O(n \cdot l)' class='latex' /> (encara que això sols passar quan <img src='http://s.wordpress.com/latex.php?latex=n%20%5Cleq%20%5Cvert%20%5CSigma%20%5Cvert&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='n \leq \vert \Sigma \vert' title='n \leq \vert \Sigma \vert' class='latex' />). </p>
<p>A primera vista sembla que no és gran cosa, ja que té el mateix cost temporal que mantenir els noms en un vector. El problema és que necessitem emmagatzemar per a cada node quins són els seus descendents.</p>
<p>Si volem mantenir el cost de cerca <img src='http://s.wordpress.com/latex.php?latex=O%28l%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(l)' title='O(l)' class='latex' />, necessitem determinar en cost <img src='http://s.wordpress.com/latex.php?latex=O%281%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(1)' title='O(1)' class='latex' /> per quina branca em de descendir quan busquem una cadena. La resposta en aquest cas és evident: Mantenir un vector de punters amb tants elements com símbols té l&#8217;alfabet amb el que treballem. Si hi ha algun descendent en el node per al símbol <em>i</em>, el punter <img src='http://s.wordpress.com/latex.php?latex=p_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='p_i' title='p_i' class='latex' /> apuntarà a aquest descendent. Mentre que si <img src='http://s.wordpress.com/latex.php?latex=p_i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='p_i' title='p_i' class='latex' /> és nul, sabem que no hi ha cap descendent del node actual per al símbol <em>i</em>. D&#8217;aquesta manera, el cost espacial de mantenir l&#8217;arbre de prefixos és <img src='http://s.wordpress.com/latex.php?latex=O%28%20l%20%5Ccdot%20n%20%5Ccdot%20%5Cvert%20%5CSigma%20%5Cvert%20%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O( l \cdot n \cdot \vert \Sigma \vert )' title='O( l \cdot n \cdot \vert \Sigma \vert )' class='latex' />.</p>
<p>El cost ja no és tan atractiu com semblava. Suposem que el nostre alfabet té 26 caràcters (de la <em>a</em> a la <em>z</em>), que tenim 3 milions de persones i la longitud mitjana dels noms és de 30 caràcters. Suposant que els punters són de 4 bytes, com serien en un processador de 32 bits, mantenir aquesta estructura podria necessitar al voltant de 9GB! </p>
<p>Evidentment, en la realitat el nombre de nodes és molt menor ja que els noms sí que tenen prefixos en comú així i tot en la realitat no només tenim 26 caràcters en l&#8217;alfabet: els noms de les persones de <a href="http://www.imdb.com" target="_blank">IMDB</a> inclouen també números, espais, símbols d&#8217;exclamació, caràcters accentuats i caràcters d&#8217;altres alfabets que no són el llatí (ciríl·lic o grec, per exemple).</p>
<p>Com solucionem aquest problema? En primer lloc, tractant de reduir el nombre de nodes i això pot fer-se fàcilment utilitzant compressió de camins en l&#8217;arbre. Què és la compressió de camins? Fixeu-se en el primer arbre que us he presentat. Veureu que molts dels nodes sols tenen un descendent. No seria possible unir en un únic node tots els nodes que tenen un únic descendent? Això és la compressió de camins i aquest <em>trie</em> s&#8217;anomena <em><a href="http://en.wikipedia.org/wiki/Radix_tree" target="_blank">Radix Tree</a></em>. Veieu com quedaria l&#8217;arbre anterior amb els camins comprimits.<br />
<img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/10/trie2.png" alt="" title="trie2" width="350" height="304" class="aligncenter size-full wp-image-1567" /></p>
<p><strong id="reduint-espai-nodes">Reduint l&#8217;espai dels nodes</strong></p>
<p>Una altra millora que pot fer-se per a reduir l&#8217;espai és intentar reduir el nombre de punters que s&#8217;emmagatzemen en cada node. Havíem dit que si volem mantenir el cost de cerca en <img src='http://s.wordpress.com/latex.php?latex=O%28l%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(l)' title='O(l)' class='latex' /> necessitem un vector de <img src='http://s.wordpress.com/latex.php?latex=%5Cvert%20%5CSigma%20%5Cvert&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\vert \Sigma \vert' title='\vert \Sigma \vert' class='latex' /> punters. Però no podem reduir aquest nombre? La resposta és sí, però a costa d&#8217;incrementar el cost de cerca.</p>
<p>Una possible millora és emmagatzemar els punters en un arbre de cerca (en el meu cas he utilitzat el map de la STL de C++). Així, en cada node sols s&#8217;emmagatzema un punter per a cada descendent i no per a cada símbol de l&#8217;alfabet (per tant, no tenim punters nuls que ocupen memòria). Si en un node no hi ha cap punter per al símbol que busquem, llavors no hi ha descendent per a aquest símbol. D&#8217;aquesta manera el nombre de punters en cada node es redueix considerablement, especialment a mesura que es descendeix en l&#8217;arbre, que és també quan més nodes hi ha. Penseu que és probable que tinguem noms començant en totes les lletres (el node arrel tindrà tants descendents com símbols té l&#8217;alfabet), en canvi és poc probable que tinguem molts noms amb el mateix prefix i amb l&#8217;última lletra diferent (els nodes de nivells inferiors tindran pocs descendents).</p>
<p>El problema és que ara el cost de cercar una cadena en el trie és <img src='http://s.wordpress.com/latex.php?latex=O%28l%20%5Ccdot%20%5Clog_2%20p%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(l \cdot \log_2 p)' title='O(l \cdot \log_2 p)' class='latex' />, on <em>p</em> és el nombre de punters en els nodes (<img src='http://s.wordpress.com/latex.php?latex=p%20%5Cleq%20%5Cvert%20%5CSigma%20%5Cvert&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='p \leq \vert \Sigma \vert' title='p \leq \vert \Sigma \vert' class='latex' />, normalment prou menor). </p>
<p>Amb totes aquestes millores, el meu programa ocupa ara mateixa 3686MB en memòria, que és bastant menys dels 9GB que comentàvem al començament. Hi ha que tenir en compte a més, que aquests 3686MB no són només del <em>trie</em> sinó també de mantenir els títols de les pel·lícules, el graf que relaciona pel·lícules i actors i tota la informació corresponent a cada persona i pel·lícula. </p>
<p>Comentaré també que em vaig endur una desagradable sorpresa al passar el programa d&#8217;una màquina de 32 bits a una de 64 bits: els punters van passar d&#8217;ocupar 4 bytes a ocupar-ne 8 i clar, emmagatzemar el mateix arbre necessitava el doble de memòria!</p>
<p><strong id="approximate-matching">Cerca aproximada</strong></p>
<p>Hem parlat molt de com emmagatzemar eficientment el <em>trie</em>, però encara no hem solucionat el problema que havíem plantejat: fer una cerca aproximada donada una cadena <em>X</em>. Ací és on unim l&#8217;algorisme de Damerau-Levenshtein explicat <a href="http://www.elvoldelhomeocell.net/?p=1553#damerau-levenshtein-ukkonen">abans</a> i els <em><a href="http://www.elvoldelhomeocell.net/?p=1553#trie">tries</a></em>.</p>
<p>Hi ha dos coses importants a tenir en compte a l&#8217;hora de calcular la distància de Damerau-Levenshtein sobre distintes cadenes:</p>
<ol>
<li>Al calcular la distància entre la cadena <em>X</em> i la cadena <em>YZ</em>, les <em>|Y|+1</em> primeres files de la matriu de Damerau-Levenshtein seran iguals a les del càlcul de la distància entre la cadena <em>X</em> i la cadena <em>Y</em>.</li>
<li>Si per al càlcul de la distància entre una cadena <em>X</em> i una cadena <em>Y</em>, totes les columnes de la fila <em>|Y|</em> tenen un valor major que <em>k</em>, cap cadena amb el prefix <em>Y</em> estarà a una distància menor que <em>k</em> de <em>X</em>.</li>
</ol>
<p>El <em>trie</em> permet estalviar temps ja què la matriu de Damerau-Levenshtein va calculant-se iterativament quan descendim en l&#8217;arbre, pel que havíem comentat en la propietat (1). A més, tenint en compte la propietat (2), si per a un node la distància a la cadena buscada és major que un cert llindar <em>k</em> llavors sabem que cap dels descendents d&#8217;aquest node tindrà un llindar menor, de manera que no hem d&#8217;explorar els descendents. </p>
<p><img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/approximate_lookup_algorithm.png" alt="" title="approximate_lookup_algorithm" width="454" height="274" class="aligncenter size-full wp-image-1655" /></p>
<p>Algunes consideracions sobre les operacions que apareixen en el pseudocodi. L&#8217;operació <img src='http://s.wordpress.com/latex.php?latex=update%28D%2C%20C%2C%20x%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='update(D, C, x)' title='update(D, C, x)' class='latex' /> el que fa és actualitzar la matriu <em>D</em> afegint <em>|x|</em> files i computant el valor dels nous elements i el vector <em>C</em> com he explicat en la millora de Ukkonen.</p>
<p>L&#8217;operació <img src='http://s.wordpress.com/latex.php?latex=dist%28D%27%2C%20C%27%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='dist(D&#039;, C&#039;)' title='dist(D&#039;, C&#039;)' class='latex' /> el que fa es tornar el valor de l&#8217;element <img src='http://s.wordpress.com/latex.php?latex=D%27_%7B%7Cv%7C%20%2C%20%7Cw%7C%20%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='D&#039;_{|v| , |w| }' title='D&#039;_{|v| , |w| }' class='latex' /> tenint en compte que potser aquest element no ha arribat a calcular-se en l&#8217;operació <em>update</em>. Aquest valor és la distància que hi ha entre les cadenes <em>v</em> i <em>w</em> si aquesta distància és menor o igual que <em>k</em>, o <em>k+1</em> en cas contrari.</p>
<p>La gràcia d&#8217;aquest algorisme és que té una complexitat temporal de <img src='http://s.wordpress.com/latex.php?latex=O%28k%20%5Ctimes%20%7B%5Cvert%20%5CSigma%20%5Cvert%7D%5Ek%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(k \times {\vert \Sigma \vert}^k)' title='O(k \times {\vert \Sigma \vert}^k)' class='latex' />, segons l&#8217;article que us citava <a href="http://www.elvoldelhomeocell.net/?p=1553#damerau-levenshtein-ukkonen">abans</a>. Això suposant que l&#8217;arbre de prefixos és complet i que cada node emmagatzema un vector de punters als possibles descendents (és a dir, sense tenir en compte les millores per a <a href="http://www.elvoldelhomeocell.net/?p=1553#radixtree">reduir</a> <a href="http://www.elvoldelhomeocell.net/?p=1553#reduint-espai-nodes">espai</a>). </p>
<p>Fixeu-vos que l&#8217;algorisme no depèn del nombre de cadenes en el <em>trie</em>! Això significa que podem trobar els noms que més s&#8217;aproximen a una cerca feta per l&#8217;usuari sense importar quants noms tenim emmagatzemats. Una considerable millora front al cost <img src='http://s.wordpress.com/latex.php?latex=O%28n%20%5Ctimes%20k%5E2%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n \times k^2)' title='O(n \times k^2)' class='latex' />, això sí, sempre que <em>k</em> siga menuda, perquè el cost creix de manera exponencial amb <em>k</em>!</p>
<p>El gràfic que es mostra ací baix és el temps mitjà de resposta per part de l&#8217;oracle quan es busca el nom d&#8217;un actor que no existeix en la base de dades. El valor de <em>k</em> que utilitze és <img src='http://s.wordpress.com/latex.php?latex=%5Cmin%283%2C%20%5Cleft%5Clfloor%20%5Cfrac%7B%7Cw%7C%7D%7B4%7D%20%5Cright%5Crfloor%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\min(3, \left\lfloor \frac{|w|}{4} \right\rfloor)' title='\min(3, \left\lfloor \frac{|w|}{4} \right\rfloor)' class='latex' />. Si usés un valor fixe per a <em>k</em> hi hauria molts suggeriments per a noms curts que realment no tenen massa sentit. Anem al cas extrem: imaginem que un usuari busca el nom <em>a</em>, té sentit suggerir-li els noms <em>b</em>, <em>c</em>, <em>d</em>, etc? La distància a tots ells és 1, però és que la longitud de la cadena era 1! Això significaria que l&#8217;usuari ha errat en el 100% de la cadena! Jo permet a l&#8217;usuari que s&#8217;equivoqui fins a un 25% de la longitud de la cadena. Però si la cadena cercada fos molt llarga el valor <em>k</em> seria molt gran, per això pose un límit al número d&#8217;errors a 3.</p>
<p><a href="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/lookup_times.png" rel="lightbox[1553]"><img src="http://www.elvoldelhomeocell.net/wp-content/uploads/2011/11/lookup_times.png" alt="" title="lookup_times" width="400" height="250" class="aligncenter size-full wp-image-1660" /></a></p>
<p>Dir que l&#8217;<a href="http://en.wikipedia.org/wiki/Confidence_interval" target="_blank">interval de confiança</a> que es mostra és per a un valor crític del 95%. Crec que el gràfic no mereix més explicacions. El temps de resposta mitjà és de 0.208 segons amb totes les millores comentades. També apareix el temps de resposta utilitzant el càlcul de la distància de Damerau-Levenshtein sense la millora de Ukkonen (0.301 segons).</p>
<p><strong>Encara millor?</strong></p>
<p>Des del primer moment vaig tenir com a referència a Google i el seu &#8220;<em><a href="http://www.google.com/support/websearch/bin/answer.py?answer=1723" target="_blank">Did you mean&#8230;?</a></em>&#8220;. I tenia ben clar que els de Google no podien utilitzar un <em>trie</em> gegantí perquè el vocabulari amb el que treballen és enorme: imagineu totes les paraules diferents que poden haver en la WWW i el que suposaria construir un <em>trie</em> com el descrit! </p>
<p>Google utilitza un <a href="http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/pubs/archive/36180.pdf" target="_blank">enfocament</a> basat en <a href="http://en.wikipedia.org/wiki/Machine_learning" target="_blank">aprenentatge automàtic</a> i <a href="http://en.wikipedia.org/wiki/Natural_language_processing" target="_blank">processament del llenguatge natural</a>. I aquesta va ser la ruta que em vaig proposar seguir i que us explicaré en el pròxim post.</p>
<p>Mentrestant els <em>tries</em> han funcionat prou bé: són ràpids i sobretot obtenen els resultats que l&#8217;usuari espera. I poden ser una alternativa senzilla en situacions amb un vocabulari i un alfabet més reduït. Per exemple, per a construir un corrector automàtic en un editor de textos.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elvoldelhomeocell.net/archives/1553/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Correcció automàtica en l&#8217;Oracle: cerca dicotòmica</title>
		<link>http://www.elvoldelhomeocell.net/archives/1515</link>
		<comments>http://www.elvoldelhomeocell.net/archives/1515#comments</comments>
		<pubDate>Mon, 31 Oct 2011 00:59:53 +0000</pubDate>
		<dc:creator>Joan</dc:creator>
				<category><![CDATA[Informàtica]]></category>
		<category><![CDATA[Jo]]></category>
		<category><![CDATA[Programació]]></category>
		<category><![CDATA[Projectes]]></category>
		<category><![CDATA[algorismes]]></category>
		<category><![CDATA[cerca aproximada]]></category>
		<category><![CDATA[cerca binària]]></category>
		<category><![CDATA[cerca dicotòmica]]></category>
		<category><![CDATA[complexitat computacional]]></category>
		<category><![CDATA[correcció automàtica]]></category>
		<category><![CDATA[friki]]></category>
		<category><![CDATA[oracle de woody allen]]></category>

		<guid isPermaLink="false">http://www.elvoldelhomeocell.net/?p=1515</guid>
		<description><![CDATA[La màxima &#8220;qui no te feina, Déu li&#8217;n busca&#8221; és ben certa, però a sovint alguns anem més enllà i ens en busquem de nova quan encara no hem acabat la que tenim entre mans. I si no tenia prou &#8230; <a href="http://www.elvoldelhomeocell.net/archives/1515">Continua llegint <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>La màxima &#8220;qui no te feina, Déu li&#8217;n busca&#8221; és ben certa, però a sovint alguns anem més enllà i ens en busquem de nova quan encara no hem acabat la que tenim entre mans. I si no tenia prou amb estar d&#8217;Erasmus, els <a href="http://ai-class.com" target="_blank">cursets</a> a <a href="http://ml-class.org" target="_blank">distància</a> de Stanford i la competició de <a href="http://aichallenge.org/" target="_blank">ai-challange.com</a>, farà un parell de setmanes que em ronda pel cap una nova millora per al meu Oracle de Woody Allen. Però bo, no vaig a queixar-me perquè no es pot dir que la feina vinga forçada.</p>
<p>Recordeu l&#8217;<a href="http://joapuipe.dyndns.org/allen/" target="_blank">Oracle de Woody Allen</a>? Ja us he parlat d&#8217;aquest xicotet projecte en <a href="http://www.elvoldelhomeocell.net/archives/990" target="_blank">altres</a> <a href="http://www.elvoldelhomeocell.net/archives/997" target="_blank">posts</a>, però us refrescaré la memòria. Es tracta d&#8217;una senzilla pàgina web que et permet trobar quina connexió uneix a dos persones del món del cinema (actors, directors, guionistes i productors). La idea és que si dos persones han treballat juntes en una pel·lícula, hi ha una distància de separació de 1. De la mateixa manera, Si la persona <em>A</em> ha treballat amb la persona <em>B</em>, i aquesta amb la pesona <em>C</em>, les persones <em>A</em> i <em>C</em> estan a una distància de 2. I així successivament.</p>
<p>No em centraré amb els detalls de com es calcula aquesta distància i la connexió (camí, en terminologia de grafs) perquè no és del que va aquest article, simplement dir que s&#8217;utilitza l&#8217;algorisme de <a href="http://en.wikipedia.org/wiki/Breadth-first_search" target="_blank">cerca per amplada</a>.</p>
<p>Des de les primeres versions, volia que el meu programa fora capaç de &#8220;tolerar&#8221; errades humanes. Per exemple, si un usuari escriu &#8220;Wody Allen&#8221; en lloc de &#8220;Woody Allen&#8221;, el meu programa fora capaç de suggerir-li una <a href="http://en.wikipedia.org/wiki/Spell_checker" target="_blank">correcció automàtica</a>. En els següents tres articles comentaré dues alternatives que ha utilitzat el programa per a solucionar el problema i finalment una tercera que encara no he implementat, però que us plantejaré ja que encara queden alguns detalls per polir i amb un poc de sort algú es decideix a aportar alguna idea.</p>
<p>Avise que el contingut de les pròximes entrades és molt tècnic. Per als dos primers articles necessitareu coneixements bàsics d&#8217;algorísmica i complexitat computacional, res que un alumne de primer o segon d&#8217;Enginyeria Informàtica no puga entendre. Per al tercer, a més, necessitareu coneixements bàsics d&#8217;aprenentatge automàtic, que inclou estadística bàsica (bàsicament, teorema de Bayes). </p>
<p><strong>Cerca dicotòmica</strong></p>
<p>La primera tècnica que vaig utilitzar per a proveir de correccions a l&#8217;usuari era l&#8217;algorisme de <a href="http://en.wikipedia.org/wiki/Binary_search" target="_blank">cerca dicotòmica</a> o binària.</p>
<p>La idea és mantenir els noms de les persones en <a href="http://en.wikipedia.org/wiki/Lexicographical_order" target="_blank">ordre lexicogràfic</a>. No vaig a explicar l&#8217;algorisme de la cerca dicotòmica, perquè entenc que si heu arribat fins ací el coneixeu.</p>
<p>El que fa l&#8217;algorisme és suggerir com a correccions els noms més pròxims (en ordre lexicogràfic) al nom introduït per l&#8217;usuari. Per a veure com funciona l&#8217;algorisme, suposarem que els noms de les persones són nombres enters. Imaginem que tenim el següent conjunt de nombres en un vector ordenat (<img src='http://s.wordpress.com/latex.php?latex=i&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i' title='i' class='latex' /> indica l&#8217;índex de l&#8217;element i <img src='http://s.wordpress.com/latex.php?latex=p&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='p' title='p' class='latex' /> el <em>nom</em> de la persona):</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7Cc%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20%20i%20%26%200%20%26%201%20%26%202%20%26%203%20%26%204%20%26%205%20%5C%5C%20%20%5Chline%20%20p%20%26%201%20%26%202%20%26%203%20%26%205%20%26%207%20%26%209%20%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c|c|c|c|c|c|c|}  \hline  i &amp; 0 &amp; 1 &amp; 2 &amp; 3 &amp; 4 &amp; 5 \\  \hline  p &amp; 1 &amp; 2 &amp; 3 &amp; 5 &amp; 7 &amp; 9 \\  \hline  \end{array}  ' title='  \begin{array}{|c|c|c|c|c|c|c|}  \hline  i &amp; 0 &amp; 1 &amp; 2 &amp; 3 &amp; 4 &amp; 5 \\  \hline  p &amp; 1 &amp; 2 &amp; 3 &amp; 5 &amp; 7 &amp; 9 \\  \hline  \end{array}  ' class='latex' /></p>
<p>Suposem que busquem la persona amb <em>nom</em> 6, llavors l&#8217;algorisme s&#8217;executaria de la següent forma:</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20%5Ctext%7BIter.%7D%20%26%20l%20%26%20r%20%26%20c%20%26%20p_c%20%5C%5C%20%20%5Chline%201%20%26%200%20%26%205%20%26%202%20%26%203%20%5C%5C%20%20%5Chline%202%20%26%203%20%26%205%20%26%204%20%26%207%20%5C%5C%20%20%5Chline%203%20%26%203%20%26%203%20%26%203%20%26%205%20%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c|c|c|c|c|}  \hline \text{Iter.} &amp; l &amp; r &amp; c &amp; p_c \\  \hline 1 &amp; 0 &amp; 5 &amp; 2 &amp; 3 \\  \hline 2 &amp; 3 &amp; 5 &amp; 4 &amp; 7 \\  \hline 3 &amp; 3 &amp; 3 &amp; 3 &amp; 5 \\  \hline  \end{array}  ' title='  \begin{array}{|c|c|c|c|c|}  \hline \text{Iter.} &amp; l &amp; r &amp; c &amp; p_c \\  \hline 1 &amp; 0 &amp; 5 &amp; 2 &amp; 3 \\  \hline 2 &amp; 3 &amp; 5 &amp; 4 &amp; 7 \\  \hline 3 &amp; 3 &amp; 3 &amp; 3 &amp; 5 \\  \hline  \end{array}  ' class='latex' /></p>
<p>Com que l&#8217;últim element considerat (<img src='http://s.wordpress.com/latex.php?latex=p_c%5E%7B%283%29%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='p_c^{(3)}' title='p_c^{(3)}' class='latex' />) és menor que el cercat, es proposarien com a correccions els elements 5 (posició <img src='http://s.wordpress.com/latex.php?latex=c%5E%7B%283%29%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c^{(3)}' title='c^{(3)}' class='latex' />) i 7 (posició <img src='http://s.wordpress.com/latex.php?latex=c%5E%7B%283%29%7D%20%2B%201&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c^{(3)} + 1' title='c^{(3)} + 1' class='latex' />). En el cas de que l&#8217;últim element considerat fora major que el cercat, es proposarien com a correccions els elements en les posicions <img src='http://s.wordpress.com/latex.php?latex=c%5E%7B%283%29%7D%20-%201&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c^{(3)} - 1' title='c^{(3)} - 1' class='latex' /> i <img src='http://s.wordpress.com/latex.php?latex=c%5E%7B%283%29%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='c^{(3)}' title='c^{(3)}' class='latex' />.</p>
<p>Fixeu-se que en cas de no trobar un element, sempre proposarem un element menor i un major. I a més, sempre seran els dos que més s&#8217;aproximen a l&#8217;element cercat.</p>
<p>El problema és que els noms no són enters i l&#8217;ordre lexicogràfic té un xicotet problema: Dos noms als que sols els hem canviat una lletra poden estar molt separats en ordre lexicogràfic. Ací un exemple que il·lustra el problema:</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20i%20%26%200%20%26%201%20%26%202%20%26%203%20%5C%5C%20%20%5Chline%20p%20%26%20%5Ctext%7Bblai%7D%20%26%20%5Ctext%7Bjaume%7D%20%26%20%5Ctext%7Bjavi%7D%20%26%20%5Ctext%7Btoni%7D%20%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c|c|c|c|c|}  \hline i &amp; 0 &amp; 1 &amp; 2 &amp; 3 \\  \hline p &amp; \text{blai} &amp; \text{jaume} &amp; \text{javi} &amp; \text{toni} \\  \hline  \end{array}  ' title='  \begin{array}{|c|c|c|c|c|}  \hline i &amp; 0 &amp; 1 &amp; 2 &amp; 3 \\  \hline p &amp; \text{blai} &amp; \text{jaume} &amp; \text{javi} &amp; \text{toni} \\  \hline  \end{array}  ' class='latex' /></p>
<p>Suposem que en l&#8217;anterior situació, un usuari s&#8217;equivoca escrivint el nom de <em>J<strong>a</strong>ume</em> i escriu <em>J<strong>e</strong>ume</em>:</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20%5Cbegin%7Barray%7D%7B%7Cc%7Cc%7Cc%7Cc%7Cc%7C%7D%20%20%5Chline%20%5Ctext%7BIter.%7D%20%26%20l%20%26%20r%20%26%20c%20%26%20p_c%20%5C%5C%20%20%5Chline%201%20%26%200%20%26%203%20%26%201%20%26%20%5Ctext%7Bjaume%7D%20%5C%5C%20%20%5Chline%202%20%26%202%20%26%203%20%26%202%20%26%20%5Ctext%7Bjavi%7D%20%5C%5C%20%20%5Chline%203%20%26%203%20%26%203%20%26%203%20%26%20%5Ctext%7Btoni%7D%20%5C%5C%20%20%5Chline%20%20%5Cend%7Barray%7D%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  \begin{array}{|c|c|c|c|c|}  \hline \text{Iter.} &amp; l &amp; r &amp; c &amp; p_c \\  \hline 1 &amp; 0 &amp; 3 &amp; 1 &amp; \text{jaume} \\  \hline 2 &amp; 2 &amp; 3 &amp; 2 &amp; \text{javi} \\  \hline 3 &amp; 3 &amp; 3 &amp; 3 &amp; \text{toni} \\  \hline  \end{array}  ' title='  \begin{array}{|c|c|c|c|c|}  \hline \text{Iter.} &amp; l &amp; r &amp; c &amp; p_c \\  \hline 1 &amp; 0 &amp; 3 &amp; 1 &amp; \text{jaume} \\  \hline 2 &amp; 2 &amp; 3 &amp; 2 &amp; \text{javi} \\  \hline 3 &amp; 3 &amp; 3 &amp; 3 &amp; \text{toni} \\  \hline  \end{array}  ' class='latex' /></p>
<p>Llavors, les correccions proposades serien <em>Javi</em> i <em>Toni</em> i cap dels dos és el que l&#8217;usuari buscava.</p>
<p>Una possible millora és no sols considerar els dos noms al voltant del cercat, sinó considerar <em>k</em> noms més pròxims en ordre lexicogràfic (per exemple, amb <em>k</em> igual a 3, l&#8217;algorisme consideraria <em>Jaume</em>, <em>Javi</em> i <em>Toni</em>). Però aquesta solució no funciona massa bé quan el nombre d&#8217;elements en el vector és de més de dos milions i l&#8217;usuari s&#8217;equivoca en una de les primeres lletres del nom.</p>
<p>No obstant això, l&#8217;algorisme és molt eficient en quant a temps i memòria i sobre tot, és molt senzill. Suposant que tots els noms tenen aproximadament la mateixa longitud <em>l</em>, podem expressar la complexitat espacial i temporal (de cerca) en funció del nombre de persones <em>n</em> i aquesta longitud <em>l</em> (la suposició de la longitud <em>l</em> és sols per a simplificar les expressions de la complexitat).</p>
<p style="text-align: center;"><img src='http://s.wordpress.com/latex.php?latex=%20%20S%28n%2C%20l%29%20%5Cin%20O%20%5Cleft%28%20l%20%5Ccdot%20n%20%5Cright%29%20%5C%5C%20%20T%28n%2C%20l%29%20%5Cin%20O%20%5Cleft%28%20l%20%5Ccdot%20%5Clog_2%20n%20%5Cright%29%20%20&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='  S(n, l) \in O \left( l \cdot n \right) \\  T(n, l) \in O \left( l \cdot \log_2 n \right)  ' title='  S(n, l) \in O \left( l \cdot n \right) \\  T(n, l) \in O \left( l \cdot \log_2 n \right)  ' class='latex' /></p>
<p>La primera expressió es deriva del fet que tenim <em>n</em> noms i cada nom té una longitud de <em>l</em> caràcters. La segona expressió es deriva del fet que la cerca dicotòmica fa <img src='http://s.wordpress.com/latex.php?latex=O%28%5Clog_2%20n%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(\log_2 n)' title='O(\log_2 n)' class='latex' /> comparacions de cadenes, i per comparar dues cadenes de longitud <em>l</em> necessitem fins a <em>l</em> comparacions de caràcters.</p>
<p>Aquesta senzilla idea fou reemplaçada per una que donava millors resultats cara a l&#8217;usuari, però que té un fort inconvenient: l&#8217;ús de memòria. L&#8217;alternativa que presentaré en la pròxima entrada, és la que està implementada ara mateixa en l&#8217;Oracle.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elvoldelhomeocell.net/archives/1515/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Sudoku</title>
		<link>http://www.elvoldelhomeocell.net/archives/666</link>
		<comments>http://www.elvoldelhomeocell.net/archives/666#comments</comments>
		<pubDate>Sat, 02 May 2009 19:33:15 +0000</pubDate>
		<dc:creator>Joan</dc:creator>
				<category><![CDATA[Curiositats]]></category>
		<category><![CDATA[Informàtica]]></category>
		<category><![CDATA[Matemàtiques]]></category>
		<category><![CDATA[Programació]]></category>
		<category><![CDATA[algorismes]]></category>
		<category><![CDATA[backtraking]]></category>
		<category><![CDATA[combinatoria]]></category>
		<category><![CDATA[complexitat computacional]]></category>
		<category><![CDATA[eda]]></category>
		<category><![CDATA[fiv]]></category>
		<category><![CDATA[friki]]></category>
		<category><![CDATA[japó]]></category>
		<category><![CDATA[miyagi]]></category>
		<category><![CDATA[np-complet]]></category>
		<category><![CDATA[sudoku]]></category>
		<category><![CDATA[upv]]></category>

		<guid isPermaLink="false">http://www.elvoldelhomeocell.net/?p=666</guid>
		<description><![CDATA[Jo pensava que el sudoku era un d&#8217;aquests jocs mil·lenaris japonesos que havia exercitat la ment de monjos i Shoguns per a poder arribar a tota classe de revelacions pròpies del senyor Miyagi i que el deixen a un bocabadat &#8230; <a href="http://www.elvoldelhomeocell.net/archives/666">Continua llegint <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Jo pensava que el <em><a href="http://en.wikipedia.org/wiki/Sudoku" target="_blank">sudoku</a></em> era un d&#8217;aquests jocs mil·lenaris japonesos que havia exercitat la ment de monjos i <a href="http://es.wikipedia.org/wiki/Sh%C5%8Dgun" target="_blank">Shoguns</a> per a poder arribar a tota classe de revelacions pròpies del <a href="http://en.wikipedia.org/wiki/Mr._Miyagi" target="_blank">senyor Miyagi</a> i que el deixen a un bocabadat durant una bona estona.</p>
<p>Però resulta que de joc mil·lenari i japonès en té ben poc, ja que va ser l&#8217;arquitecte nord-americà <a href="http://en.wikipedia.org/wiki/Howard_Garns" target="_blank">Howard Garns</a>, qui l&#8217;inventà&#8230; en 1979! Quina decepció.</p>
<p>Els <em>sudokus</em> es feren populars a Japó en la dècada dels vuitanta quan una companyia de <em>puzzles</em> comença a comercialitzar-los en el país de l&#8217;orient, però s&#8217;estengueren mundialment l&#8217;any 2004 quan el periòdic britànic <a href="http://www.timesonline.co.uk/tol/news/" target="_blank"><em>The Times</em></a> començà a incloure&#8217;ls entre les seves pàgines junt a sopes de lletres, encreuats i demés passatemps.</p>
<p><em><strong>El joc</strong></em></p>
<p>El joc consisteix <em>senzillament</em> en omplir un tauler de costats 9&#215;9 amb nombres de l&#8217;1 al 9 de manera que no hi haja dos nombre iguals en cap fila, ni cap columna, ni cap <em>bloc</em> de 3&#215;3. Els taulers de <em>sudoku</em> tenen la següent forma.</p>
<div id="attachment_681" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.elvoldelhomeocell.net/wp-content/uploads/2009/05/sudoku_sol.png" rel="lightbox[666]"><img class="size-medium wp-image-681" title="Sudoku" src="http://www.elvoldelhomeocell.net/wp-content/uploads/2009/05/sudoku-300x300.png" alt="Sudoku" width="300" height="300" /></a><p class="wp-caption-text">Un exemple de Sudoku (punxa per veure la solució)</p></div>
<p>Depenent del nombre de quadres plens que ens donen i la posició dels nombres amb els que comencem el joc resulta més difícil o menys.</p>
<p><em><strong>El principi</strong></em></p>
<p>Jo fins fa dos anys no havia jugat en ma vida al joc aquest. N&#8217;havia vist, sí, però no havia jugat mai. Fou a juny de 2007, de camí a València per comprar els bitllets del (fallit) <a href="http://www.interrailnet.com/" target="_blank">Interrail</a> quan vaig jugar per primera vegada amb <a href="http://llumdelluerna.blogspot.com/" target="_blank">Marina</a> i <a href="http://www.blaiserver.net/" target="_blank">Blai</a>. Marina i jo ens vàrem <em>picar</em> a veure qui podia completar-ne més ràpidament així que em vaig comprar un llibre d&#8217;aquests que estan tot plens de <em>sudokus</em> per anar fent-lo durant el viatge, però circumstàncies de la vida que ja coneixeu feren que el llibre l&#8217;emplenés en l&#8217;hospital.</p>
<p>I a què ve tot açò? Doncs l&#8217;altre dia estàvem en classe d&#8217;<a href="http://users.dsic.upv.es/asignaturas/facultad/eda/" target="_blank">Estructures de Dades i Algorismes</a> (<em>a.k.a</em> EDA) aprenent sobre <a href="http://en.wikipedia.org/wiki/Backtracking" target="_blank"><em>Backtracking</em></a> i sorgí l&#8217;exemple del <em>sudoku</em>, així que el professor va fer un programa que resolia <em>sudokus</em> fent servir aquest esquema algorísmic. Quan va acabar el programa va dir: &#8220;Podríem fer que ens imprimira tots els <em>sudokus</em> possibles&#8230;&#8221; i va llançar el programa per a que fera això, però com que ja era hora d&#8217;acabar la classe ací va quedar la cosa.</p>
<p>Jo, ahir que no sabia que fer (ara que ja s&#8217;ha acabat el concurs de l&#8217;assignatura) i com que no havia practicat encara res de <em>Backtraking</em> per compte propi em vaig dir: &#8220;Xe, anem a fer el <em>programeta</em> per resoldre <em>sudokus</em>&#8220;. I em vaig posar en això. Quan vaig acabar-lo em vaig preguntar per què no acabar la tasca que el professor havia començat però havia abandonat per falta de temps i vaig llançar <a href="http://trac.assembla.com/fivworks/browser/eda/codi/backtracking/sudoku.cpp" target="_blank">el meu programa</a> per a que em mostrara tots els <em>sudokus</em> que es podien formar en un tauler de 9&#215;9.</p>
<p>Ignorant de mi. Com veia que tardava me&#8217;n vaig anar a berenar esperant que quan tornes tindria ja tots els <em>sudokus</em> possibles. Però com ja imaginareu, quan vaig tornar el portàtil estava a 74ºC  (i això que havia limitat la freqüència a 996MHz) i el fitxer de solucions ja ocupava centenars de MB. Va ser ací quan em vaig adonar que segurament serien MOLTS <em>sudokus</em>&#8230;</p>
<p>Concretament, hi han <strong>6,670,903,752,021,072,936,960 taulers de <em>sudoku</em> possibles</strong> (o deixe expressat així i no en notació científica per a que s&#8217;aprecie millor la magnitud d&#8217;aquest nombre). I ací és on comença el <em>friquisme</em>.</p>
<p><em><strong>Parlant matemàticament&#8230;</strong></em></p>
<p>Hi ha un extens <a href="http://en.wikipedia.org/wiki/Mathematics_of_Sudoku" target="_blank">article en la Wikipedia anglesa</a> dedicat a les matemàtiques relacionades amb aquest joc, la seva <a href="http://en.wikipedia.org/wiki/Mathematics_of_Sudoku#References" target="_blank">llista de referències</a> no es queda curta i són moltes les investigacions sobre <a href="http://en.wikipedia.org/wiki/Combinatorics" target="_blank">Combinatòria</a> i també <a href="http://en.wikipedia.org/wiki/Computational_complexity_theory" target="_blank">Complexitat Computacional</a> que es centren en aquest joc.</p>
<p>El problema general del <em>sudoku</em> entès com un tauler de <img src='http://s.wordpress.com/latex.php?latex=N%5E2%20%5Ctimes%20N%5E2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='N^2 \times N^2' title='N^2 \times N^2' class='latex' /> caselles agrupades en blocs de <img src='http://s.wordpress.com/latex.php?latex=N%20%5Ctimes%20N&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='N \times N' title='N \times N' class='latex' /> (9&#215;9 caselles agrupades en blocs de 3&#215;3, en el nostre cas), es pot expressar com un problema de colorejat d&#8217;un graf, on cada casella seria un vèrtex del graf enumerat de la forma <img src='http://s.wordpress.com/latex.php?latex=%28x%2Cy%29&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='(x,y)' title='(x,y)' class='latex' /> (on <img src='http://s.wordpress.com/latex.php?latex=x%2C%20y%20%5Cin%20%5Cmathbb%7BN%7D%20%5Cmid%20x%20%5Cle%20N%5E2%20%5Cwedge%20y%20%5Cle%20N%5E2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x, y \in \mathbb{N} \mid x \le N^2 \wedge y \le N^2' title='x, y \in \mathbb{N} \mid x \le N^2 \wedge y \le N^2' class='latex' />)¹ i on dos vèrtex <img src='http://s.wordpress.com/latex.php?latex=i%2C%20j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='i, j' title='i, j' class='latex' /> estarien units per una aresta si:</p>
<ul>
<li><img src='http://s.wordpress.com/latex.php?latex=x_i%20%3D%20x_j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='x_i = x_j' title='x_i = x_j' class='latex' /> (dues caselles d&#8217;una mateixa columna).</li>
<li><img src='http://s.wordpress.com/latex.php?latex=y_i%20%3D%20y_j&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='y_i = y_j' title='y_i = y_j' class='latex' /> (dues caselles d&#8217;una mateixa fila).</li>
<li><img src='http://s.wordpress.com/latex.php?latex=%5Clceil%20%5Cfrac%7Bx_i%7D%7BN%7D%20%5Crceil%20%3D%20%5Clceil%20%5Cfrac%7Bx_j%7D%7BN%7D%20%5Crceil%20%5Cwedge%20%5Clceil%20%5Cfrac%7By_i%7D%7BN%7D%20%5Crceil%20%3D%20%5Clceil%20%5Cfrac%7By_j%7D%7BN%7D%5Crceil&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='\lceil \frac{x_i}{N} \rceil = \lceil \frac{x_j}{N} \rceil \wedge \lceil \frac{y_i}{N} \rceil = \lceil \frac{y_j}{N}\rceil' title='\lceil \frac{x_i}{N} \rceil = \lceil \frac{x_j}{N} \rceil \wedge \lceil \frac{y_i}{N} \rceil = \lceil \frac{y_j}{N}\rceil' class='latex' /> (dues caselles en un mateix bloc).</li>
</ul>
<p>Llavors, la solució seria assignar un color entre els <img src='http://s.wordpress.com/latex.php?latex=N%5E2&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='N^2' title='N^2' class='latex' /> possibles a cada vèrtex (un nombre de l&#8217;1 al 9 vaja) de manera que qualsevol aresta no tinga el mateix color en el seus dos extrems.</p>
<p>Doncs veges tu per on, aquest problema <strong>és un problema del tipus <a href="http://en.wikipedia.org/wiki/NP-complete" target="_blank">NP-Complet</a></strong>. Jo havia sentit parlar d&#8217;aquesta <a href="http://en.wikipedia.org/wiki/Complexity_class" target="_blank">classe de problemes</a> (per tot allò de <em><a href="http://en.wikipedia.org/wiki/P_%3D_NP_problem" target="_blank">P = NP</a>?</em>, un dels set problemes del mil·leni, la solució dels quals és premiada amb un milió de dòlars), però fins aquesta vesprada no sabia concretament que era un problema NP-Complet i tampoc estic segur de saber-ho ara, així que abans de contar-vos alguna bajanada us referencie a l&#8217;<a href="http://en.wikipedia.org/wiki/NP-complete" target="_blank">article de la Wikipedia</a>.</p>
<p>El que sí us sé dir és que, en poques a paraules, no es coneix un algorisme &#8220;eficient&#8221;, on eficient significa polinòmic (<img src='http://s.wordpress.com/latex.php?latex=O%28n%5Ek%29%20%5Cmid%20k%20%5Cin%20%5Cmathbb%7BN%7D&#038;bg=ffffff&#038;fg=000000&#038;s=0' alt='O(n^k) \mid k \in \mathbb{N}' title='O(n^k) \mid k \in \mathbb{N}' class='latex' />) per solucionar el problema. El que fa l&#8217;algorisme basat en <em>Backtracking</em> és explorar tot l&#8217;arbre de possibles solucions descartant aquelles que no complisquen les condicions del joc (descrites anteriorment).</p>
<p>Haurem de renunciar doncs, de moment, a trobar tots els possibles <em>sudokus</em>&#8230; Podríem seguir parlant de com, almenys, comptar quantes són aquestes solucions, però per això necessitaríem un apunt encara més llar i ja s&#8217;està fent tard i tampoc era eixe l&#8217;objectiu del post. Si us he despertat un poc l&#8217;interès i us ha picat el <em>cuquet friqui</em>, podeu visitar <a href="http://www.afjarvis.staff.shef.ac.uk/sudoku/" target="_blank">aquesta pàgina web</a> de Bertram Felgenhauer i Frazer Jarvis que dugueren endavant els càlculs.</p>
<p>&#8212;</p>
<p>¹ Pregue que disculpeu el formalisme d&#8217;algunes expressions, però aprofite l&#8217;apunt per provar que dóna de sí el plugin WP-Latex.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.elvoldelhomeocell.net/archives/666/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>

