<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>Assemblando</title>
	<atom:link href="http://assemblando.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://assemblando.wordpress.com</link>
	<description>Blog pessoal sobre programaçã, matemáttica e engenharia</description>
	<lastBuildDate>Mon, 21 Nov 2011 13:09:05 +0000</lastBuildDate>
	<language>pt-br</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='assemblando.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>Assemblando</title>
		<link>http://assemblando.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://assemblando.wordpress.com/osd.xml" title="Assemblando" />
	<atom:link rel='hub' href='http://assemblando.wordpress.com/?pushpress=hub'/>
		<item>
		<title>Bit Expressions com Erlang &#8211; P1</title>
		<link>http://assemblando.wordpress.com/2011/07/19/bit-expressions-com-erlang-p1/</link>
		<comments>http://assemblando.wordpress.com/2011/07/19/bit-expressions-com-erlang-p1/#comments</comments>
		<pubDate>Tue, 19 Jul 2011 14:44:09 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=165</guid>
		<description><![CDATA[Aos poucos venho estudando uma nova linguagem que vem me encantando: Erlang. Ainda estou aprendendo o seu básico, mas já consigo enxergar algumas funcionalidades interessantes, apesar de ainda estar me acostumando com o paradigma funcional. Erlang proporciona um mecanismo interessante de representação em formato binário, o que se justifica se lembrarmos que a linguagem foi [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=165&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Aos poucos venho estudando uma nova linguagem que vem me encantando: Erlang. Ainda estou aprendendo o seu básico, mas já consigo enxergar algumas funcionalidades interessantes, apesar de ainda estar me acostumando com o paradigma funcional.</p>
<p>Erlang proporciona um mecanismo interessante de representação em formato binário, o que se justifica se lembrarmos que a linguagem foi desenvolvida para se trabalhar com telecomunicações (a linguagem foi desenvolvida pela Ericsson. Nas fontes que consultei existem divergências sobre a origem do nome Erlang. Há alguns lugares que dizem ser a contração de ERicsson LANGuage. Outras fontes afirmam ser uma referência ao matemático Agner Krarup Erlang).</p>
<p>Esse mecanismo de representação binária facilitaria a transmissão e manipuação, em nível de bit, dos dados. Por Exemplo, para se representar uma sequência de bytes em Erlang, envolvemos a em &lt;&lt; &#8230; &gt;&gt;:</p>
<p>&lt;&lt;1, 77, 32, 88 &gt;&gt;.</p>
<p>Isso representa justamente quatro bytes, contendo os valores decimais: 1, 77, 32 e 88. Se quisermos entrar com os bytes em, por exemplo, hexadecimal, devemos explicitamente indicar a base:</p>
<p>&lt;&lt; 16#1, 16#4d, 16#20, 16#58 &gt;&gt;.</p>
<p>Okay, o leitor diria, até agora não vi nada de diferente em relação à uma lista. Por exemplo, eu poderia simplesmente fazer</p>
<p>[ 16#1, 16#4d, 16#20, 16#58 ].</p>
<p>e tudo daria no mesmo.</p>
<p>A primeira diferença em se usar Binary Sintax em Erlang é que este trunca os valores em 255 (ou 8 bits, FFh, como você preferir enxergar), ou seja, qualquer valor que for colocado acima de 255 ocasionará um overflow, justamente como um sistema difgital puro se comporta. Por exemplo:</p>
<p>&lt;&lt; 257 &gt;&gt;.</p>
<p>retorna:</p>
<p>&lt;&lt;1&gt;&gt;</p>
<p>Isso faz muito sentido, pois estamos trabalhando com bytes diretamente.</p>
<p>Bem, essa foi só uma rápida introdução para que possamos chegar onde eu desejo, que é a notação Binary Sintax do Erlang. Essa notação nos permite trabalhar com conjunto de dados estipulando arbitrariamente o tamanho de cada elemento em bits (isso mesmo, bits, não bytes). Imagine por exemplo que eu quero salvar um conjunto de dados que tem três elementos, cada qual pode ocupar uma faixa bem definida de valores. Com base nos valores máximo eu posso estipular a quantidade específica de bits a serem utilizados em sua representação.</p>
<p>Vamos ver isso de uma maneira mais prática:</p>
<p>Proposta &#8211; Representar em uma stream de bits o valor de um pixel que contém as três cores (Red, Green e Blue) em níveis que varias:<br />
0~31 para R<br />
0~63 para G<br />
0~31 para B</p>
<p>Aqui usamos um bit a mais para representar a cor verde, pois o olho humano tem uma maior sensibilidade à essa cor. Perceba que estamos falando em quantidade de bits necessários para representar cada cor, e não quantidade de bytes. Se pretendêssemos usar, por exemplo, uma struct C para representar esse conjunto fariamos mais ou menos assim:</p>
<p>struct RGB{<br />
char r;<br />
char g;<br />
char b;<br />
};</p>
<p>Assim eu aloquei 8 bits para cada cor. O que é completamente desnecessário, pois a se olharmos para a faixa que vada valor deve representar, e cpontarmos quantos bits realmente são necessários, teremos:</p>
<p><strong>Cor     Faixa     Bits</strong><br />
R         0~31         5<br />
G         0~63         6<br />
B         0~31         5<br />
<strong>Total                  16</strong></p>
<p>Notamos aqui que não são necessários mais que 16 bits para representar as cores RGB no nosso sistema, porém estamos trabalhando com uma struct de três chars, o que consome 24 bits. Uma alternativa é você representar as três cores em um array de char de tamanho 2, porém caberia a você toda a manipulação dos bits, o que daria um pouco de trabalho (mas nada que um programador competente não resolva em uma tarde).</p>
<p>A grande sacada de se usar Bit Syntax em Erlang é que você pode especificar quantos bits cada elemento deve ocupar, e quando você passa um valor que não cabe naquele elemento, ele se comporta da mesma maneira descrita anteriormente: ocorre-se um overflow. Vamos, utilizando bit syntax, passar os valores de RGB e recupera-los na forma de um stream de 16 bits:</p>
<p>R = 15.<br />
G = 52.<br />
B = 19.<br />
&lt;&lt; R:5, G:6, B:5 &gt;&gt;.</p>
<p>note que a saída foi &lt;&lt;126,147&gt;&gt;, um bit stream de exatamente 16 bits. Podemos agora encapsular isso, e cuidar para que ninguém coloque valores fora da faixa especificada:</p>
<p><pre class="brush: python;">
-module(rgb).
-export([marshall/3, unmarshall/1]).

marshall(R,G,B) -&gt;
  if
    R &gt; 31 -&gt;
      throw({overlimit, R});
    G &gt; 63 -&gt;
      throw({overlimit, G});
    B &gt; 31 -&gt;
      throw({overlimit, B});
    true -&gt; true
  end,
  &lt;&lt; R:5, G:6, B:5 &gt;&gt;.

unmarshall(Binary) -&gt;
&lt;&lt; R:5, G:6, B:5 &gt;&gt; = Binary,
  {R,G,B}.
</pre></p>
<p>Salvando esse código em um arquivo de mesmo nome de seu módulo (rgb.erl), vamos entrar no console Erlang e compila-lo:</p>
<p>&gt; c(rgb).<br />
{ok,rgb}</p>
<p>Pronto, agora estamos aptos a usar tanto as funções marshall e unmarshall que empacotam nossos valores RGB em stream de bits e desempacotam esse stream, devolvendo uma tupla com esses valores.</p>
<p>Exemplo:</p>
<p>rgb:unmarshall(rgb:marshall(8,19,30)).</p>
<p>Esse é um exemplo simplório da utilização de bit syntax em erlang, mas já dá pra ter uma pequena noção do seu poder.A idéia é explorar um pouco mais com coisas mais complexas, que provavelmente nos daria muito trabalho implementando em linguagens como C.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/165/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/165/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/165/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=165&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2011/07/19/bit-expressions-com-erlang-p1/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>
	</item>
		<item>
		<title>Pela união dos seus poderes&#8230;</title>
		<link>http://assemblando.wordpress.com/2011/05/14/pela-uniao-dos-seus-poderes/</link>
		<comments>http://assemblando.wordpress.com/2011/05/14/pela-uniao-dos-seus-poderes/#comments</comments>
		<pubDate>Sat, 14 May 2011 23:46:02 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Complexidade Algorítmica]]></category>
		<category><![CDATA[Matemática]]></category>
		<category><![CDATA[Programação]]></category>
		<category><![CDATA[No]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=131</guid>
		<description><![CDATA[Em um post anterior sobre complexidade algorítmica eu utilizei de diferentes implementações de algorítmos que executavam a mesma tarefa: encontrar um subset de números primos dentro de um conjunto maior de elementos. Na ocasião eu utilizei as implementações para exercitar um pouco de cálculo de complexidade, e o nosso foco era comprovar na prática o [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=131&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Em um post anterior sobre complexidade algorítmica eu utilizei de diferentes implementações de algorítmos que executavam a mesma tarefa: encontrar um <em>subset </em>de números primos dentro de um conjunto maior de elementos. Na ocasião eu utilizei as implementações para exercitar um pouco de cálculo de complexidade, e o nosso foco era comprovar na prática o que a teoria (o cálculo de complexidade algorítmica) nos revelava.</p>
<p>Desta vez vamos utilizar uma aproximação silmilar, utilizando algorítmos diferentes que realizam o mesmo propósito porém com eficiências diferentes. Porém a nossa meta neste caso é demonstrar como podemos unir idéias que a princípio parecem desconexas para um mesmo fim, melhorando implementações e nos dando um ganho considerável de tempo.</p>
<h2>Formalizando o problema</h2>
<p>Uma problema que todo mundo implemente ainda nos primeiros meses de aprendizado de programação é a implementação dos <a title="Fibonacci Number" href="http://en.wikipedia.org/wiki/Fibonacci_number" target="_blank">Números de Fibonacci</a>. A definição formal da sequência fibonacciana é a seguinte:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=Fib(n)=\left\{\begin{matrix} 0, &amp; n=0 \\ 1, &amp; n=1 \\ Fib(n-1) @plus; Fib(n-2), &amp; n \geq 2 \end{matrix}\right." target="_blank"><img title="Fib(n)=\left\{\begin{matrix} 0, &amp; n=0 \\ 1, &amp; n=1 \\ Fib(n-1) + Fib(n-2), &amp; n \geq 2 \end{matrix}\right." src="http://latex.codecogs.com/gif.latex?Fib(n)=\left\{\begin{matrix} 0, &amp; n=0 \\ 1, &amp; n=1 \\ Fib(n-1) + Fib(n-2), &amp; n \geq 2 \end{matrix}\right." alt="" /></a></p>
<p>Assim, a série de Fibonacci começa com os dois elementos iguais a 1, posteriormente temos:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=fib_{2}=2, fib_{3}=3, fib_{4}=5, fib_{5}=8, ... , fib_{n}=fib_{n-1}@plus;fib_{n-2}" target="_blank"><img title="fib_{2}=2, fib_{3}=3, fib_{4}=5, fib_{5}=8, ... , fib_{n}=fib_{n-1}+fib_{n-2}" src="http://latex.codecogs.com/gif.latex?fib_{2}=2, fib_{3}=3, fib_{4}=5, fib_{5}=8, ... , fib_{n}=fib_{n-1}+fib_{n-2}" alt="" /></a></p>
<p>Um dos motivos que os professores gostam de mostrar a sequência de Fibonacci no início do aprendizado de programação dos alunos é que a esta sequência, por ser naturalmente recursiva, é facilmente implementável e demonstra ao aluno a idéia básica de recursão. Geralmente a implementação demonstrada pelos professores é similar à seguinte:</p>
<p><pre class="brush: python;">
def fibo(n):
  if n==0 or n==1:
    return n
  else:
    return fibo(n-1)+fibo(n-2)
</pre></p>
<p><span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;">Este código é bem simples, e a recusão está bem fácil de se identificar. Porém um dos motivos que eu não gosto deste algoritmo (principalmente para mostrar para programadores que estão começando a aprender) é que este método é extremamente ineficiente. Para exemplificar, na minha máquina (um MacBook) esse algoritmo gastou pouco mais de 1:30 minutos para encontrar o valor do quadragésimo número da sequência, retornando fibo(40)=102334155. O valor está correto, mas o tempo gasto para o seu cálculo apenas reflete a fraquesa de implementação em se tratando de eficiência. Uma implementação iterativa como a seguinte:</span></p>
<p><pre class="brush: python;">
def fibo2(n):
	if n==0 or n==1:
		return n
	n -= 1
	previous=1
	current=1
	while n&gt;1:
		previous,current = current, current+previous
		n -= 1
	return current
</pre></p>
<p><span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;"><span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;"> <span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;">resolve o mesmo problema proposto para a mesma entrada (n=40) em cerca de 110 ms (no mesmo computador). Vamos rapidamente analizar os dois códigos e tentar entender o porque desta diferença. Se olharmos para a recursão podemos claramente retirarmos a forma básica da expressão de tempo para n=1 ou n=0. Para ambos a a resposta é direta, o tempo gasto sempre será constante, portanto T(1)=t(0)=O(1).</span></span></span></p>
<p><span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;"><span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;">Porém se a entrada for maior ou igual a 2, o custo computacional pode ser representado pela soma dos tempos necessários para o cálculo de f(n-1) e f(n-2). Assim fechamos nossa fórmula recursiva para o primeiro caso em:<br />
<span class="Apple-style-span" style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;font-size:13px;line-height:19px;white-space:normal;"><br />
</span></span></span></p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(1) = O(1)" target="_blank"><img title="T(1) = O(1)" src="http://latex.codecogs.com/gif.latex?T(1) = O(1)" alt="" /></a></p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(n) = T(n-1) @plus; T(n-2)" target="_blank"><img title="T(n) = T(n-1) + T(n-2)" src="http://latex.codecogs.com/gif.latex?T(n) = T(n-1) + T(n-2)" alt="" /></a></p>
<p>Se resolvemos a recursão pela método da árvore de recursão, abrindo a recursão na forma de uma árvore com dois ramos (que são as duas chamadas recursivas), contabilizamos que a quantidade de passos a se executar é proporcional ao resultado de f(n). Em palavras mais simples, a quantidade de passos para se calcular cresce na sequência fibonacciana. Logo:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(n)=O(Fibo(n))" target="_blank"><img title="T(n)=O(Fibo(n))" src="http://latex.codecogs.com/gif.latex?T(n)=O(Fibo(n))" alt="" /></a></p>
<p>que, assintoticamente, cresce exponencialmente (a solução desta recursão passo a passo não convém neste post, mas pode ser abordado em um outro momento).</p>
<p>Por fim temos que:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(n) = O(\varphi^n)" target="_blank"><img title="T(n) = O(\varphi^n)" src="http://latex.codecogs.com/gif.latex?T(n) = O(\varphi^n)" alt="" /></a></p>
<p>Já no segundo caso a análise é muito mais simples. O nosso algoritmo tem um único <em>loop</em> que corre <em>n</em> unidades, assim temos:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(n) = O(n)" target="_blank"><img title="T(n) = O(n)" src="http://latex.codecogs.com/gif.latex?T(n) = O(n)" alt="" /></a></p>
<p>o que significa que a segunda implementação tem comportamento linear. Isso claramente nos responde o porque a segunda implementação (iterativa) é tão mais eficiente que a primeira (recursiva), pois:</p>
<p><a href="http://assemblando.files.wordpress.com/2011/05/codecogseqn-1.gif"><img class="alignnone size-full wp-image-150" title="CodeCogsEqn (1)" src="http://assemblando.files.wordpress.com/2011/05/codecogseqn-1.gif?w=720" alt=""   /></a></p>
<p>Porém essa análiaze nem de logne prova que toda solução recursiva (ou com comportamento recursivo) tem eficiência inferior à uma iterativa (sequer essa é a intenção deste post, lembra?).</p>
<p>Vamos nesse momento reservar essa primeira parte do post e mostrar um segundo caso idenpendente.</p>
<h2>Potenciação</h2>
<p>Um outro exemplo simples que professores podem adotar para demonstrar recursão é a soluçao de potenciação. A potenciação é simplesmente a multiplicação <em>n</em> vezes de uma base <em>b</em> por ela mesmo. Assim:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=b^n = b*b*b* ... *b" target="_blank"><img title="b^n = b*b*b* ... *b" src="http://latex.codecogs.com/gif.latex?b^n = b*b*b* ... *b" alt="" /></a>   <em>n </em>vezes</p>
<p>Uma implementação simples com recursão segue:</p>
<p><pre class="brush: python;">
def pow(b,n):
  if n==1:
    return b
  return b*pow(b,n-1)
</pre></p>
<p>O bacana é que essa implementação roda em T(n)=O(n), ou seja, linearmente. Mas como nunca devemos nos contentar com o que já temos, podemos pensar em como melhorar essa implementação. A primeira coisa que podemos melhorar é implementar recursão-em-cauda para que o espaço ocupuado em memória não cresça ao aumentar o tamanho de <em>n</em>(Nota: mesmo com nossas implementações estando em Python, o fato de a linguagem suportar ou não a recursão em cauda aqui é desprezado, pois o que nos interessa é a metodologia aplicada na resolução).</p>
<p><pre class="brush: python;">
def pow2(b,n,acc=1):
  if n==1:
    return b*acc
  return pow2(b,n-1,acc*b)
</pre></p>
<p>Mesmo com esse problema resolvido o nosso custo computacional permanece intacto. Para melhorarmos isso podemos recorrer a uma propriedade da potenciação que nos diz que:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=b^n = (b^{\frac{n}{2}})^2" target="_blank"><img title="b^n = (b^{\frac{n}{2}})^2" src="http://latex.codecogs.com/gif.latex?b^n = (b^{\frac{n}{2}})^2" alt="" /></a><br />
<a href="http://www.codecogs.com/eqnedit.php?latex=b^n = b^{\frac{n}{2}}*b^{\frac{n}{2}}" target="_blank"><img title="b^n = b^{\frac{n}{2}}*b^{\frac{n}{2}}" src="http://latex.codecogs.com/gif.latex?b^n = b^{\frac{n}{2}}*b^{\frac{n}{2}}" alt="" /></a></p>
<p>O que, por exemplo, nos leva a dividir o cálculo</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=4^4 = 4*4*4*4" target="_blank"><img title="4^4 = 4*4*4*4" src="http://latex.codecogs.com/gif.latex?4^4 = 4*4*4*4" alt="" /></a></p>
<p>que gasta 3 multiplicaçoem em</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=4^4 = (4^2) ^ 2 = 4^2 * 4^2" target="_blank"><img title="4^4 = (4^2) ^ 2 = 4^2 * 4^2" src="http://latex.codecogs.com/gif.latex?4^4 = (4^2) ^ 2 = 4^2 * 4^2" alt="" /></a></p>
<p>que consome apenas duas (note que o mesmo cálculo 4^2 não precisa ser computado duas vezes). Agora basta chamarmos recursivamente para cada n/2, apenas fazendo uma pequena alteração para os casos de <em>n</em> ser ímpar:</p>
<p><pre class="brush: python;">
def pow3(b,n):
  if n==1:
    return b

  if n%2:
    h = pow3(b,(n-1)/2)
    return b*h*h
  else:
    h = pow3(b,n/2)
    return h*h
</pre></p>
<p>Uma rápida análise deste código nos extrai a forma recursiva do custo computacional desta nova forma:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(1)=O(1)" target="_blank"><img title="T(1)=O(1)" src="http://latex.codecogs.com/gif.latex?T(1)=O(1)" alt="" /></a><br />
<a href="http://www.codecogs.com/eqnedit.php?latex=T(n)=T(n/2) @plus; O(1)" target="_blank"><img title="T(n)=T(n/2) + O(1)" src="http://latex.codecogs.com/gif.latex?T(n)=T(n/2) + O(1)" alt="" /></a></p>
<p>Essa recursão pode ser facilmente resolvida se você imaginar que a cada vez que você dobra <em>n</em> você incremente em <em>O(1)</em> o valor de <em>T(n). </em>Isso nos leva a uma função logarítmica de base dois:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=T(n)=O(lg(n))" target="_blank"><img title="T(n)=O(lg(n))" src="http://latex.codecogs.com/gif.latex?T(n)=O(lg(n))" alt="" /></a></p>
<p>O que é bacana, pois diminuímos aqui o comportamento assintótico do cálculo da potência de <em>b^n</em> pois:</p>
<p><a href="http://assemblando.files.wordpress.com/2011/05/codecogseqn.gif"><img title="CodeCogsEqn" src="http://assemblando.files.wordpress.com/2011/05/codecogseqn.gif?w=129&#038;h=19" alt="" width="129" height="19" /></a></p>
<p>Nesse nosso segundo exemplo, ao contrário do primeiro, o uso da recursão melhororu bastante o desempenho da nossa implementação. Utilizando a técnica de dividir e conquistar, nós quebramos uma potenciação em duas mais simples de se efetuar, e depois recombinamos o resultado. Porém ainda não chegamos no intuito final desse texto que é a união de técnicas com propósitos distintos para ganharmos em desempenho.</p>
<h2>Método de Fibonacci por matrizes</h2>
<p>Uma maneira bacana de se encontrar a sequência de fibonacci é analizando a matriz</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix}" target="_blank"><img title="\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix}" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix}" alt="" /></a></p>
<p>Se multiplicarmos essa matrix por ela mesmo temos:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix}" target="_blank"><img title="\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix}" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix}" alt="" /></a></p>
<p>e novamente:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 3 &amp; 2\\ 2 &amp; 1 \end{pmatrix}" target="_blank"><img title="\begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 3 &amp; 2\\ 2 &amp; 1 \end{pmatrix}" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} 2 &amp; 1\\ 1 &amp; 1 \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} 3 &amp; 2\\ 2 &amp; 1 \end{pmatrix}" alt="" /></a></p>
<p>e genéricamente:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} a &amp; b\\ c &amp; d \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} a@plus;b &amp; a\\ c@plus;d &amp; c \end{pmatrix}" target="_blank"><img title="\begin{pmatrix} a &amp; b\\ c &amp; d \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} a+b &amp; a\\ c+d &amp; c \end{pmatrix}" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} a &amp; b\\ c &amp; d \end{pmatrix} * \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} = \begin{pmatrix} a+b &amp; a\\ c+d &amp; c \end{pmatrix}" alt="" /></a></p>
<p>Agora o interessante para nós é o comportamento da primeira linha da matriz resultado. Chamando</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} a@plus;b &amp; a \end{pmatrix} = \begin{pmatrix} K &amp; L \end{pmatrix}" target="_blank"><img title="\begin{pmatrix} a+b &amp; a \end{pmatrix} = \begin{pmatrix} K &amp; L \end{pmatrix}" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} a+b &amp; a \end{pmatrix} = \begin{pmatrix} K &amp; L \end{pmatrix}" alt="" /></a></p>
<p>percebemos que após cada multiplicação <em>K </em>recebe o valor dele mesmo mais o de <em>L</em> enquanto <em>L </em>recebe o antigo valor de<em> K. </em>Esse deslocamento de valores é semelhante (ou igual) ao que acontece com a forma recursiva da função geradora da fórmula de Fibonacci. A mesma análise pode ser feita na linha inferior da matriz. Com isso reduzimos a forma fechada da geração de números via multiplicação de matrizes:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\begin{pmatrix} Fibo_{n@plus;1} &amp; Fibo_{n}\\ Fibo_{n} &amp; Fibo_{n-1} \end{pmatrix} = \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} ^n" target="_blank"><img title="\begin{pmatrix} Fibo_{n+1} &amp; Fibo_{n}\\ Fibo_{n} &amp; Fibo_{n-1} \end{pmatrix} = \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} ^n" src="http://latex.codecogs.com/gif.latex?\begin{pmatrix} Fibo_{n+1} &amp; Fibo_{n}\\ Fibo_{n} &amp; Fibo_{n-1} \end{pmatrix} = \begin{pmatrix} 1 &amp; 1\\ 1 &amp; 0 \end{pmatrix} ^n" alt="" /></a></p>
<p>ou seja, basta multiplicarmos a matrix geradora por ela mesmo <em>n</em> vezes que obtemos na segunda coluna da primeira linha o valor equivalente a a fibo(<em>n</em>).</p>
<h2>Unindo forças</h2>
<p>Agora que vêm a pergunta, seria possível aproveitar a nossa função de potenciação para multiplicarmos a matriz geradora a fim de obter o enésimo valor da sequência de Fibonacci? Bem, a nossa função da forma que ela foi concebida não serve para multiplicar matrizes, pois a multiplicação é feita na forma <em>a*b</em> que não é suportada para matrizes (pelo menos não em Python, que é a linguagem que estamos fazendo nossos exemplos). Porém podemos facilmente modificar nossa função para aceitar como terceiro parâmetro a função que deverá ser aplicado ao elemento a ser multiplicado:</p>
<p><pre class="brush: python;">
def pow4(b,n,f=lambda x,y:x*y):
  if n==1:
    return b

  if n%2:
    h = pow4(b,(n-1)/2,f)
    return f(f(h,h),b)
  else:
    h = pow4(b,n/2,f)
    return f(h,h)
