<?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>Felipe Ribeiro &#187; algoritmo</title>
	<atom:link href="http://blog.feliperibeiro.com/tag/algoritmo/feed" rel="self" type="application/rss+xml" />
	<link>http://blog.feliperibeiro.com</link>
	<description>My hacks, rants and random thoughts</description>
	<lastBuildDate>Wed, 16 Nov 2011 17:39:15 +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>Algoritmo de &#8220;Você quis dizer&#8221;</title>
		<link>http://blog.feliperibeiro.com/2008/10/algoritmo-de-voce-quis-dizer.html</link>
		<comments>http://blog.feliperibeiro.com/2008/10/algoritmo-de-voce-quis-dizer.html#comments</comments>
		<pubDate>Wed, 08 Oct 2008 17:09:44 +0000</pubDate>
		<dc:creator>Felipe Ribeiro</dc:creator>
				<category><![CDATA[apache]]></category>
		<category><![CDATA[desenvolvimento de software]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[web 2.0]]></category>
		<category><![CDATA[algoritmo]]></category>
		<category><![CDATA[busca]]></category>

		<guid isPermaLink="false">http://blog.feliperibeiro.com/?p=126</guid>
		<description><![CDATA[Recentemente precisei implementar um sistema de correção ortográfica à la Google. Pesquisei bastante sobre como fazer isso, e encontrei duas maneiras que explicarei agora, mas antes disso explicarei algo que é comum às duas: A criação do dicionário Para criar o dicionário, você precisa ter uma base de palavras, que idealmente é um arquivo txt. [...]]]></description>
			<content:encoded><![CDATA[<p>Recentemente precisei implementar um sistema de correção ortográfica à la Google.</p>
<p>Pesquisei bastante sobre como fazer isso, e encontrei duas maneiras que explicarei agora, mas antes disso explicarei algo que é comum às duas: <strong>A criação do dicionário</strong></p>
<p>Para criar o dicionário, você precisa ter uma base de palavras, que idealmente é um arquivo txt.</p>
<p>Você lê o arquivo e cria um array onde as palavras são as chaves e as frequências com que aparecem nos textos serão os valores.</p>
<pre name='code' class='php'>
&lt;?php
$text = file_get_contents('arquivo.txt');
preg_match_all("/[a-z]+/",strtolower($text),$matches);
$palavras = $matches[0];
$dicionario = array();
foreach($palavras as $palavra)
	$dicionario[$palavra] += 1;
sort($dicionario); /* Essa ordenação será útil para otimização de performance
                               no primeiro algoritmo */
file_put_contents('dicionario_serializado.dat',serialize($dicionario));
?&gt;
</pre>
<p>1 &#8211; Algoritmo de Levenshtein</p>
<p>PHP implementa nativamente o algoritmo de Levenshtein (<a href="http://php.net/levenshtein">http://php.net/levenshtein</a>), que calcula a distância de edição entre duas palavras, funcionando da seguinte forma:</p>
<ul>
<li>suponha que eu tenha duas palavras: água e mágua &#8211;  A distância de edição delas é de uma inserção de carácter</li>
<li>suponha que eu tenha as palavras: casa e caixa &#8211; A distância delas é de uma substituição (s por i) e uma inserção (x)</li>
<li>suponha que eu tenha as palavras: arrocho e arroto &#8211; A distância delas é de uma substituição e uma remoção.</li>
</ul>
<p>A função levenshtein também permite que você dê pesos diferenciados para inserção/remoção/substituição, sendo que o valor default é 1 para todas operações. Usando essa função, fica simples implementar um corretor, supondo que você tem um dicionário que já citei, você só precisa fazer:</p>
<pre name='code' class='php'>
&lt;?php
function voce_quis_dizer($palavra_procurada) {
	$dicionario = unserialize(file_get_contents(‘dicionario_serializado.dat’));
	$minima_distancia = -1;
	$palavra_procurada = strtolower($palavra_procurada);
	foreach($dicionario as $palavra_do_dicionario) {
		if($palavra_procurada == $palavra_do_dicionario) return $palavra;
		$distancia = levenshtein($palavra_procurada,$palavra_do_dicionario);
		if($distancia < $minima_distancia || $minima_distancia == -1) {
			$minima_distancia = $distancia;
			$sugestao = $palavra_do_dicionario;
		}
	}
return $sugestao;
}
?&gt;
</pre>
<p>Análise de desempenho:</p>
<p>Considerando que temos <strong>k</strong> palavras no dicionário, a palavra que desejamos procurar tem <strong>m</strong> letras e cada palavra do dicionário tem em média <strong>n</strong> letras, o algoritmo de Levenshtein tem ordem de complexidade <strong>O(m*n)</strong> onde para <strong>k</strong> comparações (no caso da palavra <strong>não</strong> existir no dicionário, já que se ela existir não teremos nenhuma comparação) teremos <strong>O(k*m*n)</strong> (<strong>k*m*n</strong> operações onde <strong>k</strong> é muito maior que <strong>m</strong> e <strong>n</strong>).</p>
<p>2 - Algoritmo de Peter Norvig</p>
<p>Toda explicação probabilística desse algorítmo está presente no site do Norvig, um cara renomado na área de inteligência artificial: <a href="http://norvig.com/spell-correct.html">http://norvig.com/spell-correct.html</a>. Mas basicamente esse algoritmo apesar de mais complicado é bem mais inteligente que o primeiro. O que ele faz é o seguinte: gera perturbações na sua palavra procurada e vê quais dessas perturbações é a mais relevante no dicionário. Evitando comparações desnecessárias, já que no primeiro algoritmo cada palavra que você colocar será comparada com todas as outras (por exemplo: se você digitar BOLA, e ela não existir no dicionário ela será comparada com palavras absurdas como: LIVRO, que obviamente não é o que você quis dizer).</p>
<p>Apesar do conceito do Norvig ser simples, o código é um pouquinho complicado. Então eu criei uma classe que implementa ele e disponibilizei sob Licença BSD no PHPClasses.org (<a href="http://www.phpclasses.org/browse/file/24605.html">http://www.phpclasses.org/browse/file/24605.html</a>) e tenho recebido elogios e feedback muito positivo de quem está usando.</p>
<p>Análise de desempenho:</p>
<p>Considerando <strong>26</strong> letras do alfabeto (depois da reforma da língua portuguesa, agora também temos 26 letras! <img src='http://blog.feliperibeiro.com/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' /> ) e considerando que sua palavra tem <strong>m</strong> letras, teremos mais ou menos 26*m iterações para gerar as possíveis perturbações na palavra (inserções, remoções e substituições possíveis), e para garantir um segundo nível de perturbações teremos aproximadamente <strong>26*m</strong> para cada perturbação gerada, que dá um total de aproximadamente (26*m)*(26*m) = 676 m². Agora só precisamos consultar cada uma das perturbações para vermos qual a mais relevante no dicionário. Cada consulta no dicionário é <strong>O(1)</strong> então desprezaremos na análise de performance.</p>
<p>E podemos considerar esse algoritmo como sendo mais eficiente do que o primeiro pois o tempo de execução cresce de acordo com o quadrado do tamanho da palavra (que normalmente é bem menor do que o tamanho do dicionário). Enquanto o primeiro sofre interferência do tamanho do dicionário e do tamanho médio das palavras contidas lá, então esse é bem mais inteligente e eficiente.</p>
<p>Espero ter ajudado!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.feliperibeiro.com/2008/10/algoritmo-de-voce-quis-dizer.html/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
	</channel>
</rss>

