<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-6306509703738480474</id><updated>2008-07-02T18:31:12.330-03:00</updated><title type='text'>Brain Dump</title><link rel='alternate' type='text/html' href='http://www.ricbit.com/'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.ricbit.com/atom.xml'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>18</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-2158992859861495050</id><published>2008-06-23T23:37:00.007-03:00</published><updated>2008-06-24T09:25:52.092-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='livros'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><title type='text'>Como aprender computação</title><content type='html'>Dia desses o &lt;a href="http://lameiro.wordpress.com/"&gt;Lameiro&lt;/a&gt; me passou uma &lt;a href="http://blog.fragmental.com.br/2008/05/20/trilha-de-livros-desenvolvedor/"&gt;lista de livros para desenvolvedores&lt;/a&gt;, e eu achei a lista bastante curiosa. Se eu fosse criar uma similar, eu não colocaria nenhum dos livros citados naquela lista! Ao invés disso, a minha lista com os top 10 livros de computação seria a abaixo:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/livros-716370.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/livros-716366.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;1 - &lt;a href="http://books.google.com/books?id=aFcsnUEewLkC&amp;amp;dq=godel+escher+bach&amp;amp;ei=6m1gSMGPIYHAigHU0fmXDA"&gt;Gödel, Escher, Bach&lt;/a&gt; (Hofstadter): Eu começaria com o GEB, por dois motivos. O primeiro é que ele é um ótimo reality check: se você não gostar do GEB, então mude de área, porque computação não é a sua praia :) O segundo motivo é que esse livro tem uma excelente introdução à lógica (&lt;a href="http://en.wikipedia.org/wiki/Propositional_logic"&gt;proposicional&lt;/a&gt; e &lt;a href="http://en.wikipedia.org/wiki/First-order_logic"&gt;de predicados&lt;/a&gt;), que é a ferramenta básica onde você constrói a ciência da computação.&lt;br /&gt;&lt;br /&gt;2 - &lt;a href="http://books.google.com/books?id=pntQAAAAMAAJ&amp;amp;q=concrete+mathematics&amp;amp;dq=concrete+mathematics&amp;amp;source=gbs_book_other_versions_r&amp;amp;cad=2_0&amp;amp;pgis=1"&gt;Concrete Mathematics&lt;/a&gt; (Knuth): Você não precisa saber matemática para programar, mas se você quiser ser um &lt;span style="font-style: italic;"&gt;bom&lt;/span&gt; programador, então matemática é essencial. O Concrete tem todo o básico que você precisa pra fazer análises de complexidade computacional, e tudo escrito de maneira extremamente bem-humorada.&lt;br /&gt;&lt;br /&gt;3 - &lt;a href="http://books.google.com/books?id=zMEBAAAACAAJ&amp;amp;dq=algorithms+in+c%2B%2B&amp;amp;ei=xXNgSLn4Aoa4jgG-lLGJDA"&gt;Algorithms in C++&lt;/a&gt; (Sedgewick): Se você já sabe lógica e matemática, então agora pode partir pro estudo de algoritmos. O Sedgewick tem todos os algoritmos básicos, e é uma leitura bem leve: se você está começando com algoritmos agora, esse precisa ser o seu primeiro livro. Ele não vai muito fundo em nenhum tópico, mas isso é compensado pela extrema didática nos tópicos. O livro ainda tem código exemplo pra todos os algoritmos, e várias edições, uma pra cada linguagem (eu sei que tem, pelo menos, C, C++, Java e Pascal).&lt;br /&gt;&lt;br /&gt;4 - &lt;a href="http://books.google.com/books?id=NLngYyWFl_YC&amp;amp;dq=introduction+to+algorithms&amp;amp;source=gbs_summary_s&amp;amp;cad=0"&gt;Introduction to Algorithms&lt;/a&gt; (Cormen): Os algoritmos que você aprendeu no Sedgewick, você vai estudar em detalhes no Cormen. Esse livro é extremamente formal, e talvez por isso é o livro-texto usado nos cursos de computaçao do &lt;a href="http://web.mit.edu/"&gt;MIT&lt;/a&gt;. Ele também cobre algoritmos mais avançados, que o Sedgewick apenas cita (por exemplo, &lt;a href="http://en.wikipedia.org/wiki/Fibonacci_heap"&gt;Fibonacci Heap&lt;/a&gt;)&lt;span style="text-decoration: underline;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;5 - &lt;a href="http://books.google.com/books?id=QDh9AAAACAAJ&amp;amp;dq=art+of+computer+programming&amp;amp;source=gbs_book_other_versions_r&amp;amp;cad=0_2"&gt;The &lt;/a&gt;&lt;a href="http://books.google.com/books?id=QDh9AAAACAAJ&amp;amp;dq=art+of+computer+programming&amp;amp;source=gbs_book_other_versions_r&amp;amp;cad=0_2"&gt;Art&lt;/a&gt;&lt;a href="http://books.google.com/books?id=QDh9AAAACAAJ&amp;amp;dq=art+of+computer+programming&amp;amp;source=gbs_book_other_versions_r&amp;amp;cad=0_2"&gt; of Computer Programming&lt;/a&gt; (Knuth): O tAoCP está para o Cormen assim como o Cormen está pro Segdewick, aqui você vai dissecar os algoritmos até o último bit deles. Além de ser uma coleção excelente, a encadernação é muito bonita (mas não se engane, ele fica ótimo na prateleira, mas melhor ainda na sua cabeça).&lt;br /&gt;&lt;br /&gt;6 - &lt;a href="http://books.google.com/books?id=QLsqAAAACAAJ&amp;amp;dq=effective+C%2B%2B&amp;amp;ei=VnZgSPjDNabQigG90fSLDA"&gt;Effective C++&lt;/a&gt; (Meyers): Agora que você sabe os algoritmos, precisa de uma linguagem para programá-los. Pra falar a verdade, a escolha de linguagem nem importa tanto assim, mas se você escolher uma, aprenda-a tão bem quanto possível. Eu escolhi C++, e esse livro do Meyers é o que diferencia as crianças dos adultos (especialmente para aquelas horas quando você cria uma classe sem destrutor virtual e não sabe por que a memória está vazando).&lt;br /&gt;&lt;br /&gt;7 - &lt;a href="http://books.google.com/books?id=h2vbDpVhTCAC&amp;amp;q=effective+stl&amp;amp;dq=effective+stl&amp;amp;ei=ZXdgSOvCBI2ujAHwl_ioBA&amp;amp;pgis=1"&gt;Effective STL&lt;/a&gt; (Meyers): Já teve uma época em que eu não gostava de C++, mas isso é porque eu sou velho o suficiente pra ter mexido em C++ antes que os compiladores tivessem templates. Com templates a linguagem fica muito mais atraente, e esse é o livro que vai te ensinar a dominar a &lt;a href="http://www.sgi.com/tech/stl/stl_introduction.html"&gt;STL&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;8 - &lt;a href="http://books.google.com/books?id=to6M9_dbjosC&amp;amp;dq=practice+of+programming&amp;amp;source=gbs_summary_s&amp;amp;cad=0"&gt;The Practice of Programming&lt;/a&gt; (Kernighan, Pike): Se você leu tudo até agora, então você já é um programador muito bom na teoria. Na prática, entretanto, tem um monte de skills que ainda faltam. Nesse livro você aprende sobre as coisas que usualmente não se aprende na escola: debugging, otimização, unit testing, documentação.&lt;br /&gt;&lt;br /&gt;9 - &lt;a href="http://books.google.com/books?id=kse_7qbWbjsC&amp;amp;dq=programming+pearls&amp;amp;source=gbs_summary_s&amp;amp;cad=0"&gt;Programming Pearls&lt;/a&gt; (Bentley): Com os livros lidos até agora, você já deve ser um excelente programador. O passo final é passar de programador para um true hacker, e esse é um passo que não requer só conhecimento, você também precisa de manha, criatividade e insight. Eu não sei se dá pra ensinar essas coisas, mas esse livro certamente é o que chega mais próximo disso.&lt;br /&gt;&lt;br /&gt;10 - &lt;a href="http://books.google.com/books?id=K47ZAAAACAAJ&amp;amp;dq=mythical+man-month&amp;amp;ei=YH9gSNr_BZiijgGHrYCODA"&gt;The Mythical Man-Month&lt;/a&gt; (Brooks): Depois de ler todos os livros acima você estará próximo do nirvana, mas pra atingir o zen da programação de verdade, é preciso lembrar que projetos não precisam só de computadores, precisam de pessoas também. O Mythical Man-Month é um livro de gerência de projetos de software escrito em 1975, mas é surpreendente como ele continua atual. A tecnologia avança, mas as pessoas continuam as mesmas :)&lt;br /&gt;&lt;br /&gt;É claro que pra manter uma lista com só dez itens, muita coisa boa fica de fora. Mas a lista acima tem um mérito: foi com esses livros que eu aprendi computação de verdade (vale lembrar que eu sou autodidata, minha graduação foi em engenharia elétrica, e eu quase não tive computação em aulas). Se funcionou pra mim, pode ser que funcione pra você também :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/06/como-aprender-computao.html' title='Como aprender computação'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=2158992859861495050' title='12 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/2158992859861495050'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/2158992859861495050'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-2094645274186264547</id><published>2008-06-18T00:02:00.005-03:00</published><updated>2008-06-18T03:24:08.867-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='retro'/><category scheme='http://www.blogger.com/atom/ns#' term='puzzle'/><category scheme='http://www.blogger.com/atom/ns#' term='monte carlo'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><title type='text'>Um cientista em minha vida</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/cientvida-733243.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/cientvida-733241.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Eu li lá no &lt;a href="http://100nexos.com/arquivo/608"&gt;blog do Kentaro&lt;/a&gt; que o meme da semana era "Um Cientista em minha vida", onde deveríamos falar sobre algum cientista que fez a diferença pra você. Como eu adoro &lt;a href="http://en.wikipedia.org/wiki/Constrained_writing"&gt;constrained writing&lt;/a&gt;, resolvi participar (na verdade, eu adoro constrained anything, por isso que vivo criando programas em uma linha, programas que rodam em computadores de 8 bits, e assim por diante).&lt;br /&gt;&lt;br /&gt;Eu já falo de cientistas aqui todo o tempo. Olhando no histórico, eu já falei do &lt;a href="http://www.ricbit.com/2008/04/domino-dancing.html"&gt;Knuth&lt;/a&gt;, do &lt;a href="http://www.ricbit.com/2008/04/erds-e-os-logaritmos.html"&gt;Erdös&lt;/a&gt;, do &lt;a href="http://www.ricbit.com/2008/04/meta-assinatura.html"&gt;prof. Routo&lt;/a&gt;, do &lt;a href="http://www.ricbit.com/2008/05/ao-infinito-e-alm.html"&gt;prof. Henrique&lt;/a&gt;, e de vários outros. Em comum, todos eles foram cientistas que eu conheci depois de adulto. Achei apropriado então que eu falasse de um cientista que fez a diferença quando eu era criança, e pra isso vamos ter que rebobinar até a década de 80.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/microhobby-723153.gif"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/microhobby-723151.gif" alt="" border="0" /&gt;&lt;/a&gt;Se você perguntar pra alguém sobre revistas de computador na década de 80, invariavelmente irá ouvir sobre a &lt;a href="http://www.tilt.net/wiki/index.php/Micro_Sistemas"&gt;Micro Sistemas&lt;/a&gt; ("a primeira revista brasileira sobre microcomputadores"). A Micro Sistemas era muito legal, mas o que eu gostava mesmo era de outra revista, menos conhecida, chamada Microhobby.&lt;br /&gt;&lt;br /&gt;A diferença da Micro Sistemas pra Microhobby era mais ou menos a diferença de Informática pra Computação. Na primeira, nós ficávamos encantados com as notícias da maravilhosa terra além da &lt;a href="http://pt.wikipedia.org/wiki/Pol%C3%ADtica_Nacional_de_Inform%C3%A1tica"&gt;reserva de mercado&lt;/a&gt; (onde aprendíamos que a Apple planejava lançar um novo computador chamado &lt;a href="http://en.wikipedia.org/wiki/McIntosh"&gt;McIntosh&lt;/a&gt;, que vinha com um periférico estranho e esquisito chamado &lt;a href="http://en.wikipedia.org/wiki/Computer_mouse"&gt;mouse&lt;/a&gt;), enquanto que na segunda aprendíamos a calcular &lt;a href="http://en.wikipedia.org/wiki/Geodesic"&gt;geodésicas&lt;/a&gt; e a usar o &lt;a href="http://en.wikipedia.org/wiki/Bisection_method"&gt;método de Bolzano&lt;/a&gt; para achar raízes de uma equação.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/RenatodaSilvaOliveira2-792075.JPG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/RenatodaSilvaOliveira2-792072.JPG" alt="" border="0" /&gt;&lt;/a&gt;Mas o diferencial mesmo da Microhobby eram as colunas escritas pelo Renato da Silva Oliveira. Uma googlada rápida revela que o Renato é formado em Física, trabalhou nos planetários de São Paulo, Campinas, Vitória e Tatuí, e atualmente trabalha em uma empresa que vende &lt;a href="http://www.asterdomus.com.br/AsterDomus_0.htm"&gt;planetários infláveis&lt;/a&gt; (how cool is that?!). Mas é claro que eu não sabia disso na época, o que eu sabia era que ele contava historinhas!&lt;br /&gt;&lt;br /&gt;Foi lendo as historinhas do Renato que eu descobri que era possível escrever sobre ciência e computação, com clareza e bom humor. Pena que isso ainda não é muito difundido, a julgar pela quantidade de crianças que ainda acham que ciência é uma coisa chata :(&lt;br /&gt;&lt;br /&gt;As historinhas que ele escrevia sempre tinham o mesmo formato: um certo sr. Nabor Rosenthal, em suas viagens pelo mundo, deparava-se com alguma situação que sugeria uma análise matemática (os tópicos eram os mais variados e iam de &lt;a href="http://en.wikipedia.org/wiki/Graph_theory"&gt;teoria dos grafos&lt;/a&gt; até &lt;a href="http://en.wikipedia.org/wiki/Arecibo_Observatory"&gt;contatos com extraterrestres&lt;/a&gt;). Depois de ponderar sobre o problema sem conseguir resolvê-lo, o Nabor tomava uma dose do raro &lt;a href="http://en.wikipedia.org/wiki/Srinivasa_Ramanujan"&gt;Suco de Ramanujan&lt;/a&gt;, que o colocava num transe que ampliava suas capacidades analíticas, e conseguia solucionar o problema.&lt;br /&gt;&lt;br /&gt;Mas a coluna sempre acabava antes que o Nabor mostrasse qual a solução! Ao invés disso, o leitor tinha um mês pra conseguir resolver o problema, e só no mês seguinte a solução era apresentada. Na década de 80 ainda não tinha &lt;a href="http://www.spoj.pl/"&gt;spoj&lt;/a&gt;, então as colunas do Renato eram o que bombava pra quem gostava de puzzles computacionais.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/tk82c_ps_1g-793058.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/tk82c_ps_1g-793050.jpg" alt="" border="0" /&gt;&lt;/a&gt;Um dos puzzles apresentados foi o segundo puzzle mais difícil da minha vida, eu levei mais de dez anos pra conseguir resolver. Em uma das Microhobby, o Nabor entrou em transe após tomar o Suco de Ramanujan, e durante o transe ele sonhou "com um método para calcular o número pi, usando apenas o gerador de números aleatórios de seu micro" (essa era a época onde o micro mais avançado era o &lt;a href="http://pt.wikipedia.org/wiki/TK82C"&gt;TK-82C&lt;/a&gt;, com 2kb de RAM).&lt;br /&gt;&lt;br /&gt;Na época eu pensei muito e não consegui solucionar, achando que ia precisar de alguma matemática que eu ainda não tinha aprendido. Eu nunca consegui achar a revista seguinte com a solução, tive que passar pelo primário, pelo colégio técnico, e só no meio da faculdade é que caiu a ficha (e eu percebi que poderia ter solucionado ainda no primário, se tivesse insistido o suficiente :)&lt;br /&gt;&lt;br /&gt;O truque é o seguinte: você vai fazer N experimentos, cada um consistindo no sorteio de dois números aleatórios escolhidos uniformemente entre 0 e 1. Se soma dos quadrados dos números for menor ou igual a 1, incremente um contador (digamos, M). Ao final dos experimentos, pi=4*M/N. O script abaixo implementa esse algoritmo:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/pi.py"&gt;Script em python para calcular pi usando números aleatórios&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/circle-768800.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/circle-768786.png" alt="" border="0" /&gt;&lt;/a&gt;O funcionamento é bem simples e baseia-se na figura ao lado. Você começa inscrevendo um quarto de círculo num quadrado de lado 1. Os dois números que você sorteia a cada iteração podem ser interpretados como um ponto dentro do quadrado, e o teste feito é equivalente a testar se o ponto está dentro do círculo ou não. Como a distribuição dos pontos é uniforme, espera-se que a razão M/N seja igual à razão entre as áreas da figura. A área do quadrado é 1, a área do círculo é pi*r&lt;sup&gt;2&lt;/sup&gt;. Como o raio é unitário, então a área do quarto de círculo é pi/4. Isolando pi, chega-se em pi=4*M/N, QED.&lt;br /&gt;&lt;br /&gt;A pergunta que deve ser feita ao encontrar qualquer algoritmo novo é: qual é sua complexidade? Infelizmente, esse método aleatório é bem ruim. No fundo, o que estamos fazendo é aproximar pi por uma fração, cujo denominador é N. Então a precisão máxima que podemos obter é 1/N, e se você quer calcular n dígitos de pi, esse método converge, no melhor caso, em O(10&lt;sup&gt;n&lt;/sup&gt;), e na prática em bem menos que isso, porque os seus geradores de números aleatórios não são perfeitamente uniformes.&lt;br /&gt;&lt;br /&gt;Eu nunca soube qual o método que o Nabor usou pra calcular o pi. Como ele tinha o Suco de Ramanujan e eu não, espero que tenha sido um método melhor que o meu :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/06/um-cientista-em-minha-vida.html' title='Um cientista em minha vida'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=2094645274186264547' title='9 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/2094645274186264547'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/2094645274186264547'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-843067646841264017</id><published>2008-06-11T02:16:00.009-03:00</published><updated>2008-06-14T01:58:49.232-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='fibonacci'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='binário'/><title type='text'>Programa de milhagem</title><content type='html'>Olhando no &lt;a href="http://www.google.com/analytics/"&gt;Google Analytics&lt;/a&gt;, eu descobri que alguém chegou aqui no blog procurando por "transformar kilometros em arquivo binarios". Se você é essa pessoa, desculpe, mas eu não entendi sua dúvida. Se você não é essa pessoa, puxe uma cadeira pra ver como até uma pergunta sem sentido pode ser desenvolvida num tema interessante :)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/63miles-762587.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/63miles-762582.jpg" alt="" border="0" /&gt;&lt;/a&gt;Uma coisa que me incomoda toda vez que venho pra Califórnia é ter que lidar com milhas e libras. Eu cresci com o sistema métrico: se você me disser que a distância de São Paulo pra Florianópolis é de 700km, eu sei o que isso significa. Agora, se você me disser que a distância de San Francisco pra Mountain View é de 100 milhas, minha intuição falha, e eu vou precisar converter pra km pra poder ter noção da distância.&lt;br /&gt;&lt;br /&gt;Como esperado, converter mentalmente de milhas pra km é algo que faço todo o tempo por aqui. Existem várias maneiras de converter de cabeça, mas a filosofia Ricbit dita que, de várias maneiras equivalentes, o correto é escolher a mais bizarra! Sendo assim, vou mostrar a conversão utilizando números de Fibonacci.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/rabbit-764600.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/rabbit-764598.jpg" alt="" border="0" /&gt;&lt;/a&gt;Todo mundo conhece os números de Fibonacci, eles estão em todo lugar. Em minha época de estudante, uma das minhas diversões secretas era entrar escondido no andar superior da biblioteca do &lt;a href="http://www.ime.usp.br/"&gt;IME&lt;/a&gt; só pra ler a &lt;a href="http://www.engineering.sdstate.edu/%7Efib/"&gt;Fibonacci Quartely&lt;/a&gt;. Os leitores assíduos da revista conhecem um monte de fatos curiosos sobre os Fibonacci, e eu vou usar dois deles aqui.&lt;br /&gt;&lt;br /&gt;O primeiro é que os números da seqüência de Fibonacci podem ser calculados diretamente, sem precisar fazer toda a recursão F(n+2)=F(n+1)+F(n). Pra isso, basta calcular qual o inteiro mais próximo da expressão abaixo:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/formula_fn-736484.png"&gt;&lt;img style="border:0; margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/formula_fn-736482.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Na expressão acima, o phi é a conhecida &lt;a href="http://en.wikipedia.org/wiki/Golden_ratio"&gt;razão áurea&lt;/a&gt;. A demostração dessa fórmula é elementar e &lt;a href="http://everything2.com/node/1882510"&gt;fácil de encontrar na web&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;O segundo é que qualquer número pode ser escrito como uma soma de números distintos de Fibonacci. Por exemplo, 100 pode escrito como 89+8+3. Daí, se você enfileirar os números de Fibonacci, e atribuir a cada número 1 se ele é usado na soma, e 0 se não é, você pode atribuir a qualquer inteiro uma string de zeros e uns que funciona como uma espécie de base binária alternativa (o povo chama isso de base de Fibonacci).&lt;br /&gt;&lt;br /&gt;Fazendo o processo com o número 100, chegamos em 10000101000. Essa base tem algumas propriedades curiosas, por exemplo, ela não é bijetora (de fato, você pode escrever 100 de outras maneiras, como 1100101000). Uma base numérica que tem representação múltipla tem utilidades bastante curiosas em design de circuitos elétricos (mas isso é uma história pra outro dia :).&lt;br /&gt;&lt;br /&gt;Além disso, cada número possui pelo menos uma representação onde não há nenhuma seqüência com dois uns consecutivos (pela própria definição de Fibonacci, se houver dois uns em algum ponto, você pode apagá-los e trocar por um único 1 na posição seguinte). Esse fato é explorado em alguns tipos de &lt;a href="http://en.wikipedia.org/wiki/Signalling_%28telecommunications%29"&gt;sinalização&lt;/a&gt;, para fazer detecção de erro: se você receber dois uns seguidos, certamente recebeu um erro.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/shifted_20031219-799054.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/shifted_20031219-799030.jpg" alt="" border="0" /&gt;&lt;/a&gt;Mas qual a relação disso com milhas e quilômetros? É simples, para converter de milhas para km, basta fazer um shift de Fibonacci!&lt;br /&gt;&lt;br /&gt;Uma milha equivale a 1.609344 km. A razão áurea é 1.61803399. Os dois números, apesar de não serem relacionados, são muito parecidos, e esse é o truque que vamos usar pra converter. Na base binária tradicional, um shift para a esquerda é equivalente a multiplicar por dois; na base de Fibonacci, um shift para a esquerda equivale a multiplicar pela razão áurea. Então, se você tiver um valor em milhas na base de Fibonacci, um shift irá transformar o valor para o equivalente em quilômetros.&lt;br /&gt;&lt;br /&gt;Vamos conferir: 100 é 10000101000. Com um zero extra no final, fica 100001010000, que é 144+13+5=162. Se, ao invés disso, você converter diretamente, teria 160.9km, ou seja, o método realmente aproxima muito bem a conversão! O gráfico abaixo mostra a porcentagem do erro do método em relação ao ideal, e mais abaixo está o programa que converte a milhagem para quilometragem e gera o gráfico:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/miles_error-777211.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/miles_error-777209.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;a href="http://www.ricbit.com/code/miles.py"&gt;Script que plota o gráfico acima, em python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como pode ser visto, o método esquenta bem rápido, pra distâncias superiores a 10 milhas o erro é inferior a 5%, e acima disso praticamente desaparece. Em comparação, o método naive de aproximar por 1.5 (multiplicar de cabeça por 3 e dividir por 2), tem um erro constante de mais ou menos 8%.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/06/programa-de-milhagem.html' title='Programa de milhagem'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=843067646841264017' title='7 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/843067646841264017'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/843067646841264017'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-7742008920512121026</id><published>2008-06-07T15:01:00.010-03:00</published><updated>2008-06-16T20:44:06.632-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monte carlo'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><category scheme='http://www.blogger.com/atom/ns#' term='criptologia'/><title type='text'>Primos aleatórios</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/4470_primocruzado-724523.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/4470_primocruzado-724521.jpg" alt="" border="0" /&gt;&lt;/a&gt;Dia desses a &lt;a href="http://www1.folha.uol.com.br/folha/galeria/album/p_20070726-bichos06.shtml"&gt;Alice&lt;/a&gt; me perguntou se era possível criar um gerador de números aleatórios que só retornasse números primos. Eu respondi que sim, mas que provavelmente ela não iria gostar da resposta:&lt;br /&gt;&lt;pre&gt;int&amp;nbsp;random_prime(int&amp;nbsp;n)&amp;nbsp;{&lt;br&gt;&amp;nbsp;int&amp;nbsp;x;&lt;br&gt;&amp;nbsp;do&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;x&amp;nbsp;=&amp;nbsp;random(n);&lt;br&gt;&amp;nbsp;}&amp;nbsp;while&amp;nbsp;(!is_prime(x));&lt;br&gt;&amp;nbsp;return&amp;nbsp;x;&lt;br&gt;}&lt;/pre&gt;&lt;br /&gt;Eu sabia que o que ela queria na verdade era uma fórmula bonitinha; então, como esperado, ela não gostou :) Mas a verdade é que esse algoritmo é bem melhor que as alternativas!&lt;br /&gt;&lt;br /&gt;Antes de mostrar porque isso é verdade, precisamos formalizar um pouco o problema. É claro que não existem algoritmos que geram números aleatórios: se você quiser aleatoriedade real, precisa pegar alguma fonte física, como o &lt;a href="http://en.wikipedia.org/wiki/Radioactivity"&gt;decaimento radiativo&lt;/a&gt;. Assumindo então que existe uma fonte física que gera uma &lt;a href="http://en.wikipedia.org/wiki/Uniform_distribution_%28discrete%29"&gt;distribuiçao uniforme&lt;/a&gt; sobre algum intervalo, para criar o algoritmo que retorna números primos aleatórios, basta criar uma&lt;span style="text-decoration: underline;"&gt;&lt;/span&gt;&lt;a href="http://en.wikipedia.org/wiki/Injective_function"&gt;&lt;/a&gt; função bijetora que leve naturais para primos. Ou seja, uma função que, para um dado um número n, retorne o n-ésimo primo.&lt;br /&gt;&lt;br /&gt;O problema é que não existe nenhuma &lt;a href="http://en.wikipedia.org/wiki/Closed-form_expression"&gt;fórmula fechada&lt;/a&gt; que calcule isso de maneira eficiente. Você pode calcular alguma constante irracional que resolva o problema, no estilo da &lt;a href="http://mathworld.wolfram.com/MillsTheorem.html"&gt;constante de Mills&lt;/a&gt;, só que mais cedo ou mais tarde a precisão vai te limitar. Você pode calcular o n-ésimo primo com base em alguma outra distribuição, como a &lt;a href="http://en.wikipedia.org/wiki/M%C3%B6bius_function"&gt;função de Möbius&lt;/a&gt;, mas aí você só está empurrando o problema com a barriga, porque a outra função é tão difícil de calcular quanto a original.&lt;br /&gt;&lt;br /&gt;Uma maneira sem as desvantagens acima é usar o &lt;a href="http://mathworld.wolfram.com/WilsonsTheorem.html"&gt;teorema de Wilson&lt;/a&gt; pra chegar na seguinte fórmula:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/mathtran-765097.png"&gt;&lt;br /&gt;&lt;img style="display:block; border:0; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://www.ricbit.com/uploaded_images/mathtran-765088.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/dukenukem-706042.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/dukenukem-706029.jpg" alt="" border="0" /&gt;&lt;/a&gt;Mas mesmo essa fórmula ainda está longe do ideal, primeiro porque você vai ter que lidar com números enormes nela (pra n=10 os valores intermediários ficam tão grandes que estouram o limite do que cabe num float), segundo porque, mesmo que você use uma lib para long floats, a complexidade é O(2&lt;sup&gt;n&lt;/sup&gt;), ou seja, mais lento que os programadores do Duke Nukem Forever. Se ainda assim você quiser testar, minha implementação em python é a abaixo:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/nth_prime.py"&gt;Implementação em python da fórmula acima&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Sendo assim, quão melhor era a implementação original por tentativa e erro? Pra avaliar isso, precisamos calcular a complexidade daquele algoritmo. Não é difícil ver que a complexidade do algoritmo como um todo é a complexidade do is_prime() multiplicado pelo &lt;a href="http://mathworld.wolfram.com/ExpectationValue.html"&gt;valor esperado&lt;/a&gt; do número de iterações do loop.&lt;br /&gt;&lt;br /&gt;Se você estiver trabalhando numa faixa pequena de primos, pode tabelar todos os primos no intervalo e fazer um is_prime() que seja O(1), mas aí também não tem necessidade da tentativa e erro, você pode indexar seu número aleatório direto na tabela. O caso legal é quando você não pode tabelar, nesse caso você pode implementar o is_prime() usando, por exemplo, o &lt;a href="http://en.wikipedia.org/wiki/AKS_primality_test"&gt;algoritmo AKS&lt;/a&gt;, cuja complexidade é O((log n)&lt;sup&gt;10.5&lt;/sup&gt;).&lt;br /&gt;&lt;br /&gt;O que resta então é calcular o valor esperado do loop. Lembrando que E[x]=sum(x*p(x)), o que precisamos é calcular qual é a probabilidade de ter uma iteração, duas iterações, e assim por diante. Ora, o &lt;a href="http://en.wikipedia.org/wiki/Prime_number_theorem"&gt;teorema dos números primos&lt;/a&gt; nos garante que a quantidade de números primos menores que n é assintoticamente igual a n/log(n), então a chance de um número ser primo, num conjunto com n elementos, é 1/log(n). Vamos chamar isso de "p" só pra ficar mais fácil, e o complemento disso é q=1-p, ou seja, a chance de um número não ser primo.&lt;br /&gt;&lt;br /&gt;Vejamos então: pra você acertar o primo de primeira, a chance é p. Se você acertar o primo na segunda, a chance é pq. Na terceira, é pq&lt;sup&gt;2&lt;/sup&gt;, na quarta pq&lt;sup&gt;3&lt;/sup&gt; e assim por diante. Então o valor esperado é:&lt;br /&gt;&lt;br /&gt;X = 1p + 2pq + 3pq&lt;sup&gt;2&lt;/sup&gt; + 4pq&lt;sup&gt;3&lt;/sup&gt; + ...&lt;br /&gt;X = p (1 + 2q + 3q&lt;sup&gt;2&lt;/sup&gt; + 4q&lt;sup&gt;3&lt;/sup&gt; + ....)&lt;br /&gt;&lt;br /&gt;Quem tem prática com a &lt;a href="http://en.wikipedia.org/wiki/Z_transform"&gt;transformada z&lt;/a&gt; sabe calcular isso de cabeça, mas dá pra calcular também só com matemática elementar. Se você isolar q na soma, fica com:&lt;br /&gt;&lt;br /&gt;X = p (1 + q(2 + 3q + 4q&lt;sup&gt;2&lt;/sup&gt; + ....))&lt;br /&gt;&lt;br /&gt;Agora você tira da cartola y=1+q+q&lt;sup&gt;2&lt;/sup&gt;+q&lt;sup&gt;3&lt;/sup&gt;+... e substitui:&lt;br /&gt;&lt;br /&gt;X = p (1 + q(2 + 3q + 4q&lt;sup&gt;2&lt;/sup&gt; + ....))&lt;br /&gt;X = p (1 + q(y + 1 + 2q + 3q&lt;sup&gt;2&lt;/sup&gt; + ....))&lt;br /&gt;X = p (1 + q(y + X/p)) = p + pqy + pXq/p = p(1+qy) + Xq&lt;br /&gt;X - Xq = p (1 + qy)&lt;br /&gt;X (1-q) = Xp = p (1 + qy)&lt;br /&gt;X = 1 + qy&lt;br /&gt;&lt;br /&gt;Mas y é só a soma de uma PG, e isso nós sabemos que vale y=1/(1-q)=1/p. Então:&lt;br /&gt;&lt;br /&gt;X = 1 + q/p = (p+q)/p = 1/p&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/ipod-729482.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/ipod-729480.jpg" alt="" border="0" /&gt;&lt;/a&gt;Como p=1/log(n), então o valor esperado que nós queríamos é tão somente X=log n (vocês também não se impressionam quando tudo simplifica no final?)&lt;br /&gt;&lt;br /&gt;É claro que eu não iria resistir à tentação de implementar uma simulação pra ver se o valor bate mesmo. A nossa fórmula diz que, para a faixa de 10 milhões de números, o valor esperado tem que ser da ordem de log(10&lt;sup&gt;7&lt;/sup&gt;)=16.1. A simulação abaixo retorna 15.2, bem próximo do valor que foi predito.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/random_prime.cc"&gt;Simulação monte carlo do valor esperado, em C++&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No fim das contas, a complexidade do algoritmo com tentativa e erro é apenas O(log n), se você tiver um tabelão de primos. Na prática, esse é o método usado por todos que precisam de primos aleatórios: a libgcrypt usada no &lt;a href="http://www.gnupg.org/"&gt;gpg&lt;/a&gt;, por exemplo, utiliza esse método na função &lt;a href="http://www.google.com/codesearch?hl=en&amp;amp;q=libgcrypt+primegen.c+gen_prime+show:MdtcelzX8ZM:j1akSRNJFcg:nQDKjtoXTC4&amp;amp;sa=N&amp;amp;cd=1&amp;amp;ct=rc&amp;amp;cs_p=ftp://ftp.gnupg.org/gcrypt/alpha/libgcrypt/libgcrypt-1.1.44.tar.gz&amp;amp;cs_f=libgcrypt-1.1.44/cipher/primegen.c"&gt;gen_prime()&lt;/a&gt;, com vários truques pra tornar o teste de primalidade bem rápido.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/06/primos-aleatrios.html' title='Primos aleatórios'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=7742008920512121026' title='0 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/7742008920512121026'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/7742008920512121026'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-4746562879956897494</id><published>2008-06-05T06:40:00.007-03:00</published><updated>2008-06-13T21:47:20.655-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='infinito'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='binário'/><title type='text'>Aproximações</title><content type='html'>Ao longo da vida, encontramos aproximações a todo momento. Na escola, a gravidade é aproximadamente 10 m/s&lt;sup&gt;2&lt;/sup&gt;, e a velocidade da luz é aproximadamente 3x10&lt;sup&gt;8&lt;/sup&gt; m/s. Segundo a Bíblia, pi é aproximadamente três (&lt;a href="http://www.bibliaonline.net/bol/?acao=por_verso&amp;amp;livro=11&amp;amp;capitulo=7-7&amp;amp;versiculo=&amp;amp;versao=1&amp;amp;grupos=&amp;amp;agrupar=&amp;amp;link=bol&amp;amp;cab=1&amp;amp;pag_ini=20&amp;amp;lang=BR"&gt;1 Reis 7:23&lt;/a&gt;). Mas a minha aproximação predileta é uma que os computeiros usam a todo momento: infinito é aproximadamente oito!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/inf8-754160.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/inf8-754158.png" alt="" border="0" /&gt;&lt;/a&gt;De fato, essa aproximação é o que permite aos computadores trabalhar com números negativos, através do &lt;a href="http://en.wikipedia.org/wiki/Two%27s_complement"&gt;complemento de dois&lt;/a&gt;. Lembrando a regra, para calcular o oposto de um número qualquer, basta inverter os bits e somar um. Vamos ver na prática como isso funciona, calculando o oposto de 5.&lt;br /&gt;&lt;br /&gt;Cinco em binário é 101b, e pode ser escrito também como uma soma de potências de dois: 1+4. Para inverter os bits de 5, precisamos lembrar que há infinitos zeros na frente do 101b, então o inverso vai ter infinitas potências de dois:&lt;br /&gt;&lt;pre&gt; x   = 1     + 4&lt;br /&gt;~x   =     2     + 8 + 16 + 32 + ...&lt;br /&gt;~x+1 = 1 + 2     + 8 + 16 + 32 + ...&lt;/pre&gt;&lt;br /&gt;Esse valor, y=~x+1, não se parece com -5. Mas as coisas ficam mais claras se você multiplicar y por dois, parcela a parcela, ...&lt;br /&gt;&lt;pre&gt; y   = 1 + 2     + 8 + 16 + 32 + ...&lt;br /&gt;2y   =     2 + 4     + 16 + 32 + ...&lt;/pre&gt;&lt;br /&gt;... e depois subtrair esse valor do original:&lt;br /&gt;&lt;pre&gt;2y   =      2 + 4     + 16 + 32 + ...&lt;br /&gt; y   =  1 + 2     + 8 + 16 + 32 + ...&lt;br /&gt;2y-y = -1     + 4 - 8&lt;br /&gt; y   = -5&lt;/pre&gt;&lt;br /&gt;Todos as parcelas maiores que 16 cancelam, e do lado de cá, 2y menos y é o próprio y. Então y=-5, QED. Quando calculamos complementos de dois no computador, usualmente fazemos as contas apenas em um byte, mas, no fundo, é a mesma coisa: ao invés de fazer a conta com infinitas parcelas, você aproxima o valor por apenas 8 parcelas.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/escher-lego-744162.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/escher-lego-744117.jpg" alt="" border="0" /&gt;&lt;/a&gt;Seu professor de Cálculo 4 certamente deve ter te avisado dos horrores de manipular &lt;a href="http://en.wikipedia.org/wiki/Divergent_series"&gt;seqüências divergentes&lt;/a&gt;, que invariavelmente levam a paradoxos (como somar apenas parcelas positivas e obter um resultado negativo). No entanto, às vezes até os paradoxos têm utilidades práticas :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/06/aproximaes.html' title='Aproximações'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=4746562879956897494' title='2 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/4746562879956897494'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/4746562879956897494'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-8407015952004932743</id><published>2008-05-29T23:42:00.008-03:00</published><updated>2008-05-30T11:53:52.713-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='programação funcional'/><category scheme='http://www.blogger.com/atom/ns#' term='infinito'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='linguagens bizarras'/><title type='text'>Python one-liners são Turing-complete</title><content type='html'>Quem programa em C há décadas normalmente não se dá conta de quão ilegíveis são as expressões mais comuns da linguagem. Quando eu era um garoto recém-saído do BASIC, eu lembro de ter me assustado com coisas básicas como for(i=0; i&amp;lt;10; i++). Mas isso é idiossincrasia do C, outras linguagens não sofrem disso, como o Python.&lt;br /&gt;&lt;br /&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/jeff_goldblum-791685.jpg" alt="" border="0" /&gt;Python foi planejada para ser legível. Os programadores mais experientes citam o &lt;a href="http://www.python.org/dev/peps/pep-0020/"&gt;Zen of Python&lt;/a&gt;, que dita que "belo é melhor que feio", e "legibilidade conta". De fato, é até difícil escrever código ilegível em Python. Mas é claro que difícil não é impossível, e se o dr. Ian Malcolm fosse um programador, ele certamente diria que "&lt;span style="font-style: italic;"&gt;obfuscation finds a way&lt;/span&gt;."&lt;br /&gt;&lt;br /&gt;Aconteceu comigo semana passada: eu olhava alguns &lt;a href="http://www.pythonbrasil.com.br/moin.cgi/Exerc%C3%ADciosComListas"&gt;exercícios sobre listas&lt;/a&gt; para iniciantes, e notei que, embora eles fossem de fato muito simples, ficariam bem mais divertidos se eu tentasse resolvê-los usando apenas uma linha em cada. Abusando de &lt;a href="http://en.wikipedia.org/wiki/Functional_programming"&gt;programação funcional&lt;/a&gt;, eu consegui fazer os dez primeiros assim:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/oneliners.py"&gt;Soluções dos exercícios em uma linha de Python cada.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Depois de brincar algum tempo com one-liners, a pergunta que naturalmente se apresenta é: será que é possível fazer &lt;span style="font-style: italic;"&gt;qualquer&lt;/span&gt; programa em uma linha de Python? A dificuldade vem do fato de que o Python diferencia &lt;a href="http://infohost.nmt.edu/tcc/help/pubs/python22/statements.html"&gt;statements&lt;/a&gt; de expressions, e você só pode ter um statement por linha. Em Python, statements incluem print, if, while, for e atribuições, ou seja, um one-liner só pode usar um único desses.&lt;br /&gt;&lt;br /&gt;Então, colocando a pergunta de outra maneira: é possível demonstrar que um programa em Python com um único statement é &lt;a href="http://en.wikipedia.org/wiki/Turing_completeness"&gt;Turing-complete&lt;/a&gt;? Existem dois caminhos pra demonstrar isso, o primeiro é construir um emulador para uma &lt;a href="http://en.wikipedia.org/wiki/Universal_Turing_machine"&gt;máquina de Turing universal&lt;/a&gt; em uma linha, o segundo é mostrar que é possível converter para uma linha de Python todos os programas possíveis de um sistema que seja Turing-complete, como o &lt;a href="http://en.wikipedia.org/wiki/Lambda_calculus"&gt;cálculo lambda&lt;/a&gt;, ou os &lt;a href="http://en.wikipedia.org/wiki/Tag_system"&gt;tag-systems&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Eu resolvi abordar o problema com a filosofia Ricbit: se existem várias maneiras equivalentes de fazer alguma coisa, escolha a mais bizarra! Assim sendo, vou demonstrar que Python one-liners são Turing-complete através de redução ao &lt;a href="http://en.wikipedia.org/wiki/Brainfuck"&gt;Brainfuck&lt;/a&gt; (cuja universalidade já foi demonstrada &lt;a href="http://www.iwriteiam.nl/Ha_bf_Turing.html"&gt;várias vezes&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Vamos lá então: o estado de um programa em Brainfuck pode descrito em qualquer momento por uma quádrupla (mem, p, stdin, stdout), que são respectivamente a memória, o ponteiro, a entrada e saída. Vou implementar cada operação do Brainfuck como funções que recebem quádruplas e retornam quádruplas, descrevendo assim a transição de estado.&lt;br /&gt;&lt;br /&gt;A operação mais simples é o ponto, que só adiciona o elemento apontado na saída:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/lambda-787583.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 159px; height: 135px;" src="http://www.ricbit.com/uploaded_images/lambda-787577.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;dot = lambda mem, p, stdin, stdout: (mem, p, stdin, stdout+[mem[p]])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Para implementar a vírgula, eu preciso primeiro de alguma maneira de modificar um único elemento de uma lista. Se eu pudesse usar atribuições, bastaria algo do tipo mem[p]=value, mas como atribuições em Python são statements, preciso de uma função auxiliar. Além disso, eu preciso fazer o pop() do valor frontal da lista que guarda o stdin, o que me leva à outra auxiliar:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;change = lambda mem, pos, value: [value if i==pos else a for i, a in enumerate(mem)]&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;&lt;br /&gt;get = lambda s: (s[0], s[1:]) if len(s) else (0,[])&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;comma = lambda mem, p, stdin, stdout: (lambda now, next: (change(mem, p, now), p, next, stdout))(*get(stdin))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tendo a função change em mãos, fazer os comandos de mais e menos é simples:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;plus = lambda mem, p, stdin, stdout: (change(mem, p, mem[p]+1), p, stdin, stdout)&lt;/span&gt; &lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;&lt;br /&gt;minus = lambda mem, p, stdin, stdout: (change(mem, p, mem[p]-1), p, stdin, stdout)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Os comandos de esquerda e direita precisam tomar o cuidado de aumentar os limites da memória se necessário, a universalidade do Brainfuck requer uma fita infinita para os dois lados:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;left = lambda mem, p, stdin, stdout: ([0]+mem if not p else mem, 0 if not p else p-1, stdin, stdout)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;right = lambda mem, p, stdin, stdout: (mem+[0] if p==len(mem)-1 else mem, p+1, stdin, stdout)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/sloth2-792947.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/sloth2-792944.jpg" alt="" border="0" /&gt;&lt;/a&gt;Agora chegamos na parte complicada, que é o operador de loop. Como for e while são statements, e lambdas recursivos precisam de uma atribuição (fat = lambda x: 1 if x&lt;=1 else x*fat(x-1)), então a única saída é apelar pra &lt;a href="http://en.wikipedia.org/wiki/Lazy_evaluation"&gt;lazy evaluation&lt;/a&gt;, que no Python é implementada no módulo &lt;a href="http://docs.python.org/lib/module-itertools.html"&gt;itertools&lt;/a&gt;. (Incluir o módulo itertools poderia tornar o programa um two-liner, mas felizmente é possível importar um módulo usando uma expression ao invés de um statement: a função &lt;a href="http://www.diveintopython.org/functional_programming/dynamic_import.html"&gt;__import__&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;A solução para o operador de loop é criar uma lista infinita contendo [x, f(x), f(f(x)), f(f(f(x))), ...], onde cada f é uma aplicação do conteúdo do loop. Depois, para executar o loop, basta iterar nesta lista infinita, procurando o primeiro elemento onde o elemento apontado pelo ponteiro seja nulo. Precisamos então de uma função que calcule f^n e uma que gere a lista infinita:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;composite = lambda f, n: lambda x: reduce(lambda a, b: b(*a), [f]*n, x)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;infinite = lambda f, x: itertools.imap(lambda n: composite(f, n)(x), itertools.count(0))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Depois, basta criar um &lt;a href="http://en.wikipedia.org/wiki/Predicate_%28mathematics%29"&gt;predicado&lt;/a&gt; que avalie quando o loop deve parar, e pegar o primeiro elemento da lista onde o predicado é verdadeiro:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;predicate = lambda mem, p, stdin, stdout: mem[p] != 0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;getfirst = lambda it: [i for i in itertools.islice(it, 1)][0]&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;loop = lambda f: lambda *x: getfirst(itertools.dropwhile(lambda x: predicate(*x), infinite(f,x)))&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tendo todos os comandos, só precisamos de uma função extra para encadeá-los, e depois, só para o programa não ficar grande demais, um shortcut que executa strings diretamente em Brainfuck:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;&lt;br /&gt;chain = lambda f: lambda *x: reduce(lambda y, g: g(*y), f, x)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;bf = {'+':plus, '-':minus, '.':dot, ',':comma, '&amp;lt;':left, '&amp;gt;':right}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;run = lambda f: chain([bf[i] for i in f])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Feito! Agora é só fazer um script para parsear o Brainfuck original e gerar o one-liner. A título de ilustração, esse é o Hello World gerado pelo script:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/IN495-munch-BST--Scream-1893-742594.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/IN495-munch-BST--Scream-1893-742583.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;span style="color: rgb(255, 0, 0);font-size:78%;" &gt;print ''.join(chr(i) for i in ( (lambda itertools: (lambda change, get, chain, composite: (lambda comma, dot, plus, minus, left, right, infinite, predicate, getfirst: (lambda bf, loop: (lambda run: (chain ([run ("++++++++++"), loop (run ("&amp;lt;+++++++&amp;lt;" "++++++++++&amp;lt;" "+++&amp;lt;+&amp;gt;&amp;gt;&amp;gt;&amp;gt;-")), run ("&amp;lt;++.&amp;lt;" "+.+++++++..+++." "&amp;lt;++.&amp;gt;&amp;gt;" "+++++++++++++++" ".&amp;lt;.+++." "------.--------" ".&amp;lt;+.&amp;lt;.")])) ([0],0,[],[]) )( (lambda f: chain([bf[i] for i in f])) ) )( ({'+':plus, '-':minus, '.':dot, ',':comma, '&amp;lt;':left, '&amp;gt;':right}), (lambda f: lambda *x: getfirst(itertools.dropwhile(lambda x: predicate(*x), infinite(f,x)))) ) )( (lambda mem,p,stdin,stdout: (lambda now,next: (change(mem,p,now),p,next,stdout))(*get(stdin))), (lambda mem,p,stdin,stdout: (mem,p,stdin,stdout+[mem[p]])), (lambda mem,p,stdin,stdout: (change(mem,p,mem[p]+1),p,stdin,stdout)), (lambda mem,p,stdin,stdout: (change(mem,p,mem[p]-1),p,stdin,stdout)), (lambda mem,p,stdin,stdout: ([0]+mem if not p else mem, 0 if not p else p-1, stdin, stdout)), (lambda mem,p,stdin,stdout: (mem+[0] if p==len(mem)-1 else mem, p+1, stdin, stdout)), (lambda f,x: itertools.imap(lambda n: composite(f,n)(x), itertools.count(0))), (lambda mem,p,stdin,stdout: mem[p] != 0), (lambda it: [i for i in itertools.islice(it, 1)][0]) ) )( (lambda mem,pos,value: [value if i==pos else a for i,a in enumerate(mem)]), (lambda s: (s[0],s[1:]) if len(s) else (0,[])), (lambda f: lambda *x: reduce(lambda y,g: g(*y), f, x)), (lambda f,n: lambda x: reduce(lambda a,b:b(*a),[f]*n,x)) ) )(__import__("itertools")) )[3])&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;QED, em uma única linha, como prometido! (Eu não prometi que seria uma linha pequena :)&lt;br /&gt;&lt;br /&gt;O script que converte de Brainfuck para Python one-liner está abaixo, para quem quiser brincar:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/turing.py"&gt;Conversor para Python one-liner.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Como nota final, vale lembrar que só porque você &lt;span style="font-style: italic;"&gt;pode&lt;/span&gt; escrever qualquer coisa em uma linha, não significa que você &lt;span style="font-style: italic;"&gt;deve&lt;/span&gt; fazer isso. Legibilidade conta :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/05/python-one-liners-so-turing-complete.html' title='Python one-liners são Turing-complete'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=8407015952004932743' title='6 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8407015952004932743'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8407015952004932743'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-8444249420248307930</id><published>2008-05-21T21:41:00.006-03:00</published><updated>2008-06-16T20:42:05.802-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><category scheme='http://www.blogger.com/atom/ns#' term='criptologia'/><category scheme='http://www.blogger.com/atom/ns#' term='binário'/><title type='text'>Potências ótimas</title><content type='html'>Olhando no &lt;a href="http://www.google.com/analytics/"&gt;Google Analytics&lt;/a&gt;, eu descobri que alguém chegou aqui no blog procurando por "como implementar em c++ potências". Se você é essa pessoa, a resposta está abaixo. Se você não é essa pessoa, puxe uma cadeira que o papo é divertido :)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/cubes2-763087.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/cubes2-763085.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Calcular potências aproximadas em ponto flutuante é trivial, basta incluir a biblioteca &amp;lt;cmath&amp;gt; e usar a função &lt;a href="http://linux.die.net/man/3/pow"&gt;pow&lt;/a&gt;, que internamente é implementada como exp(y*log(x)). Mas existem várias aplicações onde você precisa do valor exato da potência, como, por exemplo, durante a &lt;a href="http://en.wikipedia.org/wiki/RSA"&gt;criptografia RSA&lt;/a&gt;. Nesses casos, uma primeira abordagem pode ser como no código abaixo:&lt;br /&gt;&lt;br /&gt;int&amp;nbsp;natural(int&amp;nbsp;x,&amp;nbsp;int&amp;nbsp;n)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;int&amp;nbsp;result&amp;nbsp;=&amp;nbsp;1;&lt;br&gt;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;1;&amp;nbsp;i&amp;nbsp;&amp;lt;=&amp;nbsp;n;&amp;nbsp;i++)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;*=&amp;nbsp;x;&lt;br&gt;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result;&lt;br&gt;}&lt;br&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/golf-776938.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/golf-776936.jpg" alt="" border="0" /&gt;&lt;/a&gt;Esse código funciona, mas existem maneiras mais espertas. Nós, que temos dez dedos, não estamos acostumados a pensar em binário. Mas se fôssemos como os golfinhos, que tem um cérebro maior que o nosso e só duas barbatanas, poderíamos visualizar o expoente em binário e fazer um código assim:&lt;br /&gt;&lt;br /&gt;int&amp;nbsp;binary(int&amp;nbsp;x,&amp;nbsp;int&amp;nbsp;n)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(n&amp;nbsp;==&amp;nbsp;0)&amp;nbsp;return&amp;nbsp;1;&lt;br&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(n&amp;nbsp;==&amp;nbsp;1)&amp;nbsp;return&amp;nbsp;x;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;int&amp;nbsp;half&amp;nbsp;=&amp;nbsp;binary(x,&amp;nbsp;n/2);&lt;br&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(n&amp;nbsp;&amp;amp;&amp;nbsp;1)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;half*half*x;&lt;br&gt;&amp;nbsp;&amp;nbsp;else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;half*half;&lt;br&gt;}&lt;br /&gt;&lt;br /&gt;A versão original era O(n), essa é O(log&amp;nbsp;n), uma melhoria significativa. Mas a melhor notícia sobre esse algoritmo é que você não precisa implementá-lo, ele já está pronto na biblioteca padrão do C++. Basta incluir &amp;lt;ext/numeric&amp;gt; e usar a função &lt;a href="http://www.sgi.com/tech/stl/power.html"&gt;power&lt;/a&gt;. A função da STL ainda recebe como argumento opcional um &lt;a href="http://en.wikipedia.org/wiki/Function_object"&gt;functor&lt;/a&gt; de multiplicação, então você pode implementar com ela &lt;a href="http://en.wikipedia.org/wiki/Modular_exponentiation"&gt;exponenciação modular&lt;/a&gt;, ou até mesmo potências de matrizes (ela funciona mesmo que sua multiplicação não seja comutativa).&lt;br /&gt;&lt;br /&gt;Por outro lado, esse algoritmo é prático, mas não é ótimo. Em alguns casos, existem maneiras mais rápidas de calcular a potência, o primeiro caso onde isso acontece é para n&lt;sup&gt;15&lt;/sup&gt;. O algoritmo binário precisa de seis multiplicações para resolver o problema, mas existem soluções com apenas cinco:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/power-731135.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/power-731134.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A melhor maneira de descrever as multiplicações necessárias para calcular uma potência é através de uma &lt;a href="http://en.wikipedia.org/wiki/Addition_chain"&gt;addition chain&lt;/a&gt;. Uma addition chain é uma espécie de generalização da &lt;a href="http://en.wikipedia.org/wiki/Fibonacci_number"&gt;seqüência de Fibonacci&lt;/a&gt;: enquanto no Fibonacci o próximo elemento é a soma dos dois imediatamente precedentes, numa addition chain o próximo elemento é a soma de dois anteriores quaisquer, ou até mesmo a soma de um elemento anterior com ele mesmo (com a restrição de que a seqüência precisa ser estritamente crescente).&lt;br /&gt;&lt;br /&gt;Pela regra de formação dá pra perceber que, ao contrário da seqüência de Fibonacci, existem inúmeras addition chains. E melhor ainda, você pode associar uma addition chain com a seqüência de expoentes gerados no cálculo de uma potência. Para o exemplo de n=15 acima, as duas addition chains correspondentes são [1 2 3 6 7 14 15] e [1 2 4 5 10 15].&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/OptimumPower-764684.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 129px; height: 162px;" src="http://www.ricbit.com/uploaded_images/OptimumPower-764680.jpg" alt="" border="0" /&gt;&lt;/a&gt;Achar uma seqüência ótima para calcular potências, então, é equivalente a achar uma addition chain de tamanho mínimo terminada no número que queremos. Infelizmente, eu tenho duas más notícias: o &lt;a href="http://www.ricbit.com/2008/04/erds-e-os-logaritmos.html"&gt;Erdös&lt;/a&gt; mostrou que a addition chain ótima não cresce mais lentamente que O(log&amp;nbsp;n), então assintoticamente ela não é melhor que o método binário; e pior, o cálculo da addition chain ótima é NP-Completo. Abaixo eu implementei a addition chain ótima em C++ (usando brute force, então está bem lento):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/integer_power.cc"&gt;Implementação da addition chain ótima em C++&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;É interessante também dar uma olhada nos casos onde o binário perde. No gráfico abaixo, a linha vermelha é o método binário, a linha azul é a addition chain ótima, e a linha verde é log&lt;sub&gt;2&lt;/sub&gt;n:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/graphcrop-749624.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/graphcrop-749609.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Olhando o gráfico, um golfinho certamente perceberia que o desvio do método binário é proporcional à quantidade de dígitos 1 na representação binária do expoente (os picos são em 63, 127, 255, e assim por diante). A demonstração disso é bem simples e está no &lt;a href="http://books.google.com/books?id=5oJQAAAAMAAJ"&gt;Seminumerical Algorithms&lt;/a&gt;, junto com várias heurísticas para aproximar a addition chain ótima.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/05/potncias-timas.html' title='Potências ótimas'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=8444249420248307930' title='3 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8444249420248307930'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8444249420248307930'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-3895838495750595617</id><published>2008-05-10T12:53:00.003-03:00</published><updated>2008-05-10T15:13:09.256-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='strange loop'/><category scheme='http://www.blogger.com/atom/ns#' term='infinito'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><title type='text'>Ao Infinito e Além</title><content type='html'>O que há de comum entre &lt;a href="http://en.wikipedia.org/wiki/Georg_Cantor"&gt;Cantor&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Godel"&gt;Gödel&lt;/a&gt; e &lt;a href="http://en.wikipedia.org/wiki/Alan_Turing"&gt;Turing&lt;/a&gt;? Pelo menos duas coisas, e a primeira delas é que os três tentaram expandir os limites da matemática.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/infinito-736592.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/infinito-736588.jpg" alt="" border="0" /&gt;&lt;/a&gt;Cantor, por exemplo, queria contar até infinito. Essa é uma tarefa bem difícil: todos os que tentaram, cansaram antes de concluir! Mas o que o Cantor percebeu foi que, embora seja difícil contar diretamente, você pode contar até infinito indiretamente, desde que você saiba qual o significado real de "contar" alguma coisa.&lt;br /&gt;&lt;br /&gt;O que significa, por exemplo, que você tem quatro maçãs? Na interpretação de Cantor, isso significa que você pode criar uma &lt;a href="http://en.wikipedia.org/wiki/Bijection"&gt;bijeção&lt;/a&gt; do seu conjunto de maçãs com um subconjunto dos &lt;a href="http://en.wikipedia.org/wiki/Natural_numbers"&gt;números naturais&lt;/a&gt;, como na figura abaixo:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/4apple-745936.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/4apple-745931.jpg" alt="" border="0" /&gt;&lt;/a&gt;Se você criou uma bijeção do seu conjunto com o conjunto dos quatro primeiros naturais, então você efetivamente contou seu conjunto e concluiu que ele tem quatro elementos. O truque do Cantor foi perceber que você podia estender esse conceito, e criar bijeções com todos os naturais, ao invés de só algum subconjunto. E isso leva a resultados que são bem pouco intuitivos!&lt;br /&gt;&lt;br /&gt;Por exemplo, o conjunto dos números pares. A intuição do iniciante é que esse conjunto tem a metade dos elementos dos naturais. Mas não é verdade, porque você pode construir uma bijeção entre os dois:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/Nat_Even-700158.gif"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/Nat_Even-700149.gif" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Para cada natural você tem um par correspondente, para cada par você tem um natural. Pelo raciocínio do Cantor, então esses dois conjuntos tem o mesmo número de elementos, o que também mostra que, pelo menos quando você está lidando com o infinito, a parte pode ser igual ao todo.&lt;br /&gt;&lt;br /&gt;Indo além, o Cantor também mostrou que os &lt;a href="http://en.wikipedia.org/wiki/Rational_numbers"&gt;Números Racionais&lt;/a&gt; também são do mesmo tamanho dos Naturais. Quem quiser repetir a demonstração original dele, pode tentar fazer o problema &lt;a href="http://www.spoj.pl/problems/CANTON/"&gt;CANTON&lt;/a&gt; do spoj, que pede para você criar a bijeção. Uma maneira alternativa é utilizar uma construção inventada pelo Gödel: a cada racional p/q, você associa o natural 2&lt;sup&gt;p&lt;/sup&gt;3&lt;sup&gt;q&lt;/sup&gt;, e pra transformar o natural de volta numa racional, você usa a fatoração única que o &lt;a href="http://en.wikipedia.org/wiki/Fundamental_theorem_of_arithmetic"&gt;Teorema Fundamental da Aritmética&lt;/a&gt; te garante.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/buzz-723744.JPG"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/buzz-723741.JPG" alt="" border="0" /&gt;&lt;/a&gt;Quando um conjunto tem o mesmo tamanho dos naturais, diz-se que ele tem cardinalidade &lt;a href="http://en.wikipedia.org/wiki/Aleph-zero"&gt;aleph-zero&lt;/a&gt;, ou ainda, que é um conjunto &lt;a href="http://en.wikipedia.org/wiki/Countable_set"&gt;contável&lt;/a&gt;. Mas todos os conjuntos infinitos são contáveis? Não! O Cantor também mostrou que o conjunto dos &lt;a href="http://en.wikipedia.org/wiki/Real_numbers"&gt;Números Reais&lt;/a&gt; é maior que qualquer conjunto contável dado, utilizando um método chamado &lt;a href="http://en.wikipedia.org/wiki/Cantor%27s_diagonal_argument"&gt;argumento diagonal&lt;/a&gt;. Ou seja, os números reais são um tipo de infinito maior que o infinito dos naturais!&lt;br /&gt;&lt;br /&gt;Agora, se os racionais são contáveis, e os reais não são, fica claro que os culpados por existir um infinito maior que o outro são os &lt;a href="http://en.wikipedia.org/wiki/Irrational_number"&gt;irracionais&lt;/a&gt;. Mas quais irracionais? Existem vários tipos deles também. Por exemplo, alguns irracionais podem ser construídos como raízes de polinômios de coeficientes inteiros (diz-se que esses são irracionais &lt;a href="http://en.wikipedia.org/wiki/Algebraic_number"&gt;algébricos&lt;/a&gt;). Um exemplo é a &lt;a href="http://en.wikipedia.org/wiki/Golden_ratio"&gt;razão áurea&lt;/a&gt;, que é a maior raiz do polinômio x&lt;sup&gt;2&lt;/sup&gt;-x-1.&lt;br /&gt;&lt;br /&gt;Porém, os irracionais algébricos são contáveis também. Basta utilizar novamente o mesmo truque do Gödel, dessa vez você associa cada coeficiente do polinômio a um expoente de um número primo. Um polinômio como x&lt;sup&gt;2&lt;/sup&gt;+2x+1, por exemplo, escreveria-se como 2&lt;sup&gt;1&lt;/sup&gt;*3&lt;sup&gt;2&lt;/sup&gt;*5&lt;sup&gt;1&lt;/sup&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/pi-797516.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/pi-797512.jpg" alt="" border="0" /&gt;&lt;/a&gt;Ora, se os irracionais algébricos são contáveis, então o que torna os reais maiores que os naturais são os irracionais não-algébricos (ou &lt;a href="http://en.wikipedia.org/wiki/Transcendental_number"&gt;transcendentes&lt;/a&gt;). Mas mesmo entre esses existem vários tipos. O &lt;a href="http://en.wikipedia.org/wiki/Pi"&gt;número Pi&lt;/a&gt;, por exemplo: você não consegue construí-lo como uma raiz de um polinômio, mas pode aproximá-lo com um algoritmo computacional, com tanta precisão quanto se queira. Dos números que podem ser aproximados por algoritmos, diz-se que são &lt;a href="http://en.wikipedia.org/wiki/Computable_numbers"&gt;números computáveis&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Surpreendentemente, os irracionais computáveis são contáveis também. A maneira simples de visualizar isso é da seguinte maneira: se existe um algoritmo que aproxima o número, então esse algoritmo pode ser implementado numa linguagem qualquer (como nos garante a &lt;a href="http://en.wikipedia.org/wiki/Church%E2%80%93Turing_thesis"&gt;tese de Church-Turing&lt;/a&gt;). Agora, imagine que você criou um binário que implementou esse algoritmo, e esse binário tem X bytes. Se você fizer um hexdump do binario e imprimi-lo, você vai ter na sua frente um número hexa de 2X dígitos (que é um número natural grandinho, mas ainda assim um natural).&lt;br /&gt;&lt;br /&gt;Mas se os irracionais computáveis são contáveis, quem são os não-contáveis? Existem números que não podem ser calculados por algoritmos? A resposta é sim, e um desses números é a &lt;a href="http://en.wikipedia.org/wiki/Chaitin%27s_constant"&gt;constante de Chaitin&lt;/a&gt;. A construção da constante é curiosa. Nós vimos que, a cada algoritmo possível, existe um natural associado. Calcule então a seguinte somatória: para cada algoritmo existente (cujo natural associado é n), se o algoritmo pára, você soma 2&lt;sup&gt;-n&lt;/sup&gt;, senão não soma nada. Ora, essa somatória não pode ser calculada por um algoritmo, porque o &lt;a href="http://en.wikipedia.org/wiki/Halting_problem"&gt;Teorema de Turing&lt;/a&gt; garante que você não pode construir um algoritmo que detecta se outro algoritmo pára. A constante de Chaitin, então, é um número não-computável.&lt;br /&gt;&lt;br /&gt;Mas os irracionais não-computáveis ainda não são o segredo do infinito real. Eu não consigo construir um algoritmo que aproxima a constante de Chaitin, mas no parágrafo acima eu consegui descrever exatamente do que a constante se trata. Os números que eu consigo descrever são &lt;a href="http://en.wikipedia.org/wiki/Definable_number"&gt;números definíveis&lt;/a&gt;, e, (surpresa), eles são contáveis também. O argumento é ainda mais simples, se eu posso escrever um arquivo texto que contenha a descrição do número, o hexdump desse arquivo também vai associar a descrição a um número natural.&lt;br /&gt;&lt;br /&gt;Então, os números que fazem o infinito dos reais ser maior que o infinito dos naturais são os números que não podemos construir, não podemos aproximar e não podemos descrever, ou seja, nem dá pra pensar sobre eles!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/apple-759860.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/apple-759844.jpg" alt="" border="0" /&gt;&lt;/a&gt;Se a essa altura sua cabeça deu tilt, então deixe-me concluir qual é a segunda coisa em comum entre Cantor, Gödel e Turing: os três ficaram doidos. De fato, Cantor achava que era um mensageiro de Deus, e terminou a vida num hospício. Gödel ficou paranóico com comida contaminada, e só comia o que a mulher preparava (quando a mulher ficou doente e internada num hospital, ele literalmente morreu de fome). E o Turing ficou tão deprimido que se matou, comendo uma maçã envenenada.&lt;br /&gt;&lt;br /&gt;Há quem diga que existe relação causal entre estudar o infinito e ficar doido. Se você é desse tipo, hum, então era melhor não ter lido esse post :)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;(Esse post é dedicado ao &lt;a href="http://jkogler.wordpress.com/2008/05/10/henrique-schutzer-del-nero/"&gt;prof. Henrique&lt;/a&gt;, in memorian, líder do nosso grupo de doidos, que se reunia toda sexta na USP para estudar matemática, computação e ciências cognitivas. Três vivas aos que estudam o infinito, e além!)&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/05/ao-infinito-e-alm.html' title='Ao Infinito e Além'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=3895838495750595617' title='8 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/3895838495750595617'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/3895838495750595617'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-4697975770811953600</id><published>2008-05-09T23:30:00.004-03:00</published><updated>2008-05-10T01:08:24.299-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='videogames'/><title type='text'>All your game are belong to us</title><content type='html'>Quando eu ainda morava em São Paulo, todo ano eu ministrava uma das aulas da disciplina &lt;a href="http://www.poli.usp.br/Organizacao/Departamentos/desdisciplinapg.asp?sgldis=PSI5015"&gt;PSI-5015&lt;/a&gt; da Poli, falando sobre a história dos videogames. Morando em Belo Horizonte, eu não tive como apresentar a palestra na Poli, mas eu usei o conteúdo para fazer uma &lt;a href="http://research.google.com/video.html"&gt;Tech Talk&lt;/a&gt; no Google. Eu disponibilizei os slides abaixo, para quem não teve oportunidade de ver a palestra ao vivo:&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;iframe src="http://docs.google.com/EmbedSlideshow?docid=dft52565_2hs8mspf3" frameborder="0" height="342" width="410"&gt;&lt;/iframe&gt;&lt;/center&gt;&lt;br /&gt;É uma pena que em apenas uma hora não seja possível falar tanto quanto eu gostaria. Se tivesse mais tempo, eu ainda gostaria de citar o &lt;a href="http://www.system16.com/hardware.php?id=691"&gt;Time Traveler&lt;/a&gt; da Sega, por ser o primeiro a utilizar hologramas (na verdade, pseudo-hologramas), e também o &lt;a href="http://en.wikipedia.org/wiki/Dance_Dance_Revolution"&gt;Dance Dance Revolution&lt;/a&gt; e o &lt;a href="http://en.wikipedia.org/wiki/GuitarFreaks"&gt;GuitarFreaks&lt;/a&gt; da Konami, por iniciarem as manias de jogos de dança e jogos de guitarra, respectivamente.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/05/all-your-game-are-belong-to-us.html' title='All your game are belong to us'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=4697975770811953600' title='5 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/4697975770811953600'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/4697975770811953600'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-1150090145634766805</id><published>2008-05-03T16:27:00.005-03:00</published><updated>2008-06-16T20:44:40.441-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monte carlo'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='criptologia'/><title type='text'>Paranóia x Matemática</title><content type='html'>No &lt;a href="http://www.ricbit.com/2008/04/meta-assinatura.html"&gt;último post&lt;/a&gt; eu falei sobre Criptografia, então agora, pra balancear, o tópico é &lt;a href="http://en.wikipedia.org/wiki/Cryptanalysis"&gt;Criptanálise&lt;/a&gt;. Semana passada, a polícia prendeu uma gangue que estava instalando os mini-notebooks &lt;a href="http://en.wikipedia.org/wiki/ASUS_Eee_PC"&gt;Eee PC&lt;/a&gt; dentro de caixas automáticos, para roubar senhas dos usuários. O vídeo com a matéria pode ser visto abaixo:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;object height="392" width="460"&gt;&lt;param value="http://video.globo.com/Portal/videos/cda/player/player.swf" name="movie"&gt;&lt;param value="high" name="quality"&gt;&lt;param value="midiaId=818478&amp;amp;autoStart=false&amp;amp;width=460&amp;amp;height=392" name="FlashVars"&gt;&lt;embed flashvars="midiaId=818478&amp;amp;autoStart=false&amp;amp;width=460&amp;amp;height=392" type="application/x-shockwave-flash" quality="high" src="http://video.globo.com/Portal/videos/cda/player/player.swf" height="392" width="460"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Eu tenho certeza que um monte de gente deve ter visto a matéria e pensado "omfg nunca mais vou usar caixas automáticos kthxbye", mas na verdade, mesmo com o notebook lá dentro, não é tão fácil conseguir roubar a senha!&lt;br /&gt;&lt;br /&gt;Se o seu banco for como o meu, você não digita a sua senha diretamente, ao invés disso, a máquina associa dois dígitos para cada botão, e você aperta os botões correspondentes à sua senha. Assim, se algum xereta estiver atrás de você olhando, ele não vai conseguir descobrir sua senha, e isso vale também se tiver um notebook dentro da máquina registrando o que você digita.&lt;br /&gt;&lt;br /&gt;Então esse sistema é à prova de sniffers? Não! Um jeito de quebrar esse sistema é fazendo várias observações. Se o xereta te olhar uma única vez, ele não consegue descobrir sua senha, mas reduz bastante as possibilidades. Se a senha tiver quatro dígitos, uma única observação reduz de dez mil possibilidades para apenas 16. Se ele olhar uma segunda vez,  pode ser que consiga informação suficiente para reduzir ainda mais o espaço, e, eventualmente, repetindo o processo, ele pode conseguir deduzir a senha.&lt;br /&gt;&lt;br /&gt;Para conseguir reconstruir computacionalmente a senha, tudo que ele precisa fazer é resolver uma matriz de &lt;a href="http://www.ricbit.com/2008/04/domino-dancing.html"&gt;exact cover&lt;/a&gt; (na verdade outros métodos podem ser usados, mas eu sou &lt;strike&gt;preguiçoso&lt;/strike&gt; adepto da orientação à objeto e do reuso de código pronto). Assuma n observações: para cada um dos 4 dígitos da senha há dez possibilidades, então você tem 40 linhas. Além disso, para cada observação, você precisa garantir que os quatro dígitos são consistentes, o que dá 4n colunas, e ainda mais 4 colunas extras para garantir que um único número estará associado a cada dígito da senha. No total, a matriz terá 40x(4n+4) elementos.&lt;br /&gt;&lt;br /&gt;E quantas observações ele precisa? Isso não dá pra saber a priori, depende de como os números foram sorteados. Se a máquina repetir sempre a mesma distribuição, ele nunca vai conseguir deduzir a senha (mas também nem precisaria, pois aí ele só precisa apertar os mesmos botões que você). Por outro lado, se você tiver azar, pode ser que só duas observações sejam suficientes, como no caso abaixo:&lt;br /&gt;&lt;br /&gt;observação 1:&lt;br /&gt;  senha CADA, botões A=(6 2) B=(5 8) C=(4 3) D=(1 9) E=(7 0)&lt;br /&gt;observação 2:&lt;br /&gt;  senha CAAC, botões A=(1 2) B=(8 4) C=(6 3) D=(5 0) E=(7 9)&lt;br /&gt;&lt;br /&gt;Nesse exemplo, dá pra ver claramente que a única senha consistente com os dados é 3216. Se o ladrão for levemente mais esperto, ele pode até perceber que não precisa fazer observações suficientes para que a senha seja única, basta que ele reduza o espaço de possibilidades para 3 ou menos (já que ele pode chutar 3 senhas antes da máquina bloquear o cartão).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/roleta350-702531.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/roleta350-702518.jpg" alt="" border="0" /&gt;&lt;/a&gt;Embora não seja possível calcular a priori quantas observações são necessárias, é perfeitamente possível calcular qual é o &lt;a href="http://en.wikipedia.org/wiki/Expected_value"&gt;valor esperado&lt;/a&gt; dessa distribuição. Como eu sou &lt;strike&gt;preguiçoso&lt;/strike&gt; adepto das simulações computacionais, ao invés de calcular as probabilidades na unha, eu escrevi uma &lt;a href="http://en.wikipedia.org/wiki/Monte_Carlo_method"&gt;simulação de Monte Carlo&lt;/a&gt; para calcular esse valor. O resultado é que, para uma senha de 4 dígitos, um ladrão que queira achar a senha exata precisa de 2.3 observações, enquanto que o ladrão esperto, que se contenta em reduzir pra 3 ou menos possibilidades, precisa de apenas 2.1 observações.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/montecarlo.cc"&gt;Código do simulador de Monte Carlo&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;A lição prática dessa análise é que, se você estiver desconfiado que o caixa tem um sniffer, não precisa ficar preocupado, desde que digite sua senha uma única vez. Se você precisar fazer uma outra operação, é melhor fazer em outra máquina.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/05/parania-x-matemtica.html' title='Paranóia x Matemática'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=1150090145634766805' title='4 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/1150090145634766805'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/1150090145634766805'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-5120581808165121277</id><published>2008-04-26T21:17:00.005-03:00</published><updated>2008-04-27T01:34:13.784-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='strange loop'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='criptologia'/><title type='text'>A Meta-Assinatura</title><content type='html'>Como eu já disse antes, eu sou uma criatura que se empolga fácil. Ainda não tinha feito nem duas semanas que eu e o &lt;a href="http://scholar.google.com.br/scholar?hl=en&amp;amp;lr=&amp;amp;q=%22F%C3%A1bio+J.+Ayres%22"&gt;Fábio&lt;/a&gt; tínhamos entrado na &lt;a href="http://www.poli.usp.br/"&gt;Poli&lt;/a&gt;, e nós já estávamos procurando iniciação científica pra fazer. Depois de alguma procura, achamos uma legal: o &lt;a href="http://www.ime.usp.br/%7Ert/"&gt;Routo Terada&lt;/a&gt; estava procurando alunos pra estudar &lt;a href="http://en.wikipedia.org/wiki/Cryptography"&gt;Criptologia&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;O nosso medo inicial era que o Routo não quisesse aceitar dois alunos de primeiro ano, mas isso foi mais simples que esperávamos: "Ah, eu posso passar uma tarefa simples pra vocês. O &lt;a href="http://www.schneier.com/"&gt;Schneier&lt;/a&gt; acabou de publicar um algoritmo novo chamado &lt;a href="http://en.wikipedia.org/wiki/Blowfish_%28cipher%29"&gt;Blowfish&lt;/a&gt;, vocês tem seis meses pra quebrar". É claro que não conseguimos quebrar o Blowfish, mas aprendemos um bocado no processo :)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://en.wikipedia.org/wiki/Digital_signature"&gt;&lt;br /&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/newton2-733468.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;a href="http://en.wikipedia.org/wiki/Digital_signature"&gt;Assinaturas digitais&lt;/a&gt;, por exemplo. O &lt;a href="http://en.wikipedia.org/wiki/Isaac_Newton"&gt;Isaac Newton&lt;/a&gt;, quando queria provar que algum manuscrito era dele, podia simplesmente assiná-lo com uma pena; mas o &lt;a href="http://en.wikipedia.org/wiki/Stephen_Hawking"&gt;Stephen Hawking&lt;/a&gt; não pode fazer isso! Pra ele, o ideal são as assinaturas digitais. Para assinar digitalmente, você precisa de algum tipo de problema que seja difícil de resolver, mas que seja fácil de checar se foi resolvido (como a &lt;a href="http://en.wikipedia.org/wiki/Factorization"&gt;fatoração de números&lt;/a&gt;, ou o &lt;a href="http://en.wikipedia.org/wiki/Knapsack_problem"&gt;problema da sacola&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Um exemplo simples de como isso funciona me veio à mente algum tempo atrás, enquanto eu lia um livro do &lt;a href="http://www.cogs.indiana.edu/people/homepages/hofstadter.html"&gt;Hofstadter&lt;/a&gt; (se você não conhece o Hofstadter, tem &lt;a href="http://video.globo.com/Videos/Player/Noticias/0,,GIM804293-7823-O+PENSADOR+AMERICANO+DOUGLAS+HOFSTADTER,00.html"&gt;uma entrevista dele&lt;/a&gt; para a rede Globo disponível online). Suponha que eu fiz uma grande descoberta e quero divulgar isso para o mundo:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; color: rgb(0, 102, 0);"&gt;O Ricardo sabe onde está o Bin Laden.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Embora tenha meu nome ali, qualquer um pode alterar e trocar o nome, então não tem como garantir que fui eu que escrevi:&lt;span style="font-style: italic;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-style: italic; color: rgb(0, 102, 0);"&gt;O Wilerson sabe onde está o Bin Laden.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;O método que eu bolei, e que na falta de nome melhor eu chamo de Meta-Assinatura, consiste em adicionar informação auto-referente à sua sentença:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic; color: rgb(0, 102, 0);"&gt;O Ricardo afirma que sabe onde esta o Bin Laden, nesta sentenca com dezessete letras a, vinte e sete letras e, seis letras i, sete letras o, quatro letras u e uma letra x.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Confira que a contagem de letras está certinha. Dessa maneira, o Wilerson não pode trocar o nome na frase, pois se ele trocar, a contagem de letras vai mudar. Assim, a frase com meta-assinatura garante quem é o autor. Nesse método, contar as letras é muito simples, mas consertar a frase para o número de letras bater, é bem difícil (quer dizer, só com seis letras e algum esforço, até dá pra consertar a frase, mas se você usar o alfabeto inteiro na sua contagem, aí fica realmente complexo).&lt;br /&gt;&lt;br /&gt;Para criar a frase com meta-assinatura, você não pode tentar procurar a solução por força bruta, porque demora demais. Uma solução mais rápida é criar uma função que conte as letras da sentença e troque os números correspondentes, e depois cruzar os dedos e torcer pro &lt;a href="http://en.wikipedia.org/wiki/Fixed_point_%28mathematics%29"&gt;ponto fixo&lt;/a&gt; dessa função ser um &lt;a href="http://en.wikipedia.org/wiki/Attractor"&gt;atrator&lt;/a&gt;. O script em python abaixo faz isso, tomando o cuidado de detectar loops para não ficar preso:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/pangram.py"&gt;Meta-Assinatura em python&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/fox-767986.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/fox-767977.jpg" alt="" border="0" /&gt;&lt;/a&gt;Eu ainda não consegui assinar uma sentença usando todas as letras do alfabeto (ie, gerando um &lt;a href="http://en.wikipedia.org/wiki/The_quick_brown_fox_jumps_over_the_lazy_dog"&gt;pangram&lt;/a&gt;), porque esse método não garante convergência. Se você conseguir, me avise :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/04/meta-assinatura.html' title='A Meta-Assinatura'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=5120581808165121277' title='7 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/5120581808165121277'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/5120581808165121277'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-8439018119940034061</id><published>2008-04-21T17:16:00.005-03:00</published><updated>2008-04-27T11:24:30.857-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computação analógica'/><category scheme='http://www.blogger.com/atom/ns#' term='ordenação'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><title type='text'>Otimização com ábacos</title><content type='html'>Semana passada ficou pronta mais uma réplica da &lt;a href="http://en.wikipedia.org/wiki/Difference_engine"&gt;Máquina de Diferenças n°2&lt;/a&gt; do &lt;a href="http://en.wikipedia.org/wiki/Charles_Babbage"&gt;Babbage&lt;/a&gt;. Essa é a segunda a ser construída a partir da especificação original, pesa 5 toneladas, e ficará em exposição no &lt;a href="http://www.computerhistory.org/babbage/"&gt;Computer History Museum&lt;/a&gt; de Mountain View (que eu &lt;a href="http://picasaweb.google.com.br/bluepenguin/ComputerHistoryMuseum"&gt;visitei no ano passado&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/Babbage-Difference-Engine-No.-2-759756.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/Babbage-Difference-Engine-No.-2-759742.jpg" alt="" border="0" /&gt;&lt;/a&gt;Construir a partir da especificação original é um bocado caro, e reservado só pra museus e milionários mesmo. Porém, isso não impediu um hobbysta de criar sua própria &lt;a href="http://acarol.woz.org/"&gt;máquina de diferenças usando LEGO&lt;/a&gt;, o que chega a ser até mais impressionante. Pra quem quiser simular a máquina de diferenças apenas em software, pode tentar o problema &lt;a href="http://www.spoj.pl/problems/CMPLS/"&gt;CMPLS&lt;/a&gt; no spoj, que é exatamente isso.&lt;br /&gt;&lt;br /&gt;O que talvez não seja muito óbvio é que, apesar de usar apenas processamento mecânico, a máquina de diferenças é um computador digital (ela possui um &lt;a href="http://en.wikipedia.org/wiki/Clock_signal"&gt;clock&lt;/a&gt;, dado pela rotação de um eixo interno, e executa uma adição com &lt;a href="http://en.wikipedia.org/wiki/Carry_flag"&gt;carry&lt;/a&gt; a cada quatro rotações). Já os computadores analógicos podem ser bem mais bizarros, como o computador de sabão do &lt;a href="http://www.ricbit.com/2008/04/otimizando-com-gua-e-sabo.html"&gt;post anterior&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Computadores analógicos possuem uma vantagem sobre os digitais: não estão presos aos mesmos limites computacionais. É bastante simples construir, por exemplo, um circuito, utilizando &lt;a href="http://en.wikipedia.org/wiki/Operational_amplifier"&gt;amplificadores operacionais&lt;/a&gt;, que resolva &lt;a href="http://www.owlnet.rice.edu/%7Eelec301/Projects99/anlgcomp/"&gt;equações diferenciais&lt;/a&gt; complexas em tempo bem inferior a um computador digital (embora o processo prejudique a precisão).&lt;br /&gt;&lt;br /&gt;Um exemplo mais curioso é o &lt;a href="http://en.wikipedia.org/wiki/Sorting_algorithm"&gt;problema da ordenação&lt;/a&gt; de um conjunto de números. É possível demonstrar que um computador digital, usando como elemento básico de computação a comparação de dois valores, não pode ser melhor que O(n log n) (essa demostração está em qualquer livro básico de algoritmos, como o &lt;a href="http://books.google.com/books?id=NLngYyWFl_YC"&gt;Cormen&lt;/a&gt;). Mas os computadores analógicos não tem essa restrição, sendo que existe até mesmo um algoritmo que resolve em O(1)!&lt;br /&gt;&lt;br /&gt;Uma implementação simples desse algoritmo é com ábacos: primeiro você dispõe os números que você quer ordenar na &lt;a href="http://en.wikipedia.org/wiki/Unary_numeral_system"&gt;base unária&lt;/a&gt;, como estão os números 2, 4, 1, 3, 3 na figura abaixo:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/bead1-753970.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/bead1-753967.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Depois, basta levantar o ábaco e deixar a gravidade fazer seu serviço:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/bead2-718420.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/bead2-718416.png" alt="" border="0" /&gt;&lt;/a&gt;Impressionante, não? Os números ficam perfeitamente ordenados. Apesar de ser um truque muito simples, esse método só foi inventado em 2002. No &lt;a href="http://www.cs.auckland.ac.nz/%7Ejaru003/research/publications/journals/beadsort.pdf"&gt;paper original&lt;/a&gt;, os autores chamam o método de Bead Sort e sugerem, além da implementação com ábacos, outras implementações (com redes resistivas, &lt;a href="http://en.wikipedia.org/wiki/Cellular_automaton"&gt;autômatos celulares&lt;/a&gt; e matrizes de &lt;a href="http://en.wikipedia.org/wiki/Flip-flop_%28electronics%29"&gt;flip-flops&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Como o Bead Sort usa operações analógicas, não dá pra analisar de verdade a complexidade computacional dele. Quando fazemos análises de &lt;a href="http://en.wikipedia.org/wiki/Big-oh"&gt;big-oh&lt;/a&gt;, queremos saber quantos passos o algoritmo leva pra terminar, e o Bead Sort tem só um passo (levantar o ábaco), sendo que nesse aspecto ele é O(1) mesmo. Mas intuitivamente, o que queremos quando fazemos análise de complexidade é descobrir como o tempo de execução varia com o tamanho da entrada. Desse ponto de vista, a complexidade é O(sqrt(n)), se você considerar o tempo que uma bolinha leva pra ser puxada pela gravidade (no vácuo), ou então O(n), se você levar em conta que a bolinha vai atingir uma &lt;a href="http://en.wikipedia.org/wiki/Terminal_velocity"&gt;velocidade terminal&lt;/a&gt; devido à resistência do ar.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/04/otimizando-com-bacos.html' title='Otimização com ábacos'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=8439018119940034061' title='4 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8439018119940034061'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8439018119940034061'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-8335110978935709862</id><published>2008-04-20T00:34:00.007-03:00</published><updated>2008-04-27T11:24:05.142-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='grafos'/><category scheme='http://www.blogger.com/atom/ns#' term='computação analógica'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><title type='text'>Otimização com água e sabão</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/ciencia2-735444.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/ciencia2-735442.jpg" alt="" border="0" /&gt;&lt;/a&gt;Ano passado fui pela primeira vez à &lt;a href="http://www.eciencia.usp.br/"&gt;Estação Ciência&lt;/a&gt;, em São Paulo, e fiquei espantado com a qualidade. Eu já visitei o &lt;a href="http://www.nhm.ac.uk/"&gt;Natural History Museum&lt;/a&gt; de Londres, e o &lt;a href="http://www.amnh.org/"&gt;American Museum of Natural History&lt;/a&gt; em New York, então eu digo, com conhecimento de causa, que a Estação Ciência não fica nada a dever aos museus de ciência do primeiro mundo. Tem dinossauros, planetário, simulador de terremoto, e até simulador de tsunami (que não tinha nos museus lá de cima).&lt;br /&gt;&lt;br /&gt;Na verdade, o museu brasileiro tem uma vantagem sobre os outros. Por lá, a média é de, mais ou menos, uns quarenta visitantes por guia, e aqui a média é de três guias por visitante! Se você tiver bastante tempo pra visitar, os guias irão lhe dar explicações extremamente detalhadas das exposições.&lt;br /&gt;&lt;br /&gt;A minha predileta foi na seção de matemática experimental, onde fizemos a experiência do sabão. Você pega duas placas paralelas quaisquer, coloca quatro pregos nelas, e mergulha num balde de água com sabão.  Eu estava esperando que o sabão grudasse nos pregos e fizesse um quadrilátero, mas na verdade o que ele faz é uma estrutura em duplo Y:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/steiner-779726.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/steiner-779703.jpg" alt="" border="0" /&gt;&lt;/a&gt;(Você pode fazer em casa esse experimento, mas para melhores resultados, coloque um pouco de glicerina na água. Experimente também com outras quantidades de pregos, e com figuras tridimensionais).&lt;br /&gt;&lt;br /&gt;A explicação é que o sabão, como bom sistema físico, vai procurar a posição de energia mínima, que nesse caso é equivalente a minimizar a soma dos comprimentos da película. Anedoticamente, esse problema é conhecido como o problema da estrada (como ligar quatro cidades com estradas, gastando a menor quantidade possível de asfalto?), e a solução ótima é conhecida como &lt;a href="http://en.wikipedia.org/wiki/Steiner_tree"&gt;Steiner Tree&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;À primeira vista pode parecer que a Steiner Tree é equivalente à &lt;a href="http://en.wikipedia.org/wiki/Minimum_spanning_tree"&gt;Minimum Spanning Tree&lt;/a&gt;, mas não é. Pra calcular a spanning tree, você só pode usar os pontos do grafo, já na Steiner tree você pode adicionar pontos novos. Isso faz uma diferença enorme na complexidade: existem algoritmos quase lineares para resolver a spanning tree, já a Steiner tree é &lt;a href="http://en.wikipedia.org/wiki/NP-complete"&gt;NP-completa&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Na verdade, há quem diga que isso é uma prova de que P=NP, como os autores &lt;a href="http://arxiv.org/abs/cs/0406056"&gt;desse paper no Arxiv&lt;/a&gt;. Segundo eles, existe um computador analógico feito com sabão que resolve um problema NP-completo em tempo polinomial, logo P=NP :)&lt;br /&gt;&lt;br /&gt;Pra quem quiser brincar com Steiner trees, o problema &lt;a href="http://www.spoj.pl/problems/ELC/"&gt;ELC&lt;/a&gt; do spoj pede pra resolver uma instância com 3k pontos. Como isso é demais para um brute force, você vai ter que aproximar: por exemplo, iterando a partir de uma spanning tree; ou então, calculando a &lt;a href="http://en.wikipedia.org/wiki/Delaunay_triangulation"&gt;triangulação de Delaunay&lt;/a&gt; e depois resolvendo a Steiner tree local de cada triângulo.&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/04/otimizando-com-gua-e-sabo.html' title='Otimização com água e sabão'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=8335110978935709862' title='8 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8335110978935709862'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/8335110978935709862'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-7070962175814731071</id><published>2008-04-19T00:51:00.006-03:00</published><updated>2008-04-20T12:05:22.048-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='metaprogramming'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><category scheme='http://www.blogger.com/atom/ns#' term='complexidade'/><title type='text'>Erdös e os logaritmos</title><content type='html'>Essa foi uma semana triste para a ciência, com a morte de dois grandes nomes: &lt;a href="http://en.wikipedia.org/wiki/Edward_Lorenz"&gt;Edward Lorenz&lt;/a&gt; (criador dos &lt;a href="http://en.wikipedia.org/wiki/Lorenz_attractor"&gt;atratores de Lorenz&lt;/a&gt; e criador da expressão "&lt;a href="http://en.wikipedia.org/wiki/Butterfly_effect"&gt;efeito borboleta&lt;/a&gt;"), e &lt;a href="http://en.wikipedia.org/wiki/John_Archibald_Wheeler"&gt;John Wheeler&lt;/a&gt; (orientador do &lt;a href="http://en.wikipedia.org/wiki/Richard_Feynman"&gt;Feynman&lt;/a&gt; e criador das expressões "&lt;a href="http://en.wikipedia.org/wiki/Black_hole"&gt;buraco negro&lt;/a&gt;" e "&lt;a href="http://en.wikipedia.org/wiki/Wormhole"&gt;buraco de minhoca&lt;/a&gt;"). Sempre que morre um cientista famoso, eu fico pensando que perdi a oportunidade de conhecê-lo pessoalmente pra agradecer pelo seu trabalho. Mas alguns cientistas eu consegui conhecer em vida, um deles foi o &lt;a href="http://en.wikipedia.org/wiki/Paul_Erd%C5%91s"&gt;Paul Erdös&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/erdos-780857.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/erdos-780855.jpg" alt="" border="0" /&gt;&lt;/a&gt;Em novembro de 94, o Erdös deu uma palestra na USP, e, sabendo que ele estaria lá, fui correndo assistir. Pra quem não conhece, o Erdös foi o segundo matemático mais prolífico da história, só o &lt;a href="http://en.wikipedia.org/wiki/Leonhard_Euler"&gt;Euler&lt;/a&gt; publicou mais que ele (embora anedoticamente ele seja mais conhecido pela brincadeira dos &lt;a href="http://en.wikipedia.org/wiki/Erd%C5%91s_number"&gt;números de Erdös&lt;/a&gt;). É claro que um estudante de primeiro ano, como eu era, não tinha a menor chance de entender os detalhes da palestra que ele deu. Na verdade, o que me deixou impressionado foi que, em um dado momento, ele demonstrou que o upper bound de uma função era O(log log log log n), e eu pensei comigo mesmo que um dia ainda ia encadear logaritmos como ele fazia.&lt;br /&gt;&lt;br /&gt;O tempo passou e eu ainda não consegui encadear quatro logaritmos, mas outro dia eu consegui pelo menos dois! Foi quando eu estava otimizando um código, e o seguinte problema apareceu no meio de um &lt;a href="http://en.wikipedia.org/wiki/Inner_loop"&gt;inner loop&lt;/a&gt;: achar a menor potência de 10 que seja maior ou igual a um inteiro dado. A implementação simples é a abaixo, vamos assumir que os inteiros em questão sejam de 64 bits, e que f(0)=1 por convenção:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;unsigned&amp;nbsp;long&amp;nbsp;long&amp;nbsp;simple_power10(unsigned&amp;nbsp;long&amp;nbsp;long&amp;nbsp;i)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;unsigned&amp;nbsp;long&amp;nbsp;long&amp;nbsp;current&amp;nbsp;=&amp;nbsp;10000000000000000000ULL;&lt;br&gt;&amp;nbsp;&amp;nbsp;while&amp;nbsp;(true)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(current&amp;nbsp;&amp;lt;=&amp;nbsp;i)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;current&amp;nbsp;+&amp;nbsp;!current;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current&amp;nbsp;/=&amp;nbsp;10;&lt;br&gt;&amp;nbsp;&amp;nbsp;}&lt;br&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Esse código é razoavelmente rápido, roda em O(log n). O ideal seria rodar em O(1), fazendo uma tabela com os valores pré-calculados. Porém, uma tabela assim é inviável na faixa de valores de 64 bits. Um caminho mais esperto é usar uma &lt;a href="http://en.wikipedia.org/wiki/Binary_search"&gt;busca binária&lt;/a&gt; para achar o valor correto:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;if&amp;nbsp;(i&amp;nbsp;&amp;lt;&amp;nbsp;100)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(i&amp;nbsp;&amp;lt;&amp;nbsp;10)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;1;&lt;br&gt;&amp;nbsp;&amp;nbsp;else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;10;&amp;nbsp;&lt;br&gt;}&amp;nbsp;else&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(i&amp;nbsp;&amp;lt;&amp;nbsp;1000)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;100;&lt;br&gt;&amp;nbsp;&amp;nbsp;else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;1000;&lt;br&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Essa idéia é bem melhor, mas o problema agora é escrevê-la. Para a faixa de 64 bits, os ifs aninhados ficam muito longos, e um cara distraído como eu certamente iria errar alguma coisa na implementação. Felizmente, existe uma solução: template metaprogramming!&lt;br /&gt;&lt;br /&gt;Usualmente pensamos em template metaprogramming para fazer cálculos em tempo de compilação, mas ele pode ser usado nesse caso também, pra gerar o código da busca binária. E ainda ganhamos uma vantagem, o código pode ser usado para qualquer tipo, não ficando preso ao unsigned long long, como no primeiro caso. Para implementar, começamos fazendo um template para gerar potências de dez:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;template&amp;lt;class&amp;nbsp;T,&amp;nbsp;const&amp;nbsp;int&amp;nbsp;n&amp;gt;&lt;br&gt;struct&amp;nbsp;p10&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;const&amp;nbsp;static&amp;nbsp;T&amp;nbsp;value&amp;nbsp;=&amp;nbsp;T(10)&amp;nbsp;*&amp;nbsp;p10&amp;lt;T,&amp;nbsp;n-1&amp;gt;::value;&lt;br&gt;};&lt;br&gt;&lt;br&gt;template&amp;lt;class&amp;nbsp;T&amp;gt;&lt;br&gt;struct&amp;nbsp;p10&amp;lt;T,&amp;nbsp;0&amp;gt;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;const&amp;nbsp;static&amp;nbsp;T&amp;nbsp;value&amp;nbsp;=&amp;nbsp;T(1);&lt;br&gt;};&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Com ele em mãos, podemos fazer a busca binária propriamente dita:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;template&amp;lt;class&amp;nbsp;T,&amp;nbsp;const&amp;nbsp;int&amp;nbsp;start,&amp;nbsp;const&amp;nbsp;int&amp;nbsp;len&amp;gt;&lt;br&gt;struct&amp;nbsp;compare10&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;static&amp;nbsp;T&amp;nbsp;compare(const&amp;nbsp;T&amp;nbsp;x)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(x&amp;nbsp;&amp;gt;=&amp;nbsp;p10&amp;lt;T,&amp;nbsp;start&amp;nbsp;+&amp;nbsp;len/2&amp;gt;::value)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;compare10&amp;lt;T,&amp;nbsp;start&amp;nbsp;+&amp;nbsp;len/2,&amp;nbsp;len/2&amp;gt;::compare(x);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;compare10&amp;lt;T,&amp;nbsp;start,&amp;nbsp;len/2&amp;gt;::compare(x);&lt;br&gt;&amp;nbsp;&amp;nbsp;}&lt;br&gt;};&lt;br&gt;&lt;br&gt;template&amp;lt;class&amp;nbsp;T,&amp;nbsp;const&amp;nbsp;int&amp;nbsp;start&amp;gt;&lt;br&gt;struct&amp;nbsp;compare10&amp;lt;T,&amp;nbsp;start,&amp;nbsp;1&amp;gt;&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;static&amp;nbsp;T&amp;nbsp;compare(const&amp;nbsp;T&amp;nbsp;x)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;p10&amp;lt;T,&amp;nbsp;start&amp;gt;::value;&lt;br&gt;&amp;nbsp;&amp;nbsp;}&lt;br&gt;};&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;E depois basta fazer o &lt;a href="http://en.wikipedia.org/wiki/Bootstrapping_%28computing%29"&gt;bootstrap&lt;/a&gt;, usando agora uma função pouco conhecida da biblioteca padrão do C++: o digits10, que volta a quantidade máxima de dígitos decimais que cabe num tipo qualquer.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;template&amp;lt;class&amp;nbsp;T&amp;gt;&lt;br&gt;T&amp;nbsp;template_power10(T&amp;nbsp;x)&amp;nbsp;{&lt;br&gt;&amp;nbsp;&amp;nbsp;return&amp;nbsp;compare10&amp;lt;T,&amp;nbsp;0,&amp;nbsp;numeric_limits&amp;lt;T&amp;gt;::digits10&amp;gt;::compare(x);&lt;br&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Abaixo, uma versão completa, já com benchmark, para comparar as duas versões. Na minha máquina, a versão com metaprogramming calcula um milhão de valores em metade do tempo da versão original. Isso é graças à complexidade reduzida da versão com metaprogramming, que é apenas O(log log n), com dois logaritmos, como eu queria demonstrar :)&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/power10.cc"&gt;Benchmark das duas versões&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/04/erds-e-os-logaritmos.html' title='Erdös e os logaritmos'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=7070962175814731071' title='1 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/7070962175814731071'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/7070962175814731071'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-627740469021493504</id><published>2008-04-15T01:30:00.003-03:00</published><updated>2008-04-15T03:00:16.790-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzle'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><title type='text'>Variações sobre um tema</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/toilet_sudoku-760381.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/toilet_sudoku-760379.jpg" alt="" border="0" /&gt;&lt;/a&gt;Pelos idos de 2006, um dos meus hobbies era resolver &lt;a href="http://en.wikipedia.org/wiki/Sudoku"&gt;Sudokus&lt;/a&gt;. Como eu sou uma criatura que se empolga fácil, tinha sudokus em papel, jogos de sudoku no Nintendo DS, sudokus em tudo quanto é lugar. Depois de algum tempo eu tive que desistir dos sudokus, mas não foi porque eu enjoei, foi porque eu não achava mais puzzles no meu nível!&lt;br /&gt;&lt;br /&gt;Os puzzles da Coquetel, por exemplo, podem ser resolvidos quase que exclusivamente com técnicas simples, e esses eu resolvia em poucos minutos. Só os puzzles marcados como "diabólicos" e "grande mestre" que precisavam de alguma técnica mais avançada, como &lt;a href="http://www.sudopedia.org/wiki/X-Wing"&gt;X-Wing&lt;/a&gt; e &lt;a href="http://www.sudopedia.org/wiki/Uniqueness_Test"&gt;unicidade de soluções&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Mas ainda assim eu não parei de comprar as revistinhas. O Hofstadter dizia, no &lt;a href="http://en.wikipedia.org/wiki/Metamagical_Themas"&gt;Metamagical Themas&lt;/a&gt;, que "Making variations on a theme is really the crux of creativity", e o povo dos sudokus levou isso a sério. Os sudokus tradicionais já não tinham mais graça, mas a Coquetel também publica variações sobre o tema, como os Sudokus Irregulares:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.ricbit.com/uploaded_images/irregular-764559.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://www.ricbit.com/uploaded_images/irregular-764555.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Nesse irregular 6x6 valem as mesmas regras dos sudokus normais, cada linha, coluna ou região precisa ter todos os números de 1 a 6. Mas a maioria das técnicas avançadas não funciona, então esses puzzles ainda têm graça pra mim.&lt;br /&gt;&lt;br /&gt;Computacionalmente, o sudoku irregular também pode ser resolvido com o exact cover. No caso desse irregular 6x6, a matriz resultante tem 216x144 elementos. Como eu já tinha a biblioteca de dancing links do post anterior, criar um solver para esse sudoku foi bem simples:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/irregular.cc"&gt;Solver do sudoku irregular 6x6&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/irregular.input"&gt;Entrada exemplo para o solver&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.ricbit.com/code/exactcover.h"&gt;Update da lib de exact cover&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Dessa vez eu mudei um pouco a api do exactcover.h, a versão original só permitia contar o número de soluções possíveis, agora ela é um template que recebe um &lt;a href="http://en.wikipedia.org/wiki/Function_object"&gt;functor&lt;/a&gt; que é chamado a cada solução encontrada. Como o template ficou mais genérico, agora você pode implementar a variação que quiser sobre o processamento das soluções :)&lt;div class="blogger-post-footer"&gt;&lt;script type="text/javascript"&gt;
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
&lt;/script&gt;
&lt;script type="text/javascript"&gt;
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
&lt;/script&gt;&lt;/div&gt;</content><link rel='alternate' type='text/html' href='http://www.ricbit.com/2008/04/variaes-sobre-um-tema.html' title='Variações sobre um tema'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6306509703738480474&amp;postID=627740469021493504' title='0 Comentários'/><link rel='replies' type='application/atom+xml' href='http://www.ricbit.com/atom.xml' title='Postar comentários'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/627740469021493504'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6306509703738480474/posts/default/627740469021493504'/><author><name>ricbit</name><uri>http://www.blogger.com/profile/17393980440854756685</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-6306509703738480474.post-7820468783713764965</id><published>2008-04-13T11:39:00.003-03:00</published><updated>2008-04-13T13:16:59.965-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='puzzle'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='cpp'/><title type='text'>Domino Dancing</title><content type='html'>Dois dos meus hobbies prediletos são resolver puzzles e desafios de programação. Por isso, é natural que eu me empolgue quando consigo fazer os dois ao mesmo tempo!&lt;br /&gt;&lt;br /&gt;O caso em questão é o problema &lt;a href="http://br.spoj.pl/problems/DOMINO/"&gt;DOMINO&lt;/a&gt; do &lt;a href="http://br.spoj.pl/"&gt;spojinho&lt;/a&gt;. O problema pede pra você resolver computacionalmente um puzzle clássico, que consiste de um tabuleiro 7x8 com números em cada casa, que deve ser completamente preenchido pelos 28 dominós. Esse puzzle é comum em coletâneas como a Denksel da &lt;a href="http://www.croco-puzzle.com/"&gt;Croco-Puzzle&lt;/a&gt;, e também tem disponível online em vários sites, como a applet java do &lt;a href="http://www.janko.at/Raetsel/Dominos/"&gt;Janko&lt;/a&gt;. Abaixo, um exemplo de puzzle no estado inicial e resolvido:&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&