</pre></p>
<p>O bacana é que a nossa função continua funcionando para potenciação de números, basta omitirmos o terceiro parâmetro, passando apenas a base e o expoente à função.</p>
<p>Uma implementação de multiplicação de duas matrizes 2&#215;2 é dada a seguir:</p>
<p><pre class="brush: python;">
def mult_matrix(ma,mb):
  (a,b),(c,d) = ma
  (e,f),(g,h) = mb
  return [[a*e+b*g,a*f+b*h],[c*e+d*g,c*f+d*h]]
</pre></p>
<p>e por seguinte, definimos a nossa matriz geradora:</p>
<p><pre class="brush: python;">
fib_matrix = [[1,1],[1,0]]
</pre></p>
<p>Agora basta encapsularmos a chamada da função que faz a chamada da potenciação para o cálculo:</p>
<p><pre class="brush: python;">
def fibo3(n):
  if n==0 or n==1:
    return n
  return pow4(fib_matrix, n, mult_matrix)[0][1]
</pre></p>
<p>Agora com a nossa terceira implementação aplicamos o ganho de desempenho provido pela nossa implementação de potenciação para gerarmos o enésimo número da sequência de Fibonacci, melhorando a complexidade do método de O(n) [bom] para O(lg n) [melhor]. Essa mescla de técnicas nos traz melhorias significativas, que pode se mostrado na prática nesse caso.</p>
<h2>Conclusão</h2>
<p>Com um comparativo simples, fazendo n=1.000.000 (um milhão), o cálculo de fibo(n) consumiu :</p>
<pre>fibo2	1m53.979s (implementação iterativa)
fibo3	0m23.732s (com potenciação recursiva de matrizes)</pre>
<p>Por motivos óbvios a primeira implementação não foi avaliada para um <em>n</em> tão alto poi para testes mais simples, como <em>n</em>=40 foram necessários mais de 1 minuto e meio para se computar. E como o comportamento da primeira implementação é exponencial, ao almentarmos o valor de <em>n</em> para pouco mais de 50 unidades, o tempo de execução do nosso algoritmo explodiria (quem tiver paciência, poderia deixar o algoritimo rodando para <em>n</em> com valores elevados, como 100, 200, etc.)</p>
<p>Pretendo posteriormente fazer um comparativo caso a caso das três implementações, mas acredito que com o que equacionamos (e mostramos na prática) como é possível sempre ganharmos um pouco de desempenho se fugirmos um pouco da solução óbvia, e buscarmos por meio mais eficientes, e mesclarmos técnicas, como o que fizemos aqui nessa ocasião, nos aproveitando do ganho na implementação da potenciação para gerarmos números da sequência de Fibonacci.</p>
<p>Todo o código deste post pode ser enconytrado aqui: <a href="https://gist.github.com/972762" target="_blank">gist.github.com/972762</a></p>
<p>Update: <a href="https://github.com/alvesjnr/alvesjnr.github.com/blob/master/50000000">Aqui</a> eu fiz o upload de um arquivo contendo a representação em ASCII do 50.000.000-ésimo número da sequência de fibonacci. O arquivo têm pouco mais de 10MB, e não tem muita utilidade&#8230; mas caso vocÊ queira baixar, fique a vontade!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/131/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/131/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/131/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=131&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2011/05/14/pela-uniao-dos-seus-poderes/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?Fib(n)=leftbeginmatrix0,&#38;n=01,&#38;n=1Fib(n-1)+Fib(n-2),&#38;ngeq2endmatrixright." medium="image">
			<media:title type="html">Fib(n)=\left\{\begin{matrix} 0, &#38; n=0 \\ 1, &#38; n=1 \\ Fib(n-1) + Fib(n-2), &#38; n \geq 2 \end{matrix}\right.</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?fib_2=2,fib_3=3,fib_4=5,fib_5=8,...,fib_n=fib_n-1+fib_n-2" medium="image">
			<media:title type="html">fib_{2}=2, fib_{3}=3, fib_{4}=5, fib_{5}=8, ... , fib_{n}=fib_{n-1}+fib_{n-2}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(1)=O(1)" medium="image">
			<media:title type="html">T(1) = O(1)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=T(n-1)+T(n-2)" medium="image">
			<media:title type="html">T(n) = T(n-1) + T(n-2)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=O(Fibo(n))" medium="image">
			<media:title type="html">T(n)=O(Fibo(n))</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=O(varphin)" medium="image">
			<media:title type="html">T(n) = O(\varphi^n)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=O(n)" medium="image">
			<media:title type="html">T(n) = O(n)</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2011/05/codecogseqn-1.gif" medium="image">
			<media:title type="html">CodeCogsEqn (1)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?bn=b*b*b*...*b" medium="image">
			<media:title type="html">b^n = b*b*b* ... *b</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?bn=(bfracn2)2" medium="image">
			<media:title type="html">b^n = (b^{\frac{n}{2}})^2</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?bn=bfracn2*bfracn2" medium="image">
			<media:title type="html">b^n = b^{\frac{n}{2}}*b^{\frac{n}{2}}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?44=4*4*4*4" medium="image">
			<media:title type="html">4^4 = 4*4*4*4</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?44=(42)2=42*42" medium="image">
			<media:title type="html">4^4 = (4^2) ^ 2 = 4^2 * 4^2</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(1)=O(1)" medium="image">
			<media:title type="html">T(1)=O(1)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=T(n/2)+O(1)" medium="image">
			<media:title type="html">T(n)=T(n/2) + O(1)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?T(n)=O(lg(n))" medium="image">
			<media:title type="html">T(n)=O(lg(n))</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2011/05/codecogseqn.gif" medium="image">
			<media:title type="html">CodeCogsEqn</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrix1&#38;11&#38;0endpmatrix" medium="image">
			<media:title type="html">\begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrix1&#38;11&#38;0endpmatrix*beginpmatrix1&#38;11&#38;0endpmatrix=beginpmatrix2&#38;11&#38;1endpmatrix" medium="image">
			<media:title type="html">\begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix} * \begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix} = \begin{pmatrix} 2 &#38; 1\\ 1 &#38; 1 \end{pmatrix}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrix2&#38;11&#38;1endpmatrix*beginpmatrix1&#38;11&#38;0endpmatrix=beginpmatrix3&#38;22&#38;1endpmatrix" medium="image">
			<media:title type="html">\begin{pmatrix} 2 &#38; 1\\ 1 &#38; 1 \end{pmatrix} * \begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix} = \begin{pmatrix} 3 &#38; 2\\ 2 &#38; 1 \end{pmatrix}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrixa&#38;bc&#38;dendpmatrix*beginpmatrix1&#38;11&#38;0endpmatrix=beginpmatrixa+b&#38;ac+d&#38;cendpmatrix" medium="image">
			<media:title type="html">\begin{pmatrix} a &#38; b\\ c &#38; d \end{pmatrix} * \begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix} = \begin{pmatrix} a+b &#38; a\\ c+d &#38; c \end{pmatrix}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrixa+b&#38;aendpmatrix=beginpmatrixK&#38;Lendpmatrix" medium="image">
			<media:title type="html">\begin{pmatrix} a+b &#38; a \end{pmatrix} = \begin{pmatrix} K &#38; L \end{pmatrix}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?beginpmatrixFibo_n+1&#38;Fibo_nFibo_n&#38;Fibo_n-1endpmatrix=beginpmatrix1&#38;11&#38;0endpmatrixn" medium="image">
			<media:title type="html">\begin{pmatrix} Fibo_{n+1} &#38; Fibo_{n}\\ Fibo_{n} &#38; Fibo_{n-1} \end{pmatrix} = \begin{pmatrix} 1 &#38; 1\\ 1 &#38; 0 \end{pmatrix} ^n</media:title>
		</media:content>
	</item>
		<item>
		<title>Usando List Comprehension</title>
		<link>http://assemblando.wordpress.com/2010/11/22/usando-list-comprehension/</link>
		<comments>http://assemblando.wordpress.com/2010/11/22/usando-list-comprehension/#comments</comments>
		<pubDate>Tue, 23 Nov 2010 00:09:48 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=111</guid>
		<description><![CDATA[Diferente do meu costume, esse post vai ser mais curto, só pra fixar alguns conceitos e formalizar algumas coisas que eu tenho usado mais frequentemente nos últimos tempos. Como entusiasta da linguagem Python, tento extrair ao máximo as facilidades que a linguagem me proporciona. Todo mundo que conhece mesmo que seja um pouquinho sabe que [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=111&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Diferente do meu costume, esse post vai ser mais curto, só pra fixar alguns conceitos e formalizar algumas coisas que eu tenho usado mais frequentemente nos últimos tempos.</p>
<p>Como entusiasta da linguagem Python, tento extrair ao máximo as facilidades que a linguagem me proporciona. Todo mundo que conhece mesmo que seja um pouquinho sabe que uma das grandes vantagens da linguagem são as funções <em>built in</em> que proporcionam, entre outras muitas facilidades, uma simplificação do tratamento de listas.</p>
<p>Trabalhar com listas em Python é extremamente simples (pra quem já usou a STL do C++ sabe bem o trabalho que uma lista pode nos dar!). Mas quando eu comecei a trabalhar com a linguagem (há uns três anos), eu carregava comigo muitos vícios providos dos meus muitos anos programando em C e C++. Veja no exemplo abaixo, o que eu cheguei a fazer no meu início pela linguagem:</p>
<p><pre class="brush: python;">
vetor []
cont = 0
while cont &lt; 10:
    vetor.append(cont)
    cont += 1
</pre></p>
<p>Bem, não precisa ser nenhum sênior na linguagem pra ver que a construção anterior é completamente, digamos, não-pythônica. Isso poderia ser facilmente substituido por um:</p>
<p><pre class="brush: python;">
vetor = range(10)
</pre></p>
<p>Enfim, antes que alguém pense que isso é um post sobre como usar listas em Python (não, isso <strong>não </strong>um post sobre isso. Caso queira aprender mais sobre o assunto eu <a href="http://docs.python.org/tutorial/datastructures.html" target="_blank">sugiro este texto aqui</a>), vamos ao que eu quero falar hoje: <em>List Comprehension.</em></p>
<p>Em <a href="http://en.wikipedia.org/wiki/Set_theory" target="_blank">Teoria dos Conjuntos</a> nós temos algumas notações que nos permitem criar conjuntos de elementos, como por exemplo</p>
<p><img class="alignnone" title="Real Ones" src="http://upload.wikimedia.org/math/1/e/7/1e7c2f9fafbba81ef232173524fb9b4b.png" alt="" width="134" height="20" /></p>
<p>é  a representação do conjunto de todos os elementos reais maiores que zero, ou seja, todos números reais positivos. Ou por exemplo o conjunto dado por</p>
<p><img class="alignnone" title="naturais pares" src="http://upload.wikimedia.org/math/2/8/f/28fe52a6a37298f000663c64e3681958.png" alt="" width="200" height="21" /></p>
<p>que é composto por todos os números naturais pares.</p>
<p>De certa maneira, a função Python <strong>vetor=range(10)</strong> pode ser escrita na notação de conjuntos por</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\large \{ k \in \mathbb{N} : \exists k &lt; 10 \}" target="_blank"><img title="\large \{ k \in \mathbb{N} : \exists k &lt; 10 \}" src="http://latex.codecogs.com/gif.latex?\large \{ k \in \mathbb{N} : k &lt; 10 \}" alt="" /></a></p>
<p>ou seja, <em>k</em> pertence aos naturais e <em>k</em> é menor que 10.</p>
<p><em>List Comprehension</em> é uma maneira de trazer para a linguagem de programação a notação de conjuntos. Um <em>set</em> de elementos criados por uma <em>list comprehension</em> obedece uma série de parâmetros previamente estipulados. Esses parâmetros têm como princípo impor condições para o nosso <em>set</em> de elementos, a fim de compor, baseado em regras, a nossa lista. Não expliquei nada né?? Então vamos por exemplos. Imagine uma <em>set</em> <strong>S</strong> que é dada por:</p>
<p><a href="http://www.codecogs.com/eqnedit.php?latex=\large S = \{ 2*x \ | \ x \in \mathbb{N} : x &lt; 20 \}" target="_blank"><img title="\large S = \{ 2*x \ | \ x \in \mathbb{N} : x &lt; 20 \}" src="http://latex.codecogs.com/gif.latex?\large S = \{ 2*x \ | \ x \in \mathbb{N} : x &lt; 20 \}" alt="" /></a></p>
<p>Assim, temos que <strong>S</strong> é composto por todos os elementos <strong>x </strong>pertencentes aos números naturais, tal que <strong>X&lt;20<em>. </em></strong>Porém, apareceu mais um componente aí na minha lista. Esse componente que aparece antes do <em>pipe</em> é a saída da minha função, ou seja, para cada valor de <strong>x</strong> que existir, ele inluirá no meu <em>set</em> o valor <strong>2*x</strong><em>. </em>A expressão do exemplo anterior pode ser dividida nos seguintes elementos:</p>
<p><em><a href="http://assemblando.files.wordpress.com/2010/11/untitleddrawing.png"><img class="alignnone size-full wp-image-114" title="Untitleddrawing" src="http://assemblando.files.wordpress.com/2010/11/untitleddrawing.png?w=720" alt=""   /></a></em></p>
<p>De um forma simplificada, podemos dizer que dado um <strong>conjunto de entrada<em>, </em></strong>a variável denotada recebe cada elemento deste conjunto que atende à condição imposta pelo predicado (ou seja, nesse caso, verificar se a variável retirada do conjunto é menor que 20). Uma vez atribuído um valor à variável, é aplicado a essa variável a função de saída, que dá o valor que será colocado na minha <em>list</em>. Simples não? Então qual seria o resultado de <strong>S <em>? </em></strong>Todos os números naturais, menores que 20 e multiplicados por 2&#8230;</p>
<p><a href="http://assemblando.files.wordpress.com/2010/11/codecogseqn-1.png"><img class="alignnone size-full wp-image-115" title="CodeCogsEqn (1)" src="http://assemblando.files.wordpress.com/2010/11/codecogseqn-1.png?w=720" alt=""   /></a></p>
<p>Ok, muito interessante mas&#8230; como coloco isso no código? Se estivessemos falando de linguagem C pura (a qual eu aprendi a programar, e carreguei muitos vícios por muito tempo), o nosso código ficaria parecido com:</p>
<p><pre class="brush: cpp;">
int i;
int *v = (int*)malloc(sizeof(int)*20);
for(i=0; i&amp;lt;20; i++)
    v[i] = 2*i;
</pre></p>
<p>Programadores em geral costumam carregar vícios, costumes que aprenderam em outras linguagens. Se eu fosse escrever o código anterior em Python nas primeiras semanas em que comecei a aprender a linguagem eu provavelmente não estaria tirando o máximo da linguagem, mas sim simplesmente transcrevendo uma lógica em código.</p>
<p>Escrevendo esse mesmo código, agora em Python, e usando <em>list comprehension</em> teríamos:</p>
<p><pre class="brush: python;">
S = [2*i for i in range(20)]
</pre></p>
<p>O leitor pode facilmente identificar os elementos citados anteriormente no código Python. Por exemplo, o <em>pipe</em> | foi substituído pela cláusula <strong>for</strong> enquanto o símbolo pertence <a href="http://www.codecogs.com/eqnedit.php?latex=\bg_white \large \in" target="_blank"><img title="\bg_white \large \in" src="http://latex.codecogs.com/png.latex?\bg_white \large \in" alt="" /></a> foi substituído pela cláusula <strong>in</strong>. Como a função <strong>range</strong> já nos devolve os números naturais menores que 20, não foi necessário utilizar a cláusula do predicado, mas caso isso seja necessário, basta adicionar um comparador à variável com um <strong>if</strong> ao final da sentença. Mostrando por exemplos, que fica mais fácil, digamos que em algum momento hipotético possuamos uma lista com os seguintes valores:</p>
<p><pre class="brush: python;">
S = [2,5,7,98,108]
</pre></p>
<p>e desejamos selecionar apenas os valores menores que <strong>50. </strong>Para isso podemos fazer:</p>
<p><pre class="brush: python;">
K = [x for x in S if x &lt; 50]
</pre></p>
<p>Note que nesse exemplo o meu conjunto de dados de entrada é a própria lista <strong>S</strong>, e assim aplicamos um <em>filter</em> no nosso conjunto de elementos. Ora pois, para este fim é mais viável utilizar realmente a função <em>built-in <strong>filter</strong></em> que nos dará o mesmo resultado. Mas se repararmos bem, uma <em>list comprehension</em> nada mais é que um gerador de lista que compreende também uma função <strong>filter </strong>além de uma função <strong>map </strong>em sua construção. Com essas duas utilidades, você pode, ao construir uma lista, filtrar os dados que você deseja e aplicar alguma função à lista.</p>
<h3>Aplicando list comprehension</h3>
<p>Uma aplicação que eu achei bem legal de se fazer com <em>list comprehension</em> é a implementação do algoritmo <em><a href="http://en.wikipedia.org/wiki/Quicksort" target="_blank">Quick Sort</a>. </em>Apenas para relembrar o funcionamento do algoritmo (caso precise de mais detalhes dê um pulo em alguma referência mais completa), o algoritmo basicamente consiste em estipular um <em>pivot</em> que é um elemento aleatório do meu <em>set</em> e separar este <em>set</em> em dois grupos: um contendo todos os elementos maiores que o meu <em>pivot </em>e outro contendo os elementos menores. Uma vez feito isso, a função é chamada recursivamente para cada sub-<em>set </em>de elementos.</p>
<p>Como <em>list comprehension</em> nos permite facilmente filtrar os elementos de uma lista, criando assim uma nova lista, se eu quiser, por exemplo, selecionar todos os elementos <strong>x </strong>da minha lista que são maiores que um valor <strong>pivot </strong>(ou também selecionar os elementos que são menores) eu poderia proceder da seguinte forma:</p>
<p><pre class="brush: python;">
K = [8, 4, 1, 2, 3, 9, 6, 5, 7] #vetor com elementos dispostos em ordem aleatória
pivot = k.pop() #remove um elemento aleatório de K
less_than_pivot = [x for x in K if x &lt; pivot]
greater_than_pivot = [x for x in K if x &gt; pivot]
</pre></p>
<p>Agora bastaria eu chamar recursivamente esta função para os sub-<em>sets</em> obtidos, e concactenar os resultados. O código final ficaria da seguinte forma:</p>
<p><pre class="brush: python;">
def qsort(v): return [] if v == [] else qsort([x for x in v if x &lt; v[0]]) + [v[0]] \
    + qsort([y for y in v if y &gt; v[0]])

if __name__ == &quot;__main__&quot;:
    K = [4, 5, 3, 7, 6, 1, 2, 9, 8]
    print qsort(K)
</pre></p>
<p>Poh, legal, bacana, então quer dizer que apenas o Python implementa <em>list comprehension</em> não é? Na verdade várias outras linguagens (principalmente linguagens de alto nível) implementam esta função. O mesmo código acima pode ser implementado, por exemplo, em <strong><a href="http://en.wikipedia.org/wiki/Erlang_(programming_language)">Erlang</a></strong> usando <em>list comprehension</em>:</p>
<p><pre class="brush: erlang;">
-module(sort).
-export([quick/1]).
quick([]) -&gt;
    [];
quick([Pivot|Rest]) -&gt;
    quick([X || X &lt;- Rest, X &lt; Pivot]) ++ [Pivot] ++ quick([Y || Y &lt;- Rest, Y &gt;= Pivot]).
</pre></p>
<p>Como podem ver, <em>list comprehension</em> podem nos ajudar em várias ocasiões, basta conhecer um pouco mais as funcionalidades que cada linguagem nos provê, e saber quando usá-las.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/111/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/111/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/111/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=111&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/11/22/usando-list-comprehension/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://upload.wikimedia.org/math/1/e/7/1e7c2f9fafbba81ef232173524fb9b4b.png" medium="image">
			<media:title type="html">Real Ones</media:title>
		</media:content>

		<media:content url="http://upload.wikimedia.org/math/2/8/f/28fe52a6a37298f000663c64e3681958.png" medium="image">
			<media:title type="html">naturais pares</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?largekinmathbbN:k&#60;10" medium="image">
			<media:title type="html">\large \{ k \in \mathbb{N} : \exists k &#60; 10 \}</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/gif.latex?largeS=2*x&#124;xinmathbbN:x&#60;20" medium="image">
			<media:title type="html">\large S = \{ 2*x \ &#124; \ x \in \mathbb{N} : x &#60; 20 \}</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/11/untitleddrawing.png" medium="image">
			<media:title type="html">Untitleddrawing</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/11/codecogseqn-1.png" medium="image">
			<media:title type="html">CodeCogsEqn (1)</media:title>
		</media:content>

		<media:content url="http://latex.codecogs.com/png.latex?bg_whitelargein" medium="image">
			<media:title type="html">\bg_white \large \in</media:title>
		</media:content>
	</item>
		<item>
		<title>Complexidade Algorítmica na Prática 2 &#8211; Recursões</title>
		<link>http://assemblando.wordpress.com/2010/10/22/complexidade-algoritmica-na-pratica-recursoes/</link>
		<comments>http://assemblando.wordpress.com/2010/10/22/complexidade-algoritmica-na-pratica-recursoes/#comments</comments>
		<pubDate>Fri, 22 Oct 2010 05:56:36 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Complexidade Algorítmica]]></category>
		<category><![CDATA[Matemática]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=100</guid>
		<description><![CDATA[Depois do post sobre complexidade algorítmica, algumas pessoas me perguntaram como resolver quando o algoritmo é recursivo. Bem, eu me lembrei imediatamente do livro Concrete Mathematics do Donald Knuth. Este livro traz um capítulo que trata somente de recursões, trazendo junto algumas ferramentas que facilitam nossa vida na hora de analizar a complexidade de uma [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=100&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Depois do post sobre complexidade algorítmica, algumas pessoas me perguntaram como resolver quando o algoritmo é recursivo. Bem, eu me lembrei imediatamente do livro <em><a href="http://www.amazon.com/Concrete-Mathematics-Foundation-Computer-Science/dp/0201558025" target="_blank">Concrete Mathematics</a> </em> do <em><a href="http://www-cs-faculty.stanford.edu/~uno/" target="_blank">Donald Knuth</a></em>. Este livro traz um capítulo que trata somente de recursões, trazendo junto algumas ferramentas que facilitam nossa vida na hora de analizar a complexidade de uma função recursiva.</p>
<p>Então, baseando nas soluções dadas por <em>Knuth</em>, vamos analizar aqui dois casos clássicos de recursão:</p>
<h2>Torre de Hanoy</h2>
<p>Um algoritmo clássico e que está presente na maioria dos cursos de computaçãp/programação quando é apresentado ao aluno o conceito de recursão é o algoritmo que resolve o problema da <a href="http://en.wikipedia.org/wiki/Tower_of_Hanoi" target="_blank">Torre de Hanoy</a>.  Esse algoritmo é usado em cursos de computação devido a sua extrema simplicidade e facilidade de compreensão por parte do estudante.</p>
<p>A sua idéia básica é enxergar a torre de discos como duas torres distintas: uma sub-torre formada por <em>n</em>-1 elementos e uma segunda torre formada pelo <em>n</em>-ésimo (último) disco. Assim devemos mover esta sub-torre para uma casa auxiliar, mover o <em>n</em>-ésimo elemento para o destino e, por fim, mover a sub torre da casa auxiliar para a casa de destino. Cada sub-torre pode ser enxergada como uma nova torre, e aplicada, recursivamente, o mesmo algoritmo, até que cheguemos a uma torre com <em>n=1</em> elementos. O código a seguir ilustra a sequência de passos necessária para se resolver o problema da torre de Hanoy:</p>
<p><pre class="brush: python;">
def hanoy(n, origem, destino, aux):
    if n== 1:
        print &quot;Mova disco %i de %c para %c&quot; % (n, origem, destino)
    else:
        hanoy(n-1, origem, aux, destino)
        print &quot;Mova disco %i de %c para %c&quot; % (n, origem, destino)
        hanoy(n-1, aux, destino, origem)
if __name__==&quot;__main__&quot;:
    hanoy(3, &quot;A&quot;, &quot;C&quot;, &quot;b&quot;)    #exemplo de como usar
</pre></p>
<p>Como eu disse anteriormente, a simplicidade desse código é um dos grandes atrativos para se ensinar recursão.</p>
<p>Porém aqui a nossa questão é outra (tanto porque estamos assumindo que todo leitor que deseja aferir a complexidade de um algoritmo que utilize recursão domine essa técnica). A nossa pergunta é: <strong>Qual a complexidade temporal desse algoritmo</strong>, ou em outras palavras<strong>, A medida que eu aumento a quantidade <em>n</em> de disco, em quanto eu aumento o tempo T(n) de execução do meu código?</strong></p>
<p>A primeira solução, e a mais simples possível, é a aferição empírica: coloco uma variável contanto o número de chamadas que a minha função têm e traço um gráfico da relação <em>número-de-discos X número-de-movimentos:</em></p>
<p><em><a href="http://assemblando.files.wordpress.com/2010/10/image.png"><img class="alignnone size-full wp-image-102" title="image" src="http://assemblando.files.wordpress.com/2010/10/image.png?w=720" alt=""   /></a></em></p>
<p>Uma coisa podemos aferir com total certeza sobre o comportamento deste gráfico: a função que ele representa não é linear! Mas, enfim, qual a função que representa a complexidade deste algorítmo?</p>
<p>Todo algoritmo recursivo tem uma característica comum: um ponto de final de execução. Para a função da torre de Hanoy, o fim da execução é dado para sempre que <em>n=1</em>, assim temos que:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n) = 1 \leftrightarrow n = 1" alt="" /></p>
<p>ou</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{1} = 1" alt="" /></p>
<p>Agora analizemos como se comporta o algoritmo para <em>n=2</em>. Primeiramente o código vai chamar a função (de forma recursiva) para <em>n=1</em> [linha #5], essa entrada da função faz um movimento [linha #3] e, saindo da função, fazemos mais um movimento [linha #6] e novamente chamamos a função de forma recursiva [linha #7]. Essa segunda chamada recursiva também vai passar como parâmetro o valor <em>n=1</em>, assim mais uma vez será feito um movimento [linha #3, dentro da chamada segunda chamada].</p>
<p>Resumindo, para <em>n=2</em> fizemos duas chamadas recursivas [linhas #5 e #7]. Como em cada chamada um movimento é feito, temos aí dois movimentos dentro das chamadas recursivas. Além desses dois movimentos, um terceiro movimento é executado entre as chamadas das linha #5 e #7. Assim temos um total de 3 movimentos, ou:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{2} = 3" alt="" /></p>
<p>Generalizando a quantidade de movimentos para <em>n=2</em> temos que ele realiza 1 movimento, mais duas vezes a qauntidade de movimentos para <em>n=1</em> (devido as duas chamadas recursivas). Assim temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{2} = 2*t_{1} + 1" alt="" /></p>
<p>Generalizando para qualquer valor de <em>n</em> temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{n} = 2*t_{n-1} + 1" alt="" /></p>
<p>Ótimo, retiramos assim a relação da quantidade de movimentos pela quantidade de discos existente no problema. Mas há um porém: e se eu quiser saber quantos movimentos são necessários para mover uma torre com 64 discos por exemplo? Teríamos que calcular <img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{1}" alt="" />, com isso obter <img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{2}" alt="" /> e assim por diante até <img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{62}" alt="" />. Um tanto braçal não? Agora imagine que desejamos procurar para um n ainda maior? Assim precisamos colocar essa equação em sua <a href="http://en.wikipedia.org/wiki/Closed-form_expression" target="_blank">forma fechada</a>:</p>
<p>Por enquanto temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{1} = 1" alt="" /></p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{n} = 2*t_{n-1} + 1" alt="" /></p>
<p>Somando 1 em cada lado da equação temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{n} + 1 = 2*t_{n-1} + 2" alt="" /></p>
<p>Assim podemos dizer que:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n} = t_{n} + 1" alt="" /></p>
<p>Que por extensão também é válido dizer:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n-1} = t_{n-1} + 1 \rightarrow t_{n-1} = U_{n-1} - 1" alt="" /></p>
<p>Da segunda equação temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n} = 2*t_{n-1} + 2" alt="" /></p>
<p>Assim:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n} = 2*(U_{n-1} - 1) + 2" alt="" /></p>
<p>Que por fim nos dá:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n} = 2*U_{n-1}" alt="" /></p>
<p>Depois de toda essa manipulação ainda não temos uma forma fechada que solucione o nosso problema, mas perceba agora que a relação entre um elemento e o seu antecessor é dada pelo dobro do antecessor. Parafraseando o <em>Donald Knuth</em>: Não é preciso ser nenhum gênio para notar que</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?U_{n} = 2^{n}" alt="" /></p>
<p>Como</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{n} = U_{n} - 1" alt="" /></p>
<p>Temos então que:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{n} = 2^{n} - 1" alt="" /></p>
<p>Que, por fim, nos dá a complexidade temporal do algoritmo:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n) = O(2^{n})" alt="" /></p>
<p>Sim, a ordem do nosso algoritmo é exponencial. Isso explica o porque do nosso gráfico explodir para valores [nem tão] elevados de <em>n</em>.</p>
<p>Recordadno os passos que fizemos para chegar ao resultado, primeiro nós encontramos o ponto de parada do algoritmo, em seguida aferimos quantos movimentos [aqui figurado como o nosso custo] são feitos para o caso mais simples (<em>n=1</em>). A partir disso encontramos a solução iterativa do custo computacional do nosso algoritmo. Com a solução iterativa em mãos, lançamos mão de técnicas de manipulação para encontrar a forma fechada da função que corresponde ao seu custo. E assim chegamos finalmente na complexidade relacionada ao método.</p>
<p>Perceba que por momentos lançamos mão de induções para chegar a forma fechada da solução.</p>
<h2>Algoritmo de Ordenação QuickSort</h2>
<p>Um outro algoritmo que é muito estudado pelos iniciantes de computação é a solução de ordenação <em><a href="http://en.wikipedia.org/wiki/Quicksort" target="_blank">Quick Sort</a>. </em>Existem pelo menos dois bons motivos para se estudar o <em>Quick Sort</em>: 1 &#8211; ele é um algoritmo bastante eficiente de ordenação, 2 &#8211; é um algoritmo relativamente simples de se implementar e de entender e 3 &#8211; é mais uma oportunidade de se mostrar o funcionamento de recursão, desta vez em um algoritmo que é bastante usado em diversas aplicações.</p>
<p>A idéia básica do quicksort está em subdividir os valores do <em>set</em> de elementos a serem ordenados em duas partes: uma inferior a um valor denominado <em>pivo</em> e outra maior que este valor. Em seguida, o <em>set</em> de valores é disposto de uma maneira em que os valores a direita deste <em>pivot</em> sejam de valor inferior a ele, e os a esquerda de valor superior. Por fim a função é novamente chamada (de forma recursiva, note) para estes dois grupos de elementos. Note aí a recursão, que é o nosso alvo neste momento (para mais informações sobre o funcionamento do algoritmo <em>Quick Sort</em> consulte seu livro de algoritmos preferido!).</p>
<p>Existem várias maneiras de se aferir a complexidade de um algoritmo como esse. Vamos apresentar duas, cada uma assumindo certas particularidades na implementação do algoritmo.</p>
<h3>Solução #1:</h3>
<p>Como no primeiro exemplo (Hanoy) vamos primeiramente estipular qual seria o custo para um valor <em>n</em> trivial. Assim, não é difícil enxergar que:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?t_{0} = 0" alt="" /></p>
<p>Neste caso, o primeiro custo é, dado o pivo, fazer a partição do meu <em>set</em> de dados em duas partes. Ora, dado um conjunto de <em>n</em> valores, para dividi-lo em duas partes eu preciso fazer até <em>n+1</em> comparações:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C_{a}(n) = n + 1" alt="" /></p>
<p>Como o valor do <em>pivo</em> pode ser qualquer um dos elementos do <em>set</em>, temos que estimar a média de todas as possibilidades de partições possíveis. Por exemplo, se o meu <em>set</em> contém 10 elementos, temos as seguintes opções de partição: (5,0),(1,4), (2,3), (3,2), (1,4) e (0,5). Note que cada uma dessas partições vai chamar, recursivamente, a função para cad um dos subsets criados. Cada um desses <em>subsets </em>têm um custo proporcional a quantidade de valores que ele contém. Assim temos que o valor médio entre todas as possibilidades é dada por:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C_{b}(n) = {1 \over n} {\sum_{ k=0 }^{n-1} (C(k) + C(n-k+1))}" alt="" /></p>
<p>Explicando um pouco melhor o que fizemos anteriormente, note que a quantidade de elementos de cada subset é que determina qual vai ser o custo de processar cada um destes. Emfim, tirando a média entre todas as possibilidades temos o valor médio do custo para todas as possibilidades.</p>
<p>Por fim, somando as duas partes temos que o custo C(n) para executar esse código é dado por:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n) = n + 1 + {1 \over n} {\sum_{ k=0 }^{n-1} (C(k) + C(n-k+1))}" alt="" /></p>
<p>Uhm, bacana, agora além de ser iterativo, temos um somatório aí dentro. Bem, vamos com calma. A primeira coisa a fazermos é retirar a recursão desta expressão. Para isto temos que sumir com a variável n dentro de C(n-k+1). Para isto vamos abrir o somatório para um valor dado de <em>n</em> como por exemplo <em>n=3</em>. Assim temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi? {\sum_{ k=0 }^{3-1} (C(k) + C(3-k+1))} = {C(0)+C(4) + C(1)+C(3) + C(2)+C(2) + C(3)+C(1) + C(4)+C(0)}" alt="" /></p>
<p>Uhm, agora ficou fácil. Perceba que cada elemento repete duas vezes, assim podemos simplificar para:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi? {\sum_{ k=0 }^{3-1} (C(k) + C(3-k+1))} = {2*(C(0)+C(1)+C(2)+C(3)+C(4))}" alt="" /></p>
<p>Que nos dá:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi? {\sum_{ k=0 }^{n-1} (C(k) + C(n-k+1))} = {\sum_{ k=0 }^{n-1} 2*C(k)}" alt="" /></p>
<p>Retirando a constante 2 de dentro do somatório, e reescrevendo a função temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n) = n + 1 +{2 \over n} {\sum_{ k=0 }^{n-1} C(k)}" alt="" /></p>
<p>Bem, agora que conseguimos retirar a recursão que estava dentro do somatório, temos que dar um jeito de sumir com esse somatório. Vamos para isso tentar manipular a função a fim de tira-la. Multiplicamos primeiro ambos os lados por <em> n</em>:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n) = n^2 + n +{2} {\sum_{ k=0 }^{n-1} C(k)}" alt="" /></p>
<p>Agora substituindo <em> n </em> por <em> n-1</em>:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?(n-1)C(n) = (n-1)^2 + (n-1) +{2} {\sum_{ k=0 }^{n-1} C(k)}" alt="" /></p>
<p>Subtraindo uma pela outra a fim de eliminar o somatório:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n) - (n-1)*C(n-1) = 2n - 2*C(n-1)" alt="" /></p>
<p>Assim temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C(0) = 0" alt="" /><br />
<img src="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n) = (n+1)*C(n-1) + 2*n" alt="" /></p>
<p>Voltando agora ao nosso querido <em>Donald Knuth</em>, no livro <em>Concrete Mathematics</em> ele nos passa uma maneira de reduzir uma sentença da forma</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?a_{n}T_{n} = b_{n}T_{n-1} + C_{n}" alt="" /></p>
<p>em</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi? T_{n} = {1 \over {S_{n}a_{n}}}(s_{1}b_{1}T_{0} + \sum_{k=1}^{n}S_{k}C_{k})" alt="" /></p>
<p>[para mais detalhes sobre essa transformação vide o livro <em>Concrete Mathematics, Donald Knuth</em>, pág 27]</p>
<p>Assim, reescrevendo a nossa função nesta forma temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n) = 2(n + 1)\sum_{k=1}^{n}{1 \over{k+1}}" alt="" /></p>
<p>Mas somatório denovo??? Sim, mas note desta vez que não temos mais uma recursão dentro da nossa função. Note também que essa somatória tem uma cara de alguma <a href="http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)" target="_blank">série harmônica</a>.</p>
<p>Assim, vamos manipular esse somatório para deixa-lo como uma série harmonica:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?\sum_{k=1}^{n}{1 \over{k+1}} = \sum_{k=1}^{n}{1 \over k} - 1 + {1 \over {n+1}}" alt="" /></p>
<p>Da série harmônica temos que:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?\sum_{k=1}^{n}{1 \over{k}} = \ln{k} + \gamma + \epsilon_{k}" alt="" /></p>
<p>Assim, tomando apenas o elemento de maior índice (para conta da notação <em> Big O</em>):</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n) = 2(n+1)* \ln{n}  - 2n" alt="" /></p>
<p>Que, finalmente, nos dá:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n) = O(n\ln{n})" alt="" /></p>
<h3>Solução #2:</h3>
<p>Depois de provarmos matematicamente a ordem deste algoritmo, ficamos mais a vontade de inferir uma análize um pouco menos formal. Vamos analizar agora com base apenas no fluxo do algoritmo, sem demais formalidades.</p>
<p>Uma escolha aleatória do <em>pivo</em> divide em duas partes o nosso <em>set</em> de elementos. Supunhamos agora, num caso ótimo, que o elemento escolhido como <em>pivo</em> seja o elemento central. Assim dividimos o <em>set</em> em dois grupos de iguais quantidades. Dividindo constantemente cada <em>subset</em> em duas partes iguais, fica fácil calcular o número máximo de divisões que teríamos.</p>
<p>Por indução, suponha que nosso <em>set</em> tenho 32 elementos. Se seguirmos constantemente dividindo-o por dois temos: 1*32, 2*16, 4*8, 8*4, 16*2 e por fim 32*1. Note que o crescimento é similar a uma árvore binária, onde cada nó se divide em dois ramos, e o custo total é análogo a altura desta árvore.</p>
<p>Como a altura de uma árvore é dada, em aproximação livre, por <img src="http://www.forkosh.dreamhost.com/mathtex.cgi?\log_{2}n" alt="" />, e sendo este valor análogo à quantidade de chamadas recursivas que o algoritmo irá fazer, e sendo também que, para cada chamada, toda a execução dentro da chamada é executada em O(n), temos:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n) = O(n)*O(\ln{n})" alt="" /></p>
<p>Que, por fim, nos dá:</p>
<p><img src="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n) = O(n \ln{n})" alt="" /></p>
<p>Assim concluimos a análize da complexidade de dois algoritmos recursivos. Espero que tenha sido útil o <em>post</em>.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/100/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/100/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/100/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=100&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/10/22/complexidade-algoritmica-na-pratica-recursoes/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/10/image.png" medium="image">
			<media:title type="html">image</media:title>
		</media:content>

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n)=1leftrightarrown=1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_1=1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_2=3" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_2=2*t_1+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_n=2*t_n-1+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_2" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_62" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_1=1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_n=2*t_n-1+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_n+1=2*t_n-1+2" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n=t_n+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n-1=t_n-1+1rightarrowt_n-1=U_n-1-1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n=2*t_n-1+2" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n=2*(U_n-1-1)+2" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n=2*U_n-1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?U_n=2n" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_n=U_n-1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_n=2n-1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n)=O(2n)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?t_0=0" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C_a(n)=n+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C_b(n)=1overnsum_k=0n-1(C(k)+C(n-k+1))" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n)=n+1+1overnsum_k=0n-1(C(k)+C(n-k+1))" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?sum_k=03-1(C(k)+C(3-k+1))=C(0)+C(4)+C(1)+C(3)+C(2)+C(2)+C(3)+C(1)+C(4)+C(0)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?sum_k=03-1(C(k)+C(3-k+1))=2*(C(0)+C(1)+C(2)+C(3)+C(4))" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?sum_k=0n-1(C(k)+C(n-k+1))=sum_k=0n-12*C(k)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n)=n+1+2overnsum_k=0n-1C(k)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n)=n2+n+2sum_k=0n-1C(k)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?(n-1)C(n)=(n-1)2+(n-1)+2sum_k=0n-1C(k)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n)-(n-1)*C(n-1)=2n-2*C(n-1)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C(0)=0" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?n*C(n)=(n+1)*C(n-1)+2*n" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?a_nT_n=b_nT_n-1+C_n" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T_n=1overS_na_n(s_1b_1T_0+sum_k=1nS_kC_k)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n)=2(n+1)sum_k=1n1overk+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?sum_k=1n1overk+1=sum_k=1n1overk-1+1overn+1" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?sum_k=1n1overk=lnk+gamma+epsilon_k" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?C(n)=2(n+1)*lnn-2n" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n)=O(nlnn)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?log_2n" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n)=O(n)*O(lnn)" medium="image" />

		<media:content url="http://www.forkosh.dreamhost.com/mathtex.cgi?T(n)=O(nlnn)" medium="image" />
	</item>
		<item>
		<title>OpenMP &#8211; Primeiro Contato</title>
		<link>http://assemblando.wordpress.com/2010/09/25/openmp-primeiro-contato/</link>
		<comments>http://assemblando.wordpress.com/2010/09/25/openmp-primeiro-contato/#comments</comments>
		<pubDate>Sat, 25 Sep 2010 19:04:46 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Paralelismo]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=93</guid>
		<description><![CDATA[Dando continuidade ao texto sobre Paralelismo, esta segunda parte trata na prática um pouco como funciona um programa paralelo usando OpenMP. Vale lembrar que este texto foi escrito no início do meu trabalho de pesquisa, e estou o reciclando por desativar o antigo blog. Espero que gostem: Nada como um projeto novo para entusiasmar no [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=93&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<div>
<p>Dando continuidade ao texto sobre Paralelismo, esta segunda parte trata na prática um pouco como funciona um programa paralelo usando OpenMP. Vale lembrar que este texto foi escrito no início do meu trabalho de pesquisa, e estou o reciclando por desativar o antigo blog. Espero que gostem:</p>
<p>Nada como um projeto novo para entusiasmar no aprendizado de tecnologias diferentes. Já fazia algum tempo que eu estava com vontade de começar a trabalhar com programação paralela, mas sem algo concreto para dar o impulso inicial estava complicado.</p>
<p>Relembrando o algorítimo de cálculo do valor de pi com base em uma série de somas:</p>
<pre>soma = 0
para cada i, enquanto i &lt; LIMITE:
   soma += 4/(i*4 + 1)
   soma -= 4/(i*4 + 3)
   i += 1
fim para</pre>
<p>Resolvi implementar esse pequeno código para testar uma primeira implementação usando o OpenMP. Na minha pesquisa de paralelismo de simulação de dinâmica dos Fluidos (CFD) vou usar a linguagem <a href="http://en.wikipedia.org/wiki/Fortran" target="_blank">Fortran</a>, por ser uma linguagem diretamente voltado à cálculos matemáticos. Porém, com uma rápida pesquisa sobre OpenMP, descobri que ela é suportada também pela linguagem <a href="http://en.wikipedia.org/wiki/C%2B%2B" target="_blank">C++</a>, uma velha amiga, que eu sempre gostei muito de usar. Pensei então &#8220;por que não estudar como usar o OpenMP em ambas?&#8221;. Então, na medida do possível, pretendo repetir todos os experimentos e exemplos nas duas linguagens.</p>
<div>
<dl>
<dt><a href="http://usingomp.files.wordpress.com/2010/04/openmptimeline.gif"><img title="openMPtimeline" src="http://usingomp.files.wordpress.com/2010/04/openmptimeline.gif?w=719&#038;h=143" alt="" width="719" height="143" /></a></dt>
<dd>Versões do OpenMP para C++ e Fortran</dd>
</dl>
</div>
<p>Como discutido no post anterior, a idéia do OpenMP é ser uma API que faz o gerenciamento dos Threads, distribuindo-os entre os núcleos/processadores disponíveis no sistema. Esse gerenciamento faz com que o programador não precise se preocupar com o sincronismo entre os threads, nem com a escalabilidade dos sistema.</p>
<p>Porém, o OpenMP é uma API, ou seja, uma ferramenta. E uma ferramenta só faz o que o programador a instrui fazer. Basicamente fica ao encargo do programador definir quais regiões serão paralelizadas, quais serão executadas de forma serial e quais são as variáveis compartilhadas e privadas de cada thread. O programador faz uso de algumas diretivas que são incluídas no código. Essas diretivas são avaliadas pelo pré-processador do OpenMP, e este faz o trabalho de dividir as tarefas entre os núcleos/processadores.</p>
<p>Tomemos como base a implementação em linguagem C do programa de cálculo do PI:</p>
<p><pre class="brush: cpp;">
#include
#define NUM_PASSOS 20000000

int main(){
 double pi = 0;
 int i;

 for(i = 0; i &lt; NUM_PASSOS; i++){
   pi += 4.0 / (4.0*i + 1.0);
   pi -= 4.0 / (4.0*i + 3.0);
 }
 printf(&quot;-&gt; %f\n&quot;,pi);
return 0;
}
</pre></p>
<p>O mesmo exemplo pode ser visto em Fortran <a href="http://pastebin.com/sANACdEV" target="_blank">aqui</a></p>
<p>Compilando esse código com o <a href="http://gcc.gnu.org/" target="_blank">GCC</a>, sem nenhuma flag de otimização e executando o código em um pc com processador intel ia32 com GNU/Linux o programa precisou de quase 1 segundo para executar:</p>
<p><pre class="brush: plain;">

[john@vaio rascunhos]$ time ./a.out
-&gt; 3.141593

real    0m0.906s
user    0m0.870s
sys     0m0.000s

</pre></p>
<p>Apenas para efeito de comparação, o código escrito em Fortran, compilado com o compilador ifort da intel, precisou de apenas 0.010s para executar. Um ganho de quase 90% que o Fortran proporciona, mas isso é assunto para um outropost.</p>
<p>O motivo pelo o qual esse código consumiu tanto tempo para ser executado está no número de repetições dentro do laço. A priori, em todo laço existe uma grande possibilidade de paralelizar o processo, dividindo as tarefas internas do laço entre os vário núcleos do meu sistema multiprocessado. Neste exemplo simples, percebam, a complexidade do meu algoritmo é linear, e relativa ao número de iterações. Em caso de complexidades mais críticas (quadrática, por exemplo), a paralelização do código traz benefícios ainda mais evidentes.</p>
<p>Devido a problemas estruturais (minha máquina não é multi-core) realizei o mesmo experimento com o auxílio do meu amigo <a href="http://www.coding.com.br/" target="_blank">Maluta</a> em um Mac Mini que rendeu o seguinte resultado (não paralelizado, apenas para servir de base comparativa para o código paralelizado):</p>
<p><pre class="brush: plain;">
$ time ./a
-&gt; 3.141593

real 0m0.686s
user 0m0.634s
sys 0m0.002s
</pre></p>
<p>Para paralelizar o código devemos indicar, por meio de diretivas, quais trechos o compilador deve utilizar a API OpenMP para dividir em threads. A primeira diretiva apresentada é a diretiva OMP PARALLEL. Essa diretiva indica qual parte do código vai ser dividida (fork). O final de um trecho paralelo é sinalizado com um OMP END PARALLEL:</p>
<pre>...
... trecho serial
...
OMP PARALLEL
...
... trecho paralelizado
...
OMP END PARALLEL
...
... serial novamente</pre>
<p>Desta maneira, tudo que está no trecho paralelizado vai ser executado simultaneamente por todos os threads do sistema. Devemos salientar que as diretivas sofrem leves variações do C++ para o Fortran. Essas diferenças serão notadas nos códigos, mas a semântica é a mesma para ambos os casos.</p>
<p>Outra diretiva, senão a mais usada, é a que indica que um loop deve ser paralelizado. A diretiva OMP PARALLEL FORou OMP DO são usadas para indicar que um laço deve ser dividido. Como o intuito desse blog não é ser um tutorial de OpenMP ou uma referência formal, mais detalhes sobre a sintaxe de cada diretiva pode ser encontrada no site oficial do projeteo <a href="http://openmp.org/wp/" target="_blank">OpenMP</a>.</p>
<p>Paralelizando o laço principal (e único) do meu programa, temos o seguinte código:</p>
<p><pre class="brush: cpp;">
#include
#include
#define NUM_PASSOS 20000000
&lt;pre&gt;int main(){
	double pi = 0;
	int i;
	#pragma omp parallel for
	for(i = 0; i &lt; NUM_PASSOS; i++){
		pi += 4.0 / (4.0*i + 1.0);
		pi -= 4.0 / (4.0*i + 3.0);
	}
	printf(&quot;-&gt; %f\n&quot;,pi);
return 0;
}
</pre></p>
<p>O equivalente em Fortran pode ser encontrado <a href="http://pastebin.com/MtfPneTS" target="_blank">aqui.</a></p>
<p>Para compilar o código usando o GCC e o OpenMP, é necessário usar a flg -fopenmp:</p>
<pre>gcc -fopenmp &lt;source&gt; [-o nome]</pre>
<p>O resultado da execução deste código foi:</p>
<p><pre class="brush: plain;">
$ time ./b
-&gt; 2.897633

real 0m0.544s
user 0m1.041s
sys 0m0.005s
</pre></p>
<p>Bem, o tempo de execução para o código linear foi de o.686s e no paralelo foi de 0.544s, ou seja, um ganho de aproximadamente 20%, porém, o meu resultado foi PI = 2.897633 ????????????? Alguma coisa não funcionou corretamente neste modelo de paralelismo.</p>
<p>A resposta é simples: quando o código foi paralelizada, cada parte da execução foi feita em um núcleo diferente. Assim, ao se fazer o join, as duas partes não são somadas, como deveriam. Em outras palavras, as variáveis Soma nesse caso estão separadas, e não compartilham o mesmo dado. O que devemos fazer nesse caso é forçar a união desses valores ao fim da execução dos threads. Para isso usamos mais uma diretiva, a diretiva REDUCTION:</p>
<p><pre class="brush: cpp;">
#include
#include
#define NUM_PASSOS 20000000

int main(){
    double pi = 0;
    int i;
    #pragma omp parallel for reduction (+:pi)
    for(i = 0; i &lt; NUM_PASSOS; i++){
        pi += 4.0 / (4.0*i + 1.0);
        pi -= 4.0 / (4.0*i + 3.0);
    }
    printf(&quot;-&gt; %f\n&quot;,pi);
    return 0;
}
</pre></p>
<p>Novamente, o resultado em Fortran se encontra <a href="http://pastebin.com/8a6RLr09" target="_blank">aqui.</a></p>
<p>Compilando e executando o código, temos a seguinte saída:</p>
<p><pre class="brush: plain;">
$ time ./a
-&gt; 3.141593

real 0m0.433s
user 0m0.634s
sys 0m0.003s
</pre></p>
<p>Agora o nosso resultado confere com o valor de PI e o ganho em desempenho é de aproximadamente 36% comparado com o seu equivalente serial.</p>
<p>Com isso já temos base suficiente para brincar um tanto bom com paralelismo, e testar em outros algoritimos.</p>
</div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/93/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/93/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/93/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=93&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/09/25/openmp-primeiro-contato/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://usingomp.files.wordpress.com/2010/04/openmptimeline.gif" medium="image">
			<media:title type="html">openMPtimeline</media:title>
		</media:content>
	</item>
		<item>
		<title>O que é e Como Funciona o Paralelismo</title>
		<link>http://assemblando.wordpress.com/2010/09/24/o-que-e-e-como-funciona-o-paralelismo/</link>
		<comments>http://assemblando.wordpress.com/2010/09/24/o-que-e-e-como-funciona-o-paralelismo/#comments</comments>
		<pubDate>Fri, 24 Sep 2010 03:37:04 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=54</guid>
		<description><![CDATA[Há algum tempo eu escrevi um texto sobre como funciona (informalmente) o paralelismo de códigos. Como estou querendo condensar meus tópicos em um único blog, vou reproduzí-lo aqui. Espero que gostem: Sem delongas, na computação de alto desempenho o tempo é um fator determinante no resultado final de um trabalho. Em um campo onde a [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=54&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<div>
<p>Há algum tempo eu escrevi um texto sobre como funciona (informalmente) o paralelismo de códigos. Como estou querendo condensar meus tópicos em um único blog, vou reproduzí-lo aqui. Espero que gostem:</p>
<p>Sem delongas, na computação de alto desempenho o tempo é um fator determinante no resultado final de um trabalho. Em um campo onde a complexidade dos algoritmos tende a subir, e o tempo gasto para se executar um algoritmo engrandece junto à sua complexidade, distribuir as tarefas entre vários processadores e vários núcleos é uma maneira de se ganhar um tempo precioso na execução das tarefas.</p>
<p>O princípio básico é extremamente intuitivo: O seu código é dividido em partes que podem ser executadas separadamente, e cada uma dessas partes são enviadas à diferentes núcleos/processadores do seu sistema. Simples assim.</p>
<p>Como todo computólogo já está cansado de saber, a teoria pode até ser simples, mas na prática as coisas são um pouco diferente. Como o intuito desse blog é ser direto, vamos explicar por exemplos. Tomamos o código abaixo que tem como função calcular o valor de Π (pi). Esse cálculo pode ser feito baseado em uma série dada por :</p>
<p><a href="http://usingomp.files.wordpress.com/2010/03/pi.png"><img title="pi" src="http://usingomp.files.wordpress.com/2010/03/pi.png?w=339&#038;h=50" alt="" width="339" height="50" /></a></p>
<p>Assim sendo, o algoritmo que resulta na soma pode ser descrito por:</p>
<pre>soma = 0
para cada i, enquanto i &lt; LIMITE:
   soma += 4/(i*4 + 1)
   soma -= 4/(i*4 + 3)
   i += 1
fim para</pre>
<p>Ao final da sequência, a variável soma compreende o valor de pi.</p>
<p>Dentro desse loop, que deve ser repetido diversas vezes, são executadas duas principais funções, que geram a soma total do valor de pi. Imaginemos agora que fosse possível dividir essas duas somas, cada um para um núcleo de um processador:</p>
<pre>Processador 1:
soma1 = 0
para cada i, enquanto i &lt; LIMITE:
   soma1 += 4/(i*4 + 1)
   i += 1
fim para
<pre>Processador 2:
soma2 = 0
para cada i, enquanto i &lt; LIMITE:
   soma2 -= 4/(i*4 + 3)
   i += 1
fim para</pre>
</pre>
<p>E ao final de ambos os processamentos, os valores das duas variáveis são adicionados, resultando em um único valor:</p>
<pre>soma = soma1 + soma2</pre>
<p>Com isso nosso algoritmo de soma foi dividido em dois, e cada um direcionado para um núcleo independente. Isso faz com que as duas somas de cada loop ocorram &#8220;simultâneamente&#8221;, ou seja, em paralelo. Com isso, o ganho hipotético em tempo de execução do nosso algoritmo paralelizado é de cerca de 2x.</p>
<p>Esse técnica de dividir e reagrupar (fork &amp; join) é a base de como o OpenMP trabalha para paralelizar os códigos.</p>
<p><a href="http://usingomp.files.wordpress.com/2010/03/fork_join.jpg"><img title="fork_join" src="http://usingomp.files.wordpress.com/2010/03/fork_join.jpg?w=697&#038;h=203" alt="" width="697" height="203" /></a></p>
<p>A filosofia de se utilizar fork/join consiste em um thread principal, que dispara as threads que vão trabalhar em paralelo. Ao final do trabalho dessas threads em paralelo, o fluxo é reunido (join) o processamento continua de forma serial. No nosso exemplo do cálculo do Pi, essa sequência poderia ser enxergada da forma:</p>
<p>thread principal-&gt; dispara os dois loops em threads diferentes para o cálculo das somas[1,2]<br />
fork-&gt; cada thread calcula sua soma, simultaneamente<br />
join-&gt; os valores das somas são somados, resultando no valor final do meu cálculo</p>
<p>Para este exemplo simples, não fica muito complicado para o programador escrever duas rotinas de soma dentro deloops e disparar a execução dos threads, aguardar o resultado das somas e uní-los. Porém, temos alguns pequenos problemas:</p>
<ul>
<li>O código não é maleável em relação a quantidade de núcleos/processadores a serem utilizados.</li>
<li>O programador deve se encarregar de resolver todos os problemas como concorrência, deadlock, join, etc.</li>
<li>Não há como saber quantos núcleos eu tenho disponível.</li>
<li>O código não fica transparente ao programador.</li>
<li>Com o aumento do código, a manutenção da paralelismo torna-se impraticável.</li>
</ul>
<p>Como visto, usar threads diretamente resolve o problema, mas está longe de ser uma solução prática e escalável para paralelismo de códigos.</p>
<p>Para resolver esse problema, são empregadas APIs para paralelizar o código. Essas APIs nada mais fazem do que resolver para o programador quais trechos serão divididos, em quantos threads serão divididos e encarregar de enviá-los para seus respectivos núcleos/processadores. A API OpenMP é uma das soluções para se trabalhar com paralelismo de código, e foi a minha escolha para uma primeira abordagem com o assunto.</p>
</div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/54/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/54/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/54/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=54&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/09/24/o-que-e-e-como-funciona-o-paralelismo/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://usingomp.files.wordpress.com/2010/03/pi.png" medium="image">
			<media:title type="html">pi</media:title>
		</media:content>

		<media:content url="http://usingomp.files.wordpress.com/2010/03/fork_join.jpg" medium="image">
			<media:title type="html">fork_join</media:title>
		</media:content>
	</item>
		<item>
		<title>Complexidade Algorítmica na Prática</title>
		<link>http://assemblando.wordpress.com/2010/09/21/complexidade-algoritmica-na-pratica/</link>
		<comments>http://assemblando.wordpress.com/2010/09/21/complexidade-algoritmica-na-pratica/#comments</comments>
		<pubDate>Wed, 22 Sep 2010 02:26:27 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[Complexidade Algorítmica]]></category>
		<category><![CDATA[Matemática]]></category>
		<category><![CDATA[Programação]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=22</guid>
		<description><![CDATA[É quase que uma unanimidade: alunos de graduação/pós, desenvolvedores independentes e programadores em geral não se sentem atraídos pela área de estudo de complexidade de algorítmos. E, pra não perder o costume, eu sou um dos que fogem a regra e gostam de junções de matemática e programação (não que eu seja bom nisso, mas [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=22&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h3><span style="color:#ff6600;"><br />
</span></h3>
<p>É quase que uma unanimidade: alunos de graduação/pós, desenvolvedores independentes e programadores em geral não se sentem atraídos pela área de estudo de complexidade de algorítmos. E, pra não perder o costume, eu sou um dos que fogem a regra e gostam de junções de matemática e programação (não que eu seja bom nisso, mas eu gosto mesmo assim!).  Enfim, uma maneira bacana de se enxergar a matemática aplicada à análise de desempenho algorítmico é usar diferentes algoritmos que resolvam o mesmo problema, e ir aferindo o ganho no desempenho ao longo de seu desenvolvimento. Há alguns poucos dias eu estava brincando no <a href="http://www.spoj.pl/" target="_blank">SPOJ</a> quando me deparei com um problema que quase todo programador já teve que resolver uma vez: determinar a primalidade de um número. Esse exercício é interessante pois quase todo curso/apostila/livro acaba usando como exemplo.  Eu resolvi dar uma pequena modificada pra adaptar o problema à proposta deste post, então, reformulando a questão, vamos ao nosso problema:</p>
<h3>Dado um número <em>n</em>, determinar todos os números primos <em>p</em> sendo 1 ≤ <em>p </em>≤ <em>n</em></h3>
<p>Em outras palavras, nosso programa deverá encontrar todos os número primos menores ou igual a <em>n</em>. Como o nosso intuito é justamente analizar o desempenho do algorítmo em diferentes situações, nada mais natural do que começar por um método simples (e não muito eficiente, como você verá posteriormente), o <em>brute-force</em>. Neste método o nosso algorítmo irá verificar para cada elemento do nosso conjunto de entrada (um <em>array</em> de <em>n</em> números, indo de 1 até <em>n</em>) se este elemento é ou não divisível pelos elementos de valor inferior a ele no conjunto de entradas:  Seja <em>k</em> o meu conjunto de número, e supondo que desejamos encontrar todos os valores primos menores ou igual a 20 (<em>n </em>=20)<em>, </em>prosseguiriamos da seguinte maneira:  1 e 2 são primos (da definiçào de números primos) 3 não é multiplo de 2, então é primo 4 é multiplo de 2, então não é primo 5 não é multiplo de 2 ou 3 ou 4, então é primo &#8230;  E assim nosso algorítmo caminharia até o vigésimo elemento, verificando a divisibilidade de cada elemento pelos seus antecessores até 2. Em linhas gerais nosso algorítmo ficaria como o seguinte:</p>
<p><pre class="brush: plain;">
var n: int
array k: int
array _k: int
k = [i from 2 to n]
para cada elemento i de k:
   _k = [i from 2 to i]
      para cada elemento j de _k:
         se i%j==0:
            i não é primo
</pre></p>
<p>Uma implementação deste algoritmo em Python:</p>
<p><pre class="brush: python;">
def primo(v):
    k = [i+1 for i in range(v)]
    resposta = []
    for i in k:
        if i == 1 or i == 2:
            resposta.append(i)
            continue
        k_test = [j+2 for j in range(i-2)]
        v = True
        for j in k_test:
            if not i%j:
                v = False
                break
        if v: resposta.append(i)
    return resposta

if __name__==&quot;__main__&quot;:
    print primo(20)
</pre></p>
<p>Apesar de funcionar bem para faixas pequenas de valores, como <em>n</em>=20, a medida que crescemos a faixa de valores que nosso programa deve percorrer, o tempo de execução para encontrar o resultado cresce vertiginosamente. Por exemplo, para calcular todos os números primos de 1 à 10000 [dez mil] esse algoritmo gastou em minha máquina algo em torno de 15 segundos:</p>
<p><pre class="brush: plain;">
real    0m15.694s
user    0m13.536s
sys     0m0.033s
</pre></p>
<p>Para <em>n</em>=100000 o algoritmo for abortado após 20 minutos executando, e não havia ainda terminado a sua tarefa. Mas o que faz esse algorítmo ser tão ineficiente? Bem, fazendo uma análise superficial de como ele funciona, podemos ver que para cada elemento a verificar, o nosso programa faz tantas comparaçòes quanto o valor do elemento. Assim, em um exemplo, para verificarmos todos os números primos menores ou iguais a 8, fariamos a seguinte quantidade de verificações:</p>
<p>
n | comparações
2 | 0
3 | 1
4 | 2
5 | 3
6 | 4
7 | 5
8 | 6
</p>
<p>Ou seja, a quantidade total de verificações necessárias para o nosso programa determinar os números primos menores ou igual a 8 é a soma das comparações para cada elemento do meu vetor de possibilidades. Em  uma maneira mais geral, a ordem do algoritmo pode ser dada por:</p>
<p><img class="alignnone size-full wp-image-88" title="img1" src="http://assemblando.files.wordpress.com/2010/09/img1.jpeg?w=720" alt=""   />﻿﻿(1)</p>
<p>Onde :</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img2.jpeg"><img class="alignnone size-full wp-image-87" title="img2" src="http://assemblando.files.wordpress.com/2010/09/img2.jpeg?w=720" alt=""   /></a>(2)</p>
<p>Como essa série se trata de uma progressão aritmética simples, a reduzimos para:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img3.jpeg"><img class="alignnone size-full wp-image-86" title="img3" src="http://assemblando.files.wordpress.com/2010/09/img3.jpeg?w=720" alt=""   /></a>(3)</p>
<p>Que Resolvendo nos dá:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img4.jpeg"><img class="alignnone size-full wp-image-85" title="img4" src="http://assemblando.files.wordpress.com/2010/09/img4.jpeg?w=720" alt=""   /></a>(4)</p>
<p>Por fim, o que nos interessa na notação de ordem em <em>Big-Oh </em>é o termo de maior ordem, assim temos:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img5.jpeg"><img class="alignnone size-full wp-image-84" title="img5" src="http://assemblando.files.wordpress.com/2010/09/img5.jpeg?w=720" alt=""   /></a>(5)</p>
<p>O que isto pode nos dizer deste algoritmo? Bem, pra começar podemos compreender que a medida que o meu valor de <em>n</em> vai aumentando, o tempo total de processamento vai crescendo em potência de dois em relação a <em>n</em>. Ou seja, o meu tempo de processamento cresce gigantescamente mais rápido que o tamanho do meu vetor de testes. Podemos verificar o comportamento desta curva aumentando gradativamente o valor de <em>n</em> e traçando a curva <strong><em>n vs O(n)</em>:</strong></p>
<p style="text-align:center;"><a href="http://assemblando.files.wordpress.com/2010/09/tabela.jpeg"><img class="alignnone size-full wp-image-27" title="tabela" src="http://assemblando.files.wordpress.com/2010/09/tabela.jpeg?w=720" alt=""   /></a><a href="http://assemblando.files.wordpress.com/2010/09/grafico.jpeg"><img class="size-medium wp-image-28   aligncenter" title="grafico" src="http://assemblando.files.wordpress.com/2010/09/grafico.jpeg?w=300&#038;h=254" alt="" width="300" height="254" /></a></p>
<p style="text-align:left;">Como podemos perceber ao plotar o gráfico, a curva de crescimento do tempo de execução segue um comportamento conveniente à uma função quadrática. Isso nos indica que a análise da complexidade do nosso algoritmo para calcular todos número primos em uma faixa de 1 a <em>n</em> está correta.</p>
<p style="text-align:left;">Para prosseguirmos com nosso estudo em cima dos algoritmos, vamos fixar o valor de <em>n</em>=100000 [cem mil elemento]. Com esse valor, vamos fazer algumas alterações no algoritmo, mas ainda mantandeo a sua essência, e ver o quanto conseguimos melhorar o seu desempenho.</p>
<p style="text-align:left;">Para o algoritmo na forma como foi implementado acima em python, para se calcular todos os números primos de 1 até cem mil foi gasto pouco mais de <strong>42 minutos</strong> (isso mesmo, minutos!).</p>
<p style="text-align:left;">O nosso algoritmo como está descrito faz a verificação de primalidade, dividindo e verificando o resto de cada número por todos os seus antecessores.  Porém, não é preciso muita matemática para percebermos que para verificar a primalidade de um número <em>k</em> qualquer, não é necessário verificar o seu resto de divisão por números próximos ao seu valor.</p>
<p style="text-align:left;">Colocando em exemplo, imagine que desejamos calcular a primalidade do número 11. Não faz sentido eu verificar  o resto da divisão de 11 por 10, 9, 8, etc. O maior número que poderia ser um divisor inteiro de 11 é √11. Então, ao invéz de calcular a o resto da divisão por cada elemento inferior a 11, vamos calular apenas o resto da divisão de 11 por cada elemento meor ou igual a ﻿﻿√11. Perceba que com isso diminuimos a faixa de valores que o nosso algoritmo vai percorrer dentro de cada <em>loop </em>interno, vamos verificar como fica a o algoritmo e sua complexidade com essas alterações?</p>
<p><pre class="brush: python;">
import math
def primo(v):
    k = [i+1 for i in range(v)]
    resposta = []
    for i in k:
        if i == 1 or i == 2:
            resposta.append(i)
            continue
        k_test = [j+2 for j in range(int(math.sqrt(i)))]
        v = True
        for j in k_test:
            if not i%j:
                v = False
                break
        if v: resposta.append(i)
    return resposta

if __name__==&quot;__main__&quot;:
    print primo(100000)
</pre></p>
<p>Bem, com apenas essa simples alteração o tempo para calcular os primos entre 1 e cem mil caiu de 42 minutos para apenas <strong>6.33 segundos </strong>(sim leitor, segundos!).  Assim como fizemos com a primeira implementação do algoritmo, vamos novamente traçar a curva correlacionado tempo de execução e tamanho do meu vetor de entradas:</p>
<p style="text-align:center;"><a href="http://assemblando.files.wordpress.com/2010/09/linear3.jpeg"><img class="alignnone size-full wp-image-30" title="linear3" src="http://assemblando.files.wordpress.com/2010/09/linear3.jpeg?w=720" alt=""   /></a></p>
<p style="text-align:center;"><a href="http://assemblando.files.wordpress.com/2010/09/linear3.jpeg"></a><a href="http://assemblando.files.wordpress.com/2010/09/linear4.jpeg"><img class="alignnone size-medium wp-image-31" title="linear4" src="http://assemblando.files.wordpress.com/2010/09/linear4.jpeg?w=300&#038;h=266" alt="" width="300" height="266" /></a></p>
<p style="text-align:left;">Como podemos verificar no gráfico acima, o comportamento do nosso algoritmo agora se assemelha um pouco mais a uma função linear do que a uma função quadrática. Porém, só uma análize matemática pode nos dar a certeza de não estar equivocando quanto a essa conclusão [N.R. Quem leu este <em>post</em> antes da revisão vai entender que eu errei na hora de analizar matematicamente a complexidade deste algoritmo, pois troquei as bolas entre os limites do somatório e a variávei do somatório]. Podemos comprovar isso matematicamente, fazendo novamente a análise da ordem do algoritmo:</p>
<p style="text-align:left;">Da equação (1), assumimos agora que a quantidade de verificação para cada <em>n_</em>ésimo elemento muda. Agora, ao invés de nossa iteração assumir todos os valores de inferiores a <em>i, </em>o valor deste limite superior é truncado em ﻿﻿√<em>i</em>:</p>
<pre><a href="http://assemblando.files.wordpress.com/2010/09/img6.jpeg"><img class="alignnone size-full wp-image-83" title="img6" src="http://assemblando.files.wordpress.com/2010/09/img6.jpeg?w=720" alt=""   /></a>(6)</pre>
<p>Assim como anteriormente, expandindo nossa série:</p>
<pre><a href="http://assemblando.files.wordpress.com/2010/09/img7.jpeg"><img class="alignnone size-full wp-image-82" title="img7" src="http://assemblando.files.wordpress.com/2010/09/img7.jpeg?w=720" alt=""   /></a>(7)</pre>
<p>E posteriormente resolvendo como uma PA simples:</p>
<pre><a href="http://assemblando.files.wordpress.com/2010/09/img8.jpeg"><img class="alignnone size-full wp-image-81" title="img8" src="http://assemblando.files.wordpress.com/2010/09/img8.jpeg?w=720" alt=""   /></a>(8)</pre>
<p>Por fim, resolvendo a multiplicação entre os termos e tomando o elemento de maior ordem temos:</p>
<pre><a href="http://assemblando.files.wordpress.com/2010/09/img9.jpeg"><img class="alignnone size-full wp-image-80" title="img9" src="http://assemblando.files.wordpress.com/2010/09/img9.jpeg?w=720" alt=""   /></a>(9)
<span style="font-family:Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif;line-height:19px;white-space:normal;font-size:13px;">Diferente do que foi presumido, a ordem do nosso algoritmo não se torna linear, mas mesmo assim temos um ganho considerável sobre o primeiro caso, principamente para valores grandes de <em>n</em>.</span></pre>
<p style="text-align:left;">Porém a medida que crescemos muito o tamanho de <em>n</em> novamente encontramos gargalos no tempo de execução do nosso programa. Por exemplo, se tentamos calcular todos os números primos menores ou igual a <strong>1 milhões</strong>, o tempo de execução do nosso algoritmo novamente vai ficar demasiadamente grande (em torno de 4 minutos), mesmo sua complexidade sendo melhor que a anterior. Agora imagine que desejamos encontrar a resposta para <strong><em>n</em>=100 milhões.</strong> Com esse algoritmo que escrevemos, ou temos uma ótima máquina e muito tempo disponível, ou desistimos de usá-lo. Por isso podemos pensar em novas alterações para o algoritmo.</p>
<p style="text-align:left;">A primeira modificação que fez diferença no nosso código foi a de diminuir a faixa de valores que o algoritmo verificaria a possível multiplicidade de um dos números da nossa sequência. Agora vamos imaginar um panorama diferente: Assuma que vamos procurar todos os números primos entre 1 e 10. A princípio nosso <em>array</em> k de possibilidades pode ser descrito como:</p>
<pre>k = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]</pre>
<p style="text-align:left;">Assim, assumimos por definição que 1 e 2 são primos. Para determinar se outro número é ou não primo, devemos verificar a divisibilidade deste novo número pelos demais números já encontrados. Em outras palavras, sabemos que nenhum número múltiplo de 2 é primo, então poderíamos simplesmente não verificar a primalidade de todos os números múltiplos de 2. Para indicar isso, percorro minha lista a partir do elemento 2 de 2 em 2, substituindo o seu valor (que é múltiplo de 2) por um valor neutro. No caso vamos utilizar o 0 [zero]:</p>
<pre>k = [1, 2, 3, 0, 5, 0, 7, 0, 9, 0]</pre>
<p style="text-align:left;">Prosseguimos tomando o próximo número não-zero da lista e substituindo todos os seus múltiplos pelo elemento nulo. Nesta nossa segunda passagem o valor tomado é o 3:</p>
<pre>k = [1, 2, 3, 0, 5, 0, 7, 0, 0, 0]</pre>
<p style="text-align:left;">E assim prosseguimos, até o final da lista. Assim que tomarmos o último elemento da lista, teremos uma lista composta de números primos e valores nulos. Basta agora retirar todos os valores nulos e temos nossa lista de números primos:</p>
<pre>k = [1, 2, 3, 5, 7]</pre>
<p style="text-align:left;">Como no caso anterior, não é necessário fazer o teste para todos os elementos da lista. Sendo uma lista de, por exemplo, 100 valores, no pior caso, quando vamos comparar a primalidade do centésimo elemento, é trivial demonstrar que o maior valor possível de um número inteiro que multiplique outro inteiro para formar 100 é 50*2, porém os múltiplos de 2 j foram excluídos da nossa lista, então decairíamos para 25*4, porém todo múltiplo de 4 também é multiplo de 2, então decairíamos para 20*5, porém todo número múltiplo de 20 também é múltiplo de 10, o que por fim nos leva a 10*10. Como este é a nossa última possibilidade de combinação de inteiros cujo quadrado vale 100, truncamos o valor máximo que vamos tomar para efetuar as multiplicação em 10, que é √100.</p>
<p style="text-align:left;">Em outras palavras, o valor máximo assumido para fazer a multiplicação para retirar elementos do nosso vetor de possibilidades de tamanho <em>n</em> será sempre﻿ √<em>n</em> .</p>
<p style="text-align:left;">Assim, nosso algoritmo toma a seguinte forma:</p>
<p><pre class="brush: plain;">
array k: int
var j:int
k = [from 2 to n]
j = 2
enquanto j menor ou igual a raiz(n):
   retira todos os múltiplos de j do vetor
   j recebe o próximo elemento de k
</pre></p>
<p>Se você entendeu o funcionamento deste algoritmo parabéns, você acaba de aprender o <a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes">Crivo de Erastótenes</a>. Pra ser sincero, a primeira vez que cheguei nesse algorítmo por indução lógica, fiquei maravilhado com a sua simplicidade de funcionamento. Depois, quando descobri que ele já existia, fiquei num misto de frustação, por descobrir que era algo que já era mais que consolidado (<a href="http://en.wikipedia.org/wiki/Eratosthenes">Erastótenes</a> viveu entre 276a.c. e 195a.c.) e felicidade, por chegar sozinho a um algoritmo tão importante e estudado. Enfim, eu estava apenas uns 2200 anos atrasado&#8230;</p>
<p>Implementando este algoritmo em Python, com algumas pequenas melhorias:</p>
<p><pre class="brush: python;">
import math
def prime(v):
    k = range(v+1)
    i = 2
    result = []
    raiz = math.sqrt(v)
    while i &lt;= raiz:
        j = i
        if k[j] != 0:
            while j &lt;= v-1:
                j += i
                if j &lt;= v: k[j] = 0
        i += 1
    for i in k:
        if i:
            result.append(i)
    return result

if __name__ == &quot;__main__&quot;:
    prime(10000000)
</pre></p>
<p>﻿Pode parecer surpreendente a primeira vista, mas esta nova implementação resolveu os números ímpares para <em><strong>n</strong></em><strong> = 10 milhões </strong>em apenas <strong>21 segundos.<em> <span style="font-weight:normal;"><span style="font-style:normal;">Vale lembrar que na nossa primeira implementação precisamos de 12 segundos para resolver para </span>n<span style="font-style:normal;"> = 10 mil, e como o algorítimo era quadrático, abortamos o teste da tentativa de chegar a </span>n<span style="font-style:normal;"> = 100 mil aos 20 minutos, sem sucesso. </span></span></em></strong></p>
<p><strong><em><span style="font-weight:normal;"><span style="font-style:normal;">Com os mesmos 21 segundos necessários para se calcular os primos menores ou igual a 10 milhões, na nossa segunda implementação, conseguimos alcançar </span>n<span style="font-style:normal;">=250 mil elementos. Ou seja, não há dúvidas que esta implementação é muito superior em questão de desempenho que as anteriores, mas a pergunta é: como quantificar esse ganho de desempenho? A análise da complexidade deste algoritmo é um pouco mais complexa, por isso vamos por partes:</span></span></em></strong></p>
<p><strong><em><span style="font-weight:normal;"><span style="font-style:normal;">Primeiro, vamos aferir empiricamente o seu comportamento. Como já fizemos outras duas vezes vamos executar o programa para diferentes valores de </span>n<span style="font-style:normal;">:</span></span></em></strong></p>
<p style="text-align:center;"><strong><em><span style="font-weight:normal;"><span style="font-style:normal;"><a href="http://assemblando.files.wordpress.com/2010/09/linear5.jpeg"><img class="alignnone size-full wp-image-35" title="linear5" src="http://assemblando.files.wordpress.com/2010/09/linear5.jpeg?w=720" alt=""   /></a></span></span></em></strong></p>
<p style="text-align:center;"><strong><em><span style="font-weight:normal;"><span style="font-style:normal;"><a href="http://assemblando.files.wordpress.com/2010/09/linear6.jpeg"><img class="alignnone size-medium wp-image-36" title="linear6" src="http://assemblando.files.wordpress.com/2010/09/linear6.jpeg?w=300&#038;h=254" alt="" width="300" height="254" /></a></span></span></em></strong></p>
<p style="text-align:left;"><strong><em><span style="font-weight:normal;"><span style="font-style:normal;">A princípio o resultado da relação tempo vs tamanho parece ser linear, então vamos verificar realmente se este é linear através de uma análise diretamente no algoritmo.</span></span></em></strong></p>
<p style="text-align:left;"><strong><em><span style="font-weight:normal;"><span style="font-style:normal;">Tomemos como referência um a execução do algoritmo para um </span>n<span style="font-style:normal;">=100. Vamos verificar manualmente, em caa passagem, quantas comparações é feita:</span></span></em></strong></p>
<p style="text-align:left;">Na primeira passagem temos nosso vetor completo:</p>
<pre>K= [1, 2, 3, 4, 5, 6, 7, 8, 9 ... 99, 100]</pre>
<p style="text-align:left;">Para eleminar todos os elementos múltiplos de 2 fazemos 50 inferências, ou seja 100/2</p>
<pre>passo 1 = 100/2</pre>
<p style="text-align:left;">Para eliminarmos agora os múltiplos de 3 fazemos 100/3 inferências:</p>
<pre>passo 2 = 100/3</pre>
<p style="text-align:left;">O terceiro passo a princípio deveria ser a retirada dos múltiplos de 4. Porém, como todo múltiplo de 4 também é múltiplo de 3, essa passo se torna desnecessário. Então seguimos para o próximo da lista, ou seja, retirarmos os múltiplos de 5:</p>
<pre>passo 3 = 100/5</pre>
<p style="text-align:left;">E, por consequência:</p>
<pre>passo 4 = 100/7</pre>
<p>O próximo elemento da lista seria o 11, porém este é maior que a √100, então paramos nossa verificação aqui.</p>
<p>Assim, para encontrarmos o resultado para <em>n</em>=100 executamos a seguinte quantidade de passos:</p>
<p><img class="alignnone size-full wp-image-79" title="img10" src="http://assemblando.files.wordpress.com/2010/09/img10.jpeg?w=720" alt=""   />(10)</p>
<p>Generalizando a expressão acima, temos que o numerador é sempre o tamanho <em>n</em> do meu vetor de possibilidades, e o denominador é o meu <em>p_ésimo </em>número primo. Temos também que o último elemento é √<em>n</em>_ésimo elemento:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img11.jpeg"><img class="alignnone size-full wp-image-78" title="img11" src="http://assemblando.files.wordpress.com/2010/09/img11.jpeg?w=720" alt=""   /></a>(11)</p>
<p>Apenas colocando <em>n</em> em evidência, temos:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img12.jpeg"><img class="alignnone size-full wp-image-77" title="img12" src="http://assemblando.files.wordpress.com/2010/09/img12.jpeg?w=720" alt=""   /></a>(12)</p>
<p>A essa série de somas de frações dos números primos é dado o nome de <a href="http://en.wikipedia.org/wiki/Prime_harmonic_series">Série Harmônica dos Inversos dos Primos</a>. Assim temos que essa soma pode ser dado pelo somatório para todo <em>p</em> primo menor ou igual a √n:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img13.jpeg"><img class="alignnone size-full wp-image-76" title="img13" src="http://assemblando.files.wordpress.com/2010/09/img13.jpeg?w=720" alt=""   /></a>(13)</p>
<p>Desta série temos:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img14.jpeg"><img class="alignnone size-full wp-image-75" title="img14" src="http://assemblando.files.wordpress.com/2010/09/img14.jpeg?w=720" alt=""   /></a>(14)</p>
<p>Por fim, tomando o elemento de maior ordem temos:</p>
<p><a href="http://assemblando.files.wordpress.com/2010/09/img15.jpeg"><img class="alignnone size-full wp-image-74" title="img15" src="http://assemblando.files.wordpress.com/2010/09/img15.jpeg?w=720" alt=""   /></a>(15)</p>
<p>Com isso provamos que a ordem do algoritmo é, ao contrário do que presumimos a partir do gráfico traçado, não linear.</p>
<p><a href="http://gist.github.com/591005">Aqui</a> eu disponibilizo o código do mesmo algoritmo escrito em linguagem C. Este codigo demorou no meu computador 2.4 segundos para calcular todos números primos para <em>n</em>=10 milhões (contra 19 segundos do mesmo algoritmo escrito em Python). Vale ressaltar que se vc retirar o <em>printf</em> que imprime os valores e contabilizar o apenas o tempo de encontrar a resposta, o tempo de execução cai abaixo de 1 segundo. Fica para quem quiser testar, ver quanto tempo demora para achar os primos para <em>n=1 bilhão.</em></p>
<p>Para caráter de comparação, o meu computador que usei para rodar os testes foi:</p>
<pre>Sony Vaio
Processador: Pentium M 1.73GHz, Centrino, 1 núcleo, 32 bits
1GB de memória RAM
SO: Arch Linux 32 bits</pre>
<p style="text-align:center;">
<p style="text-align:left;">
<p style="text-align:center;"><strong><em><span style="font-weight:normal;"><span style="font-style:normal;"><br />
</span></span></em></strong></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/22/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/22/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/22/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=22&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/09/21/complexidade-algoritmica-na-pratica/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img1.jpeg" medium="image">
			<media:title type="html">img1</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img2.jpeg" medium="image">
			<media:title type="html">img2</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img3.jpeg" medium="image">
			<media:title type="html">img3</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img4.jpeg" medium="image">
			<media:title type="html">img4</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img5.jpeg" medium="image">
			<media:title type="html">img5</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/tabela.jpeg" medium="image">
			<media:title type="html">tabela</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/grafico.jpeg?w=300" medium="image">
			<media:title type="html">grafico</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/linear3.jpeg" medium="image">
			<media:title type="html">linear3</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/linear4.jpeg?w=300" medium="image">
			<media:title type="html">linear4</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img6.jpeg" medium="image">
			<media:title type="html">img6</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img7.jpeg" medium="image">
			<media:title type="html">img7</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img8.jpeg" medium="image">
			<media:title type="html">img8</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img9.jpeg" medium="image">
			<media:title type="html">img9</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/linear5.jpeg" medium="image">
			<media:title type="html">linear5</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/linear6.jpeg?w=300" medium="image">
			<media:title type="html">linear6</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img10.jpeg" medium="image">
			<media:title type="html">img10</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img11.jpeg" medium="image">
			<media:title type="html">img11</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img12.jpeg" medium="image">
			<media:title type="html">img12</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img13.jpeg" medium="image">
			<media:title type="html">img13</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img14.jpeg" medium="image">
			<media:title type="html">img14</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/09/img15.jpeg" medium="image">
			<media:title type="html">img15</media:title>
		</media:content>
	</item>
		<item>
		<title>Tecnologias que uso &#8211; A gênesis</title>
		<link>http://assemblando.wordpress.com/2010/03/30/tecnologias-que-uso-a-genesis/</link>
		<comments>http://assemblando.wordpress.com/2010/03/30/tecnologias-que-uso-a-genesis/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 01:51:28 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[blablabla]]></category>
		<category><![CDATA[Tecnologia]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=6</guid>
		<description><![CDATA[Comecei a me enfiar no mundo da informática por volta de 1992 (baseado em algumas interpolações como corridas do Senna e o fato de eu comprar pão com notas de Cruzeiro).  Comecei com um IBM PC-AT, que sequer disco rígido ou sistema operacional tinha. Não lembro de ter feito algo de útil nesse computador, no [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=6&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Comecei a me enfiar no mundo da informática por volta de 1992 (baseado em algumas interpolações como corridas do Senna e o fato de eu comprar pão com notas de Cruzeiro).  Comecei com um IBM PC-AT, que sequer disco rígido ou sistema operacional tinha. Não lembro de ter feito algo de útil nesse computador, no máximo jogar  Pacman ou brincar com o editor de texto (Lotus?). Infelizmente este não durou muito na minha mão, e hoje não me resta sequer o gabinete.</p>
<div id="attachment_9" class="wp-caption aligncenter" style="width: 310px"><a href="http://assemblando.files.wordpress.com/2010/03/800px-ibm_pc_5150.jpg"><img class="size-medium wp-image-9  " title="IBM PC-AT" src="http://assemblando.files.wordpress.com/2010/03/800px-ibm_pc_5150.jpg?w=300&#038;h=216" alt="" width="300" height="216" /></a><p class="wp-caption-text">IBM PC-AT - Compudar com o qual eu tive meu primeiro contato com o mundo da informática</p></div>
<p>Logo em seguida comecei a usar um poderosíssimo IBM 286, equipado com um disco rígido de 250MB e rodando o fantástico MS-DOS 6.22. Hoje em dia não uso mais os sistemas da Microssoft, mas devo muito do meu aprendizado ao MS-DOS. Escrever arquivos de lote (.bat, lembra?) era comigo mesmo. Formatar disquete, jogar Prince Of Persia, brincar no Star Print&#8230; como eu me divertia com um computador que sem internet, ou melhor, sem interface gráfica! Enfim, o computador era aquilo, e ponto final. Minha maior decepção com aquele 286 foi quando descobri que não poderia rodar o jogo da <a title="Tv Colosso" href="http://pt.wikipedia.org/wiki/TV_Colosso" target="_blank">Tv Colosso</a> (lembra? Priscila, capaxão, Jota Efe&#8230;). O motivo? precisava de uma placa gráfica e um mouse (sim, eu nunca havia usado mouse até então).</p>
<p>Bem, o próximo passo foi um computador que marcou época para mim: UIS-PC 486 (UIS era uma cópia dos IBM PC, assim como muitos outros). Enfim eu estava alinhado ao topo da tecnologia (pelo menos a disponível no Brasil). Era um computador com 16MB de memória RAM e, pirem, 900MB de disco rígido!! Imagina lotar 900MB numa época em que os Sistemas Operacionais eram vendidos em disquetes e internet era algo que vc leu numa revista, e que, um dia, vc vai pra Fenasoft ver como funciona!</p>
<p>Com esse 486, além de finalmente conseguir jogar Tv Colosso e ver pela primeira vez uma interface colorida, eu vi algo que me deixou maravilhado. Esse algo atendia pelo nome de Windows 3.10, e era a maior maravilha que eu já havia visto até então! Paintbrush, navegador de diretórios gráfico (sim, diretórios, o termo pasta só viria com o Windows 95), joguinhos como Sokoban, Duke Nuken, DOOM, Prince of Persia (dessa vez colorido) entre outros.</p>
<div id="attachment_12" class="wp-caption aligncenter" style="width: 310px"><a href="http://assemblando.files.wordpress.com/2010/03/dukenukem1.png"><img class="size-medium wp-image-12" title="Dukenukem1" src="http://assemblando.files.wordpress.com/2010/03/dukenukem1.png?w=300&#038;h=187" alt="" width="300" height="187" /></a><p class="wp-caption-text">Tela Colorida!! Uma das grande novidades do meu UIS 486</p></div>
<p>Foi nessa época que eu frequentei meu primeiro curso de informática, [re]aprendi a usar o DOS (já era tão íntimo, que omitia metade do nome), usar o edit, mudar o prompt de comandos. Conheci lá o Excel, o Access, Label, vi uma impressora colorida (HP580c), descobri que não precisaria de um <a href="http://pt.wikipedia.org/wiki/Zip_drive" target="_blank">Zip Drive</a> para transferir um arquivo maior que 1,4MB (usando compactação fracionada), dentre outras coisas. Hoje quase extintos, os cursos de informática no começo da década de 90 eram um frenesi sem tamanho. &#8220;Se você não faz um curso de inglês e um de informática, você é analfabeto&#8221;, diziam então. Conheci pessoas que foram influentes na minha escolha pela informática. Enfim, estava definitivamente no trilho da Informação Automática.</p>
<p>O 486 me trouxe para um mundo que era totalmente novo, mas isso não chegava nem perto da maior descoberta da minha vida geek até então (estavamos aí por volta de 1995). Em um dia de estudo na biblioteca municiapl da minha cidade (não, não haviam PCs lá em 1995), eu me deparei com um livro de eletricidade voltado para o público infantil. Lei de Ohm, circuito de chave e lâmpada, resistor, etc.. Sempre tive uma atração por ciências, principalmente eletrônica, mas foi algo no fim do livro que me chamou a atenção: uma série de comandos que pareciam uma receita de como calcular a corrente em cima de um resistor. Algumas palavras eram conhecidas por mim (DO, PRINT, IF&#8230;). Nunca havia visto programação aantes, no máximo scripts de lote no DOS, mas eu não tive dúvida, sabia que aquilo era <strong>UM PROGRAMA DE COMPUTADOR!!!</strong></p>
<p>Procurei ajuda pra saber como colocar aquilo para funcionar (acredite, em 1995 e morando numa cidade de 8 mil habitantes, foi um parto conseguir alguém que pudesse me dar um norte). Enfim, um conhecido do meu professor de informática me apresentou um &#8216;programinha&#8217; muito parecido com o Edit, mas que tinha a fonte de outra  cor. Esse programa era o<a href="http://en.wikipedia.org/wiki/QBasic" target="_blank"> QBasic</a>, que vinha junto ao MS-DOS. Enfim eu era apresentado ao meu primeiro compilador e à minha <a href="http://pt.wikipedia.org/wiki/Basic" target="_blank">primeira linguagem de programação</a>. Daí pra frente, tudo que eu aprendia de matemática virava programa de computador. Fórmula de Báskhara, regra de três, cálculo do IMC&#8230; Tudo virava &#8216;software&#8217;.</p>
<p>Depois desse 486 eu tive alguns outros computadores (duron, atlon&#8230;). Hoje uso um vaio de 2006, mas que rodando um bom <a href="http://wiki.archlinux.org/index.php/Main_Page" target="_blank">Arch Linux</a> deixa muitos Quad Core no chinelo. Tenho um celular hoje com muito mais capacidade de processamento que aquele meu 486, e com mais que o dobro de capacidade de armazenamento, mas pra mim aquele 486 foi um marco, algo que mudou minha vida, sem demagogia ou pieguisse.</p>
<p>Desde aquele programa em Basic pra calcular a corrente em um resistor tive contato com muitas outras linguagens de programação e script (C, assembly,  Java Script,  C++, Java, Visual Basic, Python, Ada, e até Fortran, que comecei a estudar exatamente hoje). Mas mesmo não escrevendo mais nada nessa linguagem, Basic é uma das minhas favoritas. Não pela capacidade, mas sim por gratidão.</p>
<p>E assim se constrói um programador, com curiosidade e vontade de aprender. Daí pra encarar a engenharia foi um pulo, mas muita coisa interessante acontecendo.</p>
<p>Mas como agora tenho internet para pesquisar, e uma boa GUI para fazer um front-end, deixe-me voltar ao trabalho, porque muita linha de código há de passar pelo compilador ainda&#8230;</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/6/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/6/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/6/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=6&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/03/30/tecnologias-que-uso-a-genesis/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/03/800px-ibm_pc_5150.jpg?w=300" medium="image">
			<media:title type="html">IBM PC-AT</media:title>
		</media:content>

		<media:content url="http://assemblando.files.wordpress.com/2010/03/dukenukem1.png?w=300" medium="image">
			<media:title type="html">Dukenukem1</media:title>
		</media:content>
	</item>
		<item>
		<title>Bom dia flor do dia</title>
		<link>http://assemblando.wordpress.com/2010/03/30/bom-dia-flor-do-dia/</link>
		<comments>http://assemblando.wordpress.com/2010/03/30/bom-dia-flor-do-dia/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 00:39:43 +0000</pubDate>
		<dc:creator>alvesjnr</dc:creator>
				<category><![CDATA[blablabla]]></category>

		<guid isPermaLink="false">http://assemblando.wordpress.com/?p=4</guid>
		<description><![CDATA[Primeiro post de blog novo, o mais natural seria eu me apresentar e blablabla, mas&#8230; acho que isso não interessa muito, não é? A idéia desse blog é compilar informações sobre TI, engenharia (elétrica e da computação) e, lógico, programação. Colocar no papel (ePaper) um pouco do que eu absorvo no meu cotidiano de engenheiro [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=4&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Primeiro post de blog novo, o mais natural seria eu me apresentar e blablabla, mas&#8230; acho que isso não interessa muito, não é?</p>
<p>A idéia desse blog é compilar informações sobre TI, engenharia (elétrica e da computação) e, lógico, programação. Colocar no papel (ePaper) um pouco do que eu absorvo no meu cotidiano de engenheiro e estudante.</p>
<p>Espero estar atualizando constantemente esse espaço, que eu não caia no auto-esquecimento!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/assemblando.wordpress.com/4/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/assemblando.wordpress.com/4/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/assemblando.wordpress.com/4/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=assemblando.wordpress.com&amp;blog=12870459&amp;post=4&amp;subd=assemblando&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://assemblando.wordpress.com/2010/03/30/bom-dia-flor-do-dia/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://1.gravatar.com/avatar/ba1c522466fec0845bee447874c91c37?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">alvesjnr</media:title>
		</media:content>
	</item>
	</channel>
</rss>
