tag:blogger.com,1999:blog-63065097037384804742024-03-05T09:50:38.536-03:00Brain DumpO que está passando pela cabeça do Ricbit no momento.Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.comBlogger70125tag:blogger.com,1999:blog-6306509703738480474.post-68043442216239263322021-04-16T22:50:00.002-03:002021-04-17T00:30:24.502-03:00As Figurinhas dos Vingadores são Honestas? (parte 2)<p>No <a href="https://blog.ricbit.com/2018/07/as-figurinhas-dos-vingadores-sao.html">último post do blog</a>, eu comprei vários pacotinhos de figurinhas dos Vingadores, para tentar responder à pergunta: as figurinhas são honestas? Após fazer dois testes de hipótese, eu concluí que as figurinhas possuem <b>distribuição uniforme</b>, então parece que sim. Mas tem uma pegadinha: para serem honestas, garantir que são uniformes não é o suficiente, ainda tem outro teste que precisamos fazer!</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3-DH6fYsBd45Vzihzi9430N0a3lkdKqxoPK7MIlE96MPs2A1Tph7O1eQpdMPAf1-JsJlAZZnyjf06zsFxUzYPNMZLPCEXJz1lW_z53jfm2SBmpwGNOGYsVqTXXejo8eSRLcGFjZQUYNc/s800/figurinhas_2021.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="625" data-original-width="800" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3-DH6fYsBd45Vzihzi9430N0a3lkdKqxoPK7MIlE96MPs2A1Tph7O1eQpdMPAf1-JsJlAZZnyjf06zsFxUzYPNMZLPCEXJz1lW_z53jfm2SBmpwGNOGYsVqTXXejo8eSRLcGFjZQUYNc/s320/figurinhas_2021.jpg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h3 style="text-align: left;">A Distribuição dos Pacotinhos</h3><div><br /></div><div>Para fazer a análise das figurinhas, eu comprei 106 pacotinhos na banca. Mas enquanto eu abria os pacotinhos, notei uma propriedade curiosa: todos os pacotinhos tinham sempre 4 figurinhas diferentes. Mas olha só, se os pacotinhos não possuem repetidas entre si, então as figurinhas podem até ser uniformes, mas não são <b>independentes</b>! Cada pacotinho tem quatro figurinhas, e se eu tirei a figurinha 42 em um pacotinho, então eu sei que as outras três não são a figurinha 42.</div><div><br /></div><div>Mas pode ter sido só o acaso né? Vai ver eu dei sorte de comprar exatamente 106 pacotinhos que não tinham repetidas. Mas felizmente nós sabemos como testar se foi sorte ou não, basta fazer o teste do ##\chi^2##, que vimos no <a href="https://blog.ricbit.com/2018/07/as-figurinhas-dos-vingadores-sao.html">último post</a>!</div><div><br /></div><div>Acho que o jeito mais fácil de testar é contar quantas figurinhas diferentes tem dentro do pacotinho. Quantos pacotinhos possíveis existem com 4 figurinhas diferentes? Esse é fácil, o álbum tem 180 figurinhas, então são ##180\choose 4## pacotinhos. Com 3 diferentes precisa de mais cuidado, existem ##180\choose 3## combinações de figurinhas, mas você precisa contar qual dessas três diferentes está repetida (os casos são AABC, ABBC, ABCC). Então para 3 diferentes são ##3 {180\choose 4}## pacotinhos.</div><div><br /></div><div>Com 2 diferentes também tem três casos: AAAB, AABB, ABBB, logo temos ##3{180\choose 2}## pacotinhos. Para uma única figurinha diferente, são apenas ##180\choose 1## pacotinhos. Agora podemos montar a tabela com quantos pacotinhos eu deveria ter visto, e quantos pacotinhos eu vi:</div><div><br />
<div class="post-table">
<table>
<tbody>
<tr></tr>
<tr><th></th><th>1 diferente</th><th>2 diferentes</th><th>3 diferentes</th><th>4 diferentes</th></tr>
<tr><td><b>Total de pacotinhos</b></td><td>##180\choose 1##</td><td>##3{180\choose 2}##</td><td>##3{180\choose 3}##</td><td>##180\choose 4##</td></tr>
<tr><td><b>Total (numérico)</b></td><td>180</td><td>48330</td><td>2867580</td><td>42296805</td></tr>
<tr><td><b>Total (normalizado)</b></td><td>3.98e-6</td><td>1.07e-3</td><td>0.634</td><td>0.936</td></tr>
<tr><td><b>Esperado (total*106)</b></td><td>422e-6</td><td>0.113</td><td>6.72</td><td>99.163</td></tr>
<tr><td><b>Observado</b></td><td>0</td><td>0</td><td>0</td><td>106</td></tr>
</tbody></table>
</div>
<div>
<br /></div>
Opa, nas duas primeiras categorias ##np\lt 5##, então eu não tenho amostras suficientes para usar o ##\chi^2##! Um jeito de consertar isso sem comprar mais pacotinhos é juntando as três primeiras categorias em uma só:<p>
</p><div class="post-table">
<table>
<tbody>
<tr></tr>
<tr><th></th><th>Menos de 4 diferentes</th><th>4 diferentes</th></tr>
<tr><td><b>Esperado</b></td><td>6.837</td><td>99.163</td></tr>
<tr><td><b>Observado</b></td><td>0</td><td>106</td></tr>
</tbody></table>
</div>
<div>
<br /></div>
Com isso conseguimos calcular o ##\chi^2## pela definição, chegando no valor de ##\chi^2=7.308##. Qual é o limite para rejeitar a hipótese? Se estivermos trabalhando com ##\alpha=5\%##, e um grau de liberdade, então o limite é:
<pre class="prettyprint">InverseCDF[ChiSquareDistribution[1], 1 - 0.05]
>> 3.84146
</pre>
Como ##7.308\gt 3.84146##, então temos que rejeitar a hipótese, e concluir que as figurinhas dos Vingadores não são independentes, e, como consequência, <b>não são honestas</b>, apesar de serem uniformes!<p></p></div><div><br /></div><div>Felizmente para o colecionador, elas são desonestas mas estão <b>jogando do seu lado</b>. Se os pacotinhos diminuem a chance de ter repetidas, então o total esperado de pacotinhos que você precisa comprar é menor que no caso puramente honesto.</div><div><br /></div><div>Dá para calcular o quanto você <b>economiza</b> com as figurinhas desonestas? Sure, vou mostrar dois métodos para fazer isso:</div><div><br /></div><h3 style="text-align: left;">Cadeias de Markov</h3><div><br /></div><div>Esse primeiro método é mais complicado, mas vou começar por ele porque é mais genérico, e se aplica a mais casos. Imagine que eu já tenho 50 figurinhas no meu álbum, e eu abro mais um pacotinho. Se eu der azar, vão sair quatro figurinhas que eu já tenho. Se eu der sorte, pode sair até quatro que eu ainda não tinha. </div><div><br /></div><div>O método mais natural para modelar esse problema é com uma <a href="https://en.wikipedia.org/wiki/Markov_chain">Cadeia de Markov</a>: você considera o estado como sendo quantas figurinhas você tem no álbum, e as transições possíveis são quantas figurinhas você ainda não tinha no pacotinho.</div><div><p>Para montar a matriz de transição, nós precisamos da probabilidade de ir de um estado ##n## até um estado ##m##. Como cada pacotinho tem 4 figurinhas diferentes, então o ##m## precisa ser necessariamente ##n##, ##n+1##, ..., ##n+4##, e outras transições não são possíveis. </p><p>Qual a chance de ir do estado ##n## para ##n+k##? Nós sabemos que as quatro figurinhas do pacotinho são diferentes entre si, então temos ##k## figurinhas novas e ##4-k## figurinhas que já tínhamos. A quantidade de pacotinhos que fazem essa transição é igual a ##{180-n\choose k}{n\choose 4-k}##, já que precisamos escolher ##k## figurinhas dentre que as não temos, e ##4-k## dentre as que temos. O total de pacotinhos é ##180\choose 4##, então a probabilidade total do estado ##n## para ##n+k## é ##{180-n\choose k}{n\choose 4-k}/{180\choose 4}## . Com isso podemos montar a <b>matriz de transição</b> ##M##:</p><p><br /></p><p>
$$ M= \begin{bmatrix} 0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & \dots & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0.02 & 0.97 & 0 & 0 & \dots & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & \approx0 & 0.04 & 0.95 & 0 & \dots & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & \approx0 & \approx0 & 0.06 & 0.93 & \dots & 0 & 0 & 0 \\ \vdots &\vdots & \vdots & \vdots & \vdots & \vdots & \vdots & \vdots & & \vdots & \vdots & \vdots \\ 0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \dots & 0.95 & 0.04 & \approx0 \\0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \dots & 0 & 0.97 & 0.02 \\0 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & \dots & 0 & 0 & 1 \\ \end{bmatrix}$$</p></div><p><br /></p><p>Pela matriz podemos conferir a intuição: se você não tem nenhuma figurinha, o primeiro pacotinho vai garantidamente te dar 4 figurinhas novas, então o estado 0->4 na matriz vale 1, e todos os outros nessa linha são zero. Por outro lado, se está faltando só uma figurinha para completar, comprar um pacotinho novo dá um chance de 2% de conseguir a faltante. E, claro, se você tem todas as figurinhas, então a chance de você não conseguir mais nenhuma é 100%.</p><p>O que queremos calcular é quanto economizamos devido às figurinhas desonestas. Para isso, vamos calcular qual o <b>valor esperado</b> do número de pacotinhos que precisamos comprar para completar o álbum. Tendo a matriz de transição em mãos, isso é equivalente ao número médio de transições para ir do estado 0 (álbum vazio) ao estado 180 (álbum completo). E como calcular isso? Usando um <b>algoritmo secreto</b>!</p><p>Really, um algoritmo secreto. Não sei o motivo, mas você não acha esse algoritmo em lugar nenhum; não tem na wikipedia e nem nos livros de processos estocásticos que eu tenho aqui em casa. Então aproveitem para anotar porque é bem útil:</p><p></p><ol style="text-align: left;"><li>Dada a matriz de transição ##M##, calcule a matriz reduzida ##R##, retirando da matriz original todas as linhas e colunas associadas aos estados finais. (No nosso caso só a última linha e coluna são finais).</li><li>Calcule a matriz fundamental ##N## do processo, através da fórmula ##N=(I-R)^{-1}##.</li><li>O valor esperado do número de transições é a soma dos elementos da primeira linha da matriz ##N##.</li></ol><div>Implementando o algoritmo acima com um <a href="https://gist.github.com/ricbit/79d4a639885da821302fccea7244cfbb">scriptinho rápido no Mathematica</a>, concluímos que o valor esperado do número de pacotinhos que você precisa comprar é ##257.98##. Como cada pacotinho custa R$ 1.20, então você precisa de R$309.58 em média para completar o álbum.</div><div><br /></div><div>E quanto você gastaria com figurinhas 100% honestas? Esse cálculo eu já fiz <a href="https://blog.ricbit.com/2016/09/a-matematica-do-pokemon-go.html">aqui no blog</a> anteriormente, se as figurinhas são uniformes e independentes então esse é o <a href="https://en.wikipedia.org/wiki/Coupon_collector%27s_problem">Coupon Collector Problem</a>: para ##n## figurinhas a solução é ##n H(n)##, onde ##H(n)## é o <a href="https://en.wikipedia.org/wiki/Harmonic_number">número harmônico</a> de ##n##. Essa fórmula dá o total de figurinhas, como elas são independentes, para o total de pacotinhos é só dividir por 4, chegando no total de ##259.78## pacotinhos, ou R$ 311.74, uma economia de 2 reais! (Eu falei que economizava, só não falei que era muito).</div><div><br /></div><div>Embora o algoritmo secreto seja bacana e funcione em vários casos, ele é meio lento. Você precisa inverter uma matriz 181x181, o que não é muito fácil. O fato dela ser triangular e esparsa ajuda, mas tem um método melhor, que calcula o valor em O(n)!</div><div><br /></div><h3 style="text-align: left;">Linearidade da Expectativa</h3><div><br /></div><div>Qual é o teorema mais surpreendente que você conhece? Só em probabilidade tem vários que são difíceis de acreditar à primeira vista, como o <a href="https://en.wikipedia.org/wiki/Monty_Hall_problem">Monty Hall Problem</a> e o <a href="https://en.wikipedia.org/wiki/Birthday_problem">Paradoxo do Aniversário</a>. Mas um que sempre me espanta é a <b>linearidade da expectativa </b>(o valor esperado da soma é a soma dos valores esperados):</div><div><br /></div><div>$$E[a+b]=E[a]+E[b]$$</div><div><br /></div><div>Para mim a parte curiosa é que ele funciona mesmo se as variáveis ##a## e ##b## <b>não forem independentes</b>! Para o nosso problema isso ajuda bastante: o valor esperado para conseguir 180 figurinhas claramente é correlacionado com o valor esperado para conseguir 179 figurinhas, mas eu posso usar relações lineares entre eles sem medo.</div><div><br /></div><div>Para essa análise eu acho mais fácil pensar invertido. Se estiver faltando 0 figurinhas para completar o album, quantos pacotinhos eu preciso? Nenhum né, ele já está completo. E se estiver faltando só uma figurinha? Aí depende do que sair no pacotinho, se sair a figurinha faltante, um pacotinho basta, se não sair, então você adiciona um pacotinho no seu valor esperado e repete o procedimento:</div><div><br /></div><div>$$\begin{align*}\\E[1]&=p_0(1+E[0]) + p_1(1+E[1])\\ (1-p_1)E[1] &= p_1 + p_0(1+E[0]) \\ E[1] &=\frac{1}{1-p_1}\left(p_1+p_0(1+E[0])\right)\end{align*}$$</div><div><br /></div><div>Olha só, eu sei que ##E[0]=0##; para calcular ##E[1]## eu preciso só do ##E[0]##; para o ##E[2]## eu preciso do ##E[1]## e ##E[0]##; e assim por diante. Como cada pacotinho tem no máximo 4 figurinhas que eu não tenho, então eu só preciso olhar quatro valores para trás na recorrência. Portanto, esse algoritmo é linear!</div><div><br /></div><div>Preenchendo as probabilidades corretas, o algoritmo completo fica assim:</div><div><br /></div><div>$$\begin{align*} \\ E[0] &= 0 \\ E[n] &= \frac{1}{1-{{180-n}\choose 4}/{180\choose 4}}\left(\frac{{180-n}\choose 4}{180\choose 4}+\sum_{1\le k \le 4}\frac{{n\choose k}{{180-n}\choose{4-k}}}{180\choose 4}E[n-k]\right) \\ \end{align*} $$</div><div><br /></div><div>Essa implementação realmente é bem mais rápida que o anterior. A versão com matrizes no Mathematica leva 1s para rodar, enquanto que um <a href="https://gist.github.com/ricbit/5e30001b73b0c385c1a44d08e7ef4306">scriptinho python</a> com o algoritmo linear leva só 55ms!</div><div><br /></div><div>Agora que temos os pacotinhos corretamente modelados, finalmente vamos conseguir bolar uma estratégia para minimizar o custo de completar o álbum. Mas vai ficar para o próximo post porque esse já ficou comprido demais :)</div><p></p><p><br /></p><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-6716517199537270942018-07-16T13:15:00.000-03:002018-07-16T16:11:00.750-03:00As Figurinhas dos Vingadores são Honestas? (parte 1)<div dir="ltr" style="text-align: left;" trbidi="on">
O Brasil é um país muito estranho. Uma das inúmeras bizarrices brasileiras é que temos <b>tabus temporários</b>. Por exemplo, homem se vestir de mulher é tabu (vai acabar com a família, fim dos tempos, etc), mas no carnaval pode! Da mesma maneira, adulto comprando álbum de figurinhas é tabu (precisa crescer, largar os brinquedos, etc.), mas em época de Copa pode!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo1lIC-Rhv2MSPjq-5VgQg51R_jLabHg4Or1x2v9meGsXhQ-YAR3opcnhDnmUw41JTRruon94zphrclSe9pFEmkjD1-GtxDVU-srDCzF_8h1pNHMGZ0ehGdlTpfZsguOg5uzdbVsYgQ-te/s1600/figurinhas.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="600" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo1lIC-Rhv2MSPjq-5VgQg51R_jLabHg4Or1x2v9meGsXhQ-YAR3opcnhDnmUw41JTRruon94zphrclSe9pFEmkjD1-GtxDVU-srDCzF_8h1pNHMGZ0ehGdlTpfZsguOg5uzdbVsYgQ-te/s320/figurinhas.jpg" width="320" /></a></div>
<br />
Sempre que chega época de Copa eu fico espantado com a dedicação que meus amigos tem para completar o álbum com os jogadores. E pensando um pouco no fenômeno, me veio a dúvida: quanto dinheiro você precisa para terminar um álbum desses?<br />
<br />
Resolvi fazer um experimento para tentar calcular esse valor. Mas, ao invés de usar o álbum da Copa, escolhi completar o álbum dos Vingadores, já que é um universo que eu tenho mais afinidade. Os valores originais são os seguintes:<br />
<br />
<ul style="text-align: left;">
<li>O <b>álbum</b> em si custa R$ 6.90, e você precisa de 180 figurinhas para completar. </li>
<li>Cada <b>pacotinho</b> com 4 figurinhas custa R$ 1.20.</li>
<li>Existe um <b>kit</b> com <a href="https://photos.app.goo.gl/Wu7p4zEJdmMShLS48">15 pacotinhos</a> que você pode comprar com desconto por R$ 13.90. Mas é difícil de achar: eu só encontrei na loja <a href="https://www.livrariacultura.com.br/c/geek">Geek</a> na av. Paulista.</li>
<li>Por fim, você sempre pode pedir <b>figurinhas avulsas</b> no <a href="https://figurinhas.abril.com.br/">site da editora</a> (nesse caso você pode comprar até 40 figurinhas com preço individual de R$ 0.30, mais o frete de R$ 10.00).</li>
</ul>
<br />
Se você for uma pessoa sem imaginação, de cara já tem uma estratégia fácil para completar o álbum: é só pedir tudo pelo correio. Nesse caso, basta comprar o álbum e mais 180 figurinhas em 5 fretes, num total de R$110.90.
<br />
<br />
Tem como gastar menos que isso? Para descobrir, temos que analisar as figurinhas!
<br />
<br />
<h3 style="text-align: left;">
A Distribuição das Figurinhas</h3>
<br />
Para descobrir a distribuição das figurinhas, eu comprei ##t=106## pacotinhos (ou seja, ##n=106\times 4=424## figurinhas), e anotei o conteúdo de cada um deles (os dados originais estão no <a href="https://docs.google.com/spreadsheets/d/1PMrtdJ3_mQF1dwjWQTvIVhtz1K3Vi7xxJITIRTtlgqg/edit?usp=sharing">google docs</a>). A primeira coisa que fiz foi traçar o histograma para ver o jeitão dos dados:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRqr9MPV9WytKv7slAUx6M2xEpv3EJZhDdlzHGG8Ik7NxXBaESJv3DeJmX_Y-93jdIySkf1_POBZSag6GXNf09ZX1z0rrZCjR0JTisg6Ssn-dnDgn6jWB4NZqeLDxOxapUn186LacFVhVB/s1600/histogram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="348" data-original-width="545" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRqr9MPV9WytKv7slAUx6M2xEpv3EJZhDdlzHGG8Ik7NxXBaESJv3DeJmX_Y-93jdIySkf1_POBZSag6GXNf09ZX1z0rrZCjR0JTisg6Ssn-dnDgn6jWB4NZqeLDxOxapUn186LacFVhVB/s320/histogram.png" width="320" /></a></div>
Pelo histograma dá para ver que faltam nove figurinhas para completar meu álbum (são os nove buracos no histograma). Por outro lado tem três figurinhas que repetiram 6 vezes, o que indica que possivelmente esse álbum não é honesto, já que algumas figurinhas são mais fáceis que outras.<br />
<br />
Ou... talvez não? <b>A intuição humana é notoriamente falha em intuir probabilidades</b>, de repente essa impressão de que algumas figurinhas são mais fáceis é falsa. O jeito correto de decidir isso é fazendo um <a href="https://en.wikipedia.org/wiki/Statistical_hypothesis_testing">teste de hipótese</a>. O teste em si é simples, mas eu vou fazer em câmera lenta caso alguém nunca tenha visto.
<br />
<br />
Para começar, precisamos de uma <a href="https://en.wikipedia.org/wiki/Null_hypothesis">hipótese nula</a> ##H_0## e um <a href="https://en.wikipedia.org/wiki/Statistical_significance">nível de significância</a> ##\alpha##.
<br />
<br />
Nossa hipótese nula ##H_0## é que as figurinhas tem <b>distribuição uniforme</b> e são <b>independentes</b>. O teste vai nos falar se esse hipótese precisa ser rejeitada. O nível de significância nós podemos escolher, mas escolher o ##\alpha## é uma arte: você precisa levar em conta o número de amostras, e as taxas aceitáveis de falsos positivos e falsos negativos. Usualmente, ##\alpha##=5% funciona bem.
<br />
<br />
Qual o significado desses parâmetros? Fica simples de entender com um exemplo: suponha que todas as 424 figurinhas que eu comprei vieram repetidas. Em uma distribuição é uniforme, isso não é impossível, mas é extremamente improvável: a chance é de 1 em ##180^{424}\sim 1.7\times 10^{956}##. Esse valor é maior que o número de átomos no universo (muito maior!), então a chance de ter acontecido ao acaso é quase nula.
<br />
<br />
Se eu tirasse 424 figurinhas repetidas, eu teria bastante convicção de que as figurinhas não são uniformes. Quando fazemos um teste de hipótese, é essa ideia que estamos tentando capturar: os dados que coletamos são tão raros que não poderiam ter acontecido na prática? A chance de terem acontecido ao acaso é menor que ##\alpha##=5%? Se for, então talvez a hipótese ##H_0## seja falsa.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnv0dJJOR_KoNi6njpoqnAjKJ58AuJzWL2gzf6WZBM0WZO86nqNTunRvfHs2HQTyncjDi_0NBdnRv9kqqR4ZG5GqmziWVI3W0SzVdhvXGgcFxubGpEEgW0KKvzZTe5MmDbMQFwr7Ge0Bnc/s1600/figurinha_repetida.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="469" data-original-width="600" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnv0dJJOR_KoNi6njpoqnAjKJ58AuJzWL2gzf6WZBM0WZO86nqNTunRvfHs2HQTyncjDi_0NBdnRv9kqqR4ZG5GqmziWVI3W0SzVdhvXGgcFxubGpEEgW0KKvzZTe5MmDbMQFwr7Ge0Bnc/s320/figurinha_repetida.jpg" width="320" /></a></div>
<br />
Existem vários testes de hipótese possíveis. Como queremos checar se nossas amostras seguem uma distribuição dada (uniforme), então o teste mais natural a se fazer é o <a href="https://en.wikipedia.org/wiki/Pearson%27s_chi-squared_test">teste do ##\chi^2##</a> (pronuncia-se quiquadrado), que serve justamente para verificar se duas distribuições são iguais.
<br />
<br />
Você começa o teste do ##\chi^2## separando suas amostras em categorias. No nosso caso, o mais natural seria fazer cada figurinha ser uma categoria, mas isso não vai funcionar. O motivo é que o teste do ##\chi^2##, estritamente falando, só funciona com infinitas amostras. Como é impossível coletar infinitas amostras, nós assumimos que o resultado não muda muito de "infinitas amostras" para "muitas amostras".
<br />
<br />
Mas quantas amostras são "muitas amostras"? Em geral, a regra usada é que ##np>5##, onde ##n## é o número de amostras e ##p## é a menor probabilidade de uma amostra estar em uma categoria. Vamos fazer as contas: se cada figurinha é uma amostra, e as 180 figurinhas são uniformes, então ##p=1/180##. Como tenho ##n=424## figurinhas, então ##np=424/180\sim 2.35##. Oops, 2.35 é menor que 5, então 180 categorias é muito.<br />
<div>
<br /></div>
<div>
Vamos reduzir então. Eu vou escolher ##c=5## categorias, porque 180 divide certinho por 5. Cada categoria vai ter ##180/5=36## figurinhas, logo ##p=1/36## e ##np=424/36\sim 11.77##, agora sim! O histograma com cinco categorias é assim:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFnq0XTYgp7wg9v8wyfN2Cq699Ek1I_z66MffVTwCj4bzPyeiysV2G19hRE1w8Vx2OGN8n5HmE7_UV5gonFXWejCSzKPQ9o1Tym2DqDzlnGvoirG5a09__mnrasSxsu0f50X-u2QbSWo13/s1600/uniform.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="360" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFnq0XTYgp7wg9v8wyfN2Cq699Ek1I_z66MffVTwCj4bzPyeiysV2G19hRE1w8Vx2OGN8n5HmE7_UV5gonFXWejCSzKPQ9o1Tym2DqDzlnGvoirG5a09__mnrasSxsu0f50X-u2QbSWo13/s320/uniform.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Agora nós vamos tabelar nossas amostras. A linha <i>Observado</i> mostra quantas amostras nós coletamos em cada categoria, a linha <i>Esperado</i> mostra quantas amostras uma distribuição ideal teria (no caso uniforme, todas são iguais a ##n/c=424/5=84.8##):<br />
<br /></div>
<div class="post-table">
<table>
<tbody>
<tr></tr>
<tr><th></th><th>Categoria 1</th><th>Categoria 2</th><th>Categoria 3</th><th>Categoria 4</th><th>Categoria 5</th></tr>
<tr><td><b>Observado</b></td><td>75</td><td>90</td><td>80</td><td>95</td><td>84</td></tr>
<tr><td><b>Esperado</b></td><td>84.8</td><td>84.8</td><td>84.8</td><td>84.8</td><td>84.8</td></tr>
</tbody></table>
</div>
<div>
<br /></div>
<div>
Podemos calcular o ##\chi^2## agora. A intuição do teste é simples, vamos calcular qual o quadrado da diferença entre o observado e o esperado, e normalizar. Se esse número for muito grande, então tem algo errado. Calculando pela fórmula, temos:<br />
<br />
$$\chi^2=\sum\frac{(Y_i-E_i)^2}{E_i}\sim 2.957$$<br />
Esse valor, 2.957 é grande ou é pequeno? Para decidir isso precisamos primeiro calcular quantos <a href="https://en.wikipedia.org/wiki/Degrees_of_freedom_(statistics)">graus de liberdade</a> tem esse modelo. O número de graus de liberdade é o número de parâmetros que estamos estimando. Como nossa tabela tem 5 categorias, então o número de graus de liberdade é 4.<br />
<br />
Opa! Se são 5 categorias, não tinha que ser 5 graus de liberdade? É verdade que estamos tentando estimar ##Y_1##, ##Y_2##, ##Y_3##, ##Y_4## e ##Y_5##. Mas nós sabemos que a soma de todos eles é ##n##, então se tivermos os quatro primeiros, o quinto é só calcular!<br />
<br />
Em tempos idos esse era o momento em que você precisava sacar do bolso uma tabela de ##\chi^2## (como curiosidade, coloquei online a <a href="https://drive.google.com/file/d/1Ex81nVxpF44jh9yEbeMjWXRBD8mWKDqi/view?usp=sharing">tabela que eu usava na Poli</a>). Mas hoje em dia você pode ir direto no Mathematica para descobrir o valor de ##\chi^2## com ##\alpha##=5% e quatro graus de liberdade:<br />
<pre class="prettyprint">InverseCDF[ChiSquareDistribution[4], 1 - 0.05]
>> 9.48773
</pre>
<br />
Como 2.957 é menor que 9.488, então não podemos rejeitar a hipótese de que as figurinhas são uniformes e independentes, e concluímos que <b>sim, as figurinhas provavelmente são honestas</b>.
<br />
<br />
Alguns de vocês irão chiar, eu sei. "Mas veja, algumas figurinhas saíram repetidas seis vezes, outras não apareceram nenhuma vez! Não tem como isso ser uniforme, claramente tem figurinhas que são mais fáceis!".<br />
<br />
Bem, se você ainda estiver na dúvida, você pode comprar mais pacotinhos para deixar o teste mais robusto. Ao invés disso, eu vou usar só as figurinhas que eu tenho para ir direto na raiz da dúvida: afinal de contas, é normal tirar muitas figurinhas repetidas?<br />
<br />
<h3 style="text-align: left;">
A Análise das Repetidas</h3>
<br />
A intuição nos diz que, em uma distribuição uniforme, todas as figurinhas tem que aparecer mais ou menos com a mesma frequência. Só que esse é mais um caso onde <b>a intuição é falha</b>! Quem lê o blog há mais tempo deve lembrar de um <a href="http://blog.ricbit.com/2010/11/sorteios-e-aniversarios.html">post sobre o paradoxo do aniversário</a>, que diz exatamente isso: mesmo em distribuições uniformes, a chance de colisões é muito alta.<br />
<br />
Podemos usar esse fato para tentar um novo teste de hipótese. Dessa vez, vamos contar quantas repetidas nós temos para cada figurinha, e separá-las por número de repetições. O histograma da quantidade de figurinhas repetidas fica assim:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_UWBuKB6Vg2gtUEYUZM0ArrouUyp0uFCQJUVc1BovWHjKVMmmc5WYqzgW9AUv_cM-joFH6LgCaSU3xGGQSrAKdtg9VKY-if3-fHKwI028nw0jG5eHjJ0Uv9_2o7CXazHjHsEc8M6Iy8Te/s1600/repetidas.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="360" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_UWBuKB6Vg2gtUEYUZM0ArrouUyp0uFCQJUVc1BovWHjKVMmmc5WYqzgW9AUv_cM-joFH6LgCaSU3xGGQSrAKdtg9VKY-if3-fHKwI028nw0jG5eHjJ0Uv9_2o7CXazHjHsEc8M6Iy8Te/s320/repetidas.png" width="320" /></a></div>
<br />
O histograma foi traçado da quantidade de figurinhas repetidas:<br />
<br />
<div class="post-table">
<table>
<tbody>
<tr></tr>
<tr><th><b>Quantidade de repetições</b></th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6</th></tr>
<tr><td><b>Figurinhas</b></td><td>9</td><td>41</td><td>50</td><td>48</td><td>24</td><td>5</td><td>3</td></tr>
</tbody></table>
</div>
<div>
<br /></div>
O álbum tem 180 figurinhas, então a soma da segunda linha vale 180. Do total de figurinhas, nove delas não apareceram, três apareceram 6 vezes, e a maioria apareceu entre 1 e 3 vezes.<br />
<br />
O formato do histograma parece familiar né? Se nós soubéssemos qual a é a fórmula exata para o número de figurinhas repetidas em uma distribuição uniforme, poderíamos usar o teste do ##\chi^2## para ver se a hipótese ainda se mantém. Só que agora não tem jeito, para descobrir a fórmula precisamos fazer as contas. Pule a caixa azul se você tem medo de combinatória:<br />
<br />
<div class="caixa-azul">
O truque para usar <a href="https://en.wikipedia.org/wiki/Symbolic_method_(combinatorics)">combinatória analítica</a> numerada (<i>labelled</i>) é sempre tentar achar um isomorfismo entre o seu problema e uma lista de inteiros. Para o nosso caso, imagine o seguinte experimento: temos ##n## bolinhas numeradas de ##1## a ##n##, e temos também ##m## caixinhas, cada caixa representando uma figurinha. A bolinha representa a ordem em que eu comprei a figurinha. Se eu atirar as bolinhas em caixas aleatórias, tenho um problema isomórfico a comprar figurinhas aleatórias.<br />
<br />
Por exemplo: se a bolinha 5 cair na caixa 32, então significa que a quinta figurinha que eu comprei foi a de número 32. Se no fim do experimento a caixa 32 tiver seis bolinhas, então eu tenho a figurinha 32 repetida seis vezes.<br />
<br />
No nosso experimento, as caixinhas são diferentes umas das outras, então vamos usar o operador ##\text{SEQ}_m(z)## para modelar as caixinhas. A ordem das bolinhas dentro da caixinha não importa (tanto faz se dissermos que a caixa 32 tem as bolinhas 3, 5, 9, 10 e 15; ou se tiver as bolinhas 9, 5, 3, 15, e 10, é a mesma coisa). Então dentro das caixinhas, usaremos o operador ##\text{SET}(z)##. O experimento completo é ##\text{SEQ}_m(\text{SET}(z))##.<br />
<br />
Digamos que estamos interessados em saber quantas caixinhas vão terminar com ##s## bolinhas. Um jeito de medir isso é colocar um marcador nas caixinhas com ##s## elementos. Para isso, nós tiramos as das caixinhas os conjuntos de ##s## elementos, e colocamos eles de volta com o marcador, digamos que o marcador seja ##u##. Então o experimento para medir ##s## repetidas é ##\text{SEQ}_m(\text{SET}(z)-\text{SET}_s(z)+u \text{SET}_s(z))##.<br />
<br />
Os experimentos vão mapear para <a href="https://en.wikipedia.org/wiki/Generating_function#Exponential_generating_function_(EGF)">funções geradoras exponenciais</a> de duas variáveis. Das tabelas de combinatória analítica:<br />
$$\begin{align*}<br />
\text{SEQ}_m(z) &= z^m \\<br />
\text{SET}(z) &= e^z \\<br />
\text{SET}_s(z) &= \frac{z^s}{s!}\\<br />
\text{SEQ}_m(\text{SET}(z)-\text{SET}_s(z)+u \text{SET}_s(z)) &=<br />
\left(e^z-\frac{z^s}{s!}+u \frac{z^s}{s!}\right)^m \\<br />
&= \left(e^z+ (u-1) \frac{z^s}{s!}\right)^m<br />
\end{align*}$$
<br />
Se abrirmos essa função geradora em série, então o coeficiente do termo ##z^n u^k## indica quantas combinações possíveis de ##n## bolinhas vão ter ##k## caixinhas com ##s## bolinhas repetidas (como a função geradora é exponencial, você precisa multiplicar o coeficiente por ##n!## para ter o valor real). Se chamarmos esse coeficiente de ##R_s(n, k)##, então temos:<br />
$$\sum_{n}\sum_{k}R_s(n,k) z^n u^k = \left(e^z+ (u-1) \frac{z^s}{s!}\right)^m $$<br />
Vamos listar com cuidado o que temos. ##n! R_s(n,k)## é número de combinações possíveis com ##k## caixinhas contendo exatamente ##s## bolinhas. Logo, de todas as combinações possíveis de ##n## bolinhas, existem ##\sum k n! R_s(n,k)## caixinhas com ##s## bolinhas.<br />
<br />
Por outro lado, o número total de combinações de ##n## bolinhas em ##m## caixinhas é ##m^n##. Como cada combinação dessas tem ##m## caixinhas, então o número total de caixinhas que podem ser preenchidas em todas as combinações é ##m^{n+1}##.<br />
<br />
Juntando as duas contagens, a probabilidade de um caixinha tem ##s## bolinhas em ##n## arremessos é:<br />
$$p_s(n)=\sum_{k>0}\frac{k \;n!}{m^{n+1}}R_s(n, k)$$<br />
Mas olha só! Nós podemos conseguir essa somatória derivando a função geradora em relação a ##u##, e depois setando ##u=1##.<br />
$$\left.\frac{\partial}{\partial u}\sum_{n}\sum_{k}R_s(n,k) z^n u^k \right|_{u=1}=\sum_n \sum_{k}k R_s(n,k) z^n$$<br />
Agora é só fazer as contas:<br />
$$\begin{align*}p_s(n)<br />
&=\sum_k \frac{n!}{m^{n+1}}k R_s(n, k) \\<br />
&=\frac{n!}{m^{n+1}} [z^n] \left.\frac{\partial}{\partial u}\sum_{n}\sum_{k}R_s(n,k) z^n u^k \right|_{u=1}\\<br />
&=\frac{n!}{m^{n+1}} [z^n] \left.\frac{\partial}{\partial u}\left(e^z+ (u-1) \frac{z^s}{s!}\right)^m \right|_{u=1}\\<br />
&=\frac{n!}{m^{n+1}} [z^n] \left. m\left(e^z+ (u-1) \frac{z^s}{s!}\right)^{m-1} \frac{z^s}{s!} \right|_{u=1}\\<br />
&=\frac{n!}{m^{n+1}} [z^n] m\left(e^z\right)^{m-1} \frac{z^s}{s!} \\<br />
&=\frac{n!}{s!\;m^n} [z^n]z^s e^{z(m-1)}\\<br />
\end{align*}$$
<br />
<div>
Abrindo a exponencial em série:</div>
$$\begin{align*}p_s(n)<br />
&=\frac{n!}{s!\;m^{n}} [z^n]z^s\sum_{k\ge 0}\frac{(z(m-1))^k}{k!} \\
&=\frac{n!}{s!\;m^{n}} [z^n]\sum_{k\ge 0}\frac{z^{k+s}(m-1)^k}{k!} \\
&=\frac{n!}{s!\;m^{n}} [z^n]\sum_{p\ge s}\frac{z^p(m-1)^{p-s}}{(p-s)!} \\
&=\frac{n!}{s!\;m^{n}}\; \frac{(m-1)^{n-s}}{(n-s)!} \\
&=\frac{n!}{s!(n-s)!}\; \frac{(m-1)^{n-s}}{m^{n}} \\
&={{n}\choose{s}}\frac{(m-1)^{n-s}}{m^{n}} \\ \end{align*}$$
<br />
Essa é a solução exata, em forma fechada, que queríamos! Mas tem uma massagem que podemos fazer que vai ajudar a entender por que essa fórmula é assim. Em geral ##n## é bem maior que ##m##, então vamos reescrever ##m## de modo que ##n=\lambda m##, e calcular o limite quando ##n## é muito grande:
$$\begin{align*}\lim_{n\to\infty}p_s(n)
&=\lim_{n\to\infty}\frac{n!}{s!(n-s)!}\; \frac{(n/\lambda-1)^{n-s}}{(n/\lambda)^{n}} \\
&=\lim_{n\to\infty}\frac{n!}{s!(n-s)!}\; \left(\frac{n-\lambda}{\lambda}\right)^{n-s} \left(\frac{\lambda}{n}\right)^{n}\\
&=\lim_{n\to\infty}\frac{n!}{s!(n-s)!}\; \lambda^{s} \left(\frac{n-\lambda}{n}\right)^{n-s}\left(\frac{1}{n}\right)^{s} \\
&=\lim_{n\to\infty}\frac{n!\;\lambda^{s}}{s!(n-s)!\;n^{s}}\; \left(1-\frac{\lambda}{n}\right)^{n-s}\\
&=\lim_{n\to\infty}\left(\frac{\lambda^{s}}{s!}\right)
\left(\frac{n!}{(n-s)!\;n^s}\right)
\left(1-\frac{\lambda}{n}\right)^{n}
\left(1-\frac{\lambda}{n}\right)^{-s} \\
\end{align*}$$
O limite do produto é o produto dos limites, desde que todos existam e sejam finitos, então vamos conferir um por um. O primeiro termo é constante, easy. No segundo, o numerador é um polinômio de grau ##n##, o denominador é um polinômio de grau ##n##, então a fração é constante e vale 1, já que os coeficientes iniciais ambos valem 1. O terceiro é a definição de ##e^{-\lambda}##. No quarto a segunda fração vai para zero, então o total tende a 1. Combinando tudo:
$$\lim_{n\to\infty}p_s(n)= e^{-\lambda}\frac{\lambda^s}{s!}$$
Ahá, é uma distribuição de Poisson!
</div>
<br />
Não foi à toa que aquele histograma das figurinhas repetidas parecia familiar, elas seguem uma <a href="https://en.wikipedia.org/wiki/Poisson_distribution">distribuição de Poisson</a>! Em retrospecto até faz sentido. Se a distribuição das figurinhas é uniforme, então em um longo período de tempo, cada figurinha deve ter uma taxa de aparição constante (igual a ##\lambda=n/m##). Se a taxa é constante ao longo do tempo, então a quantidade de figurinhas em um pequeno intervalo tem que seguir a distribuição de Poisson.<br />
<br />
Agora falta pouco para conseguir usar o teste do ##\chi^2## nas repetidas. Precisamos primeiro separar as amostras em categorias. O natural seria usar uma categoria por quantidade de repetidas, mas em teoria, um cara muito azarado poderia ter 424 figurinhas repetidas, e nesse caso o produto ##np## seria muito, muito menor que 5. Vamos então aglutinar todas as categorias de 6 repetidas em diante em um grupo só. A tabela fica assim:<br />
<br />
<div class="post-table">
<table>
<tbody>
<tr></tr>
<tr><th><b>Quantidade de repetições</b></th><th>0</th><th>1</th><th>2</th><th>3</th><th>4</th><th>5</th><th>6 ou mais</th></tr>
<tr><td><b>Figurinhas, medido</b></td><td>9</td><td>41</td><td>50</td><td>48</td><td>24</td><td>5</td><td>3</td></tr>
<tr><td><b>Figurinhas, teórico</b></td><td>17.1</td><td>40.2</td><td>47.4</td><td>37.2</td><td>21.9</td><td>10.3</td><td>5.9</td></tr>
</tbody></table>
</div>
<br />
Agora podemos calcular o ##\chi^2## medido e o teórico, lembrando que temos 6 graus de liberdade e ##\alpha## valendo 5%:
$$\chi^2=\sum\frac{(Y_i-E_i)^2}{E_i}\sim 11.5275$$
<br />
<pre class="prettyprint">InverseCDF[ChiSquareDistribution[6], 1 - 0.05]
>> 12.59
</pre>
Conclusão: como 11.53 é menor que 12.59, então novamente não podemos rejeitar a hipótese de que as figurinhas são uniformes e independentes, e concluímos que <b>sim, as figurinhas provavelmente são honestas</b>. É verdade que tem muitas repetidas, mas o número de repetidas está dentro do esperado.
<br />
<br />
<h3 style="text-align: left;">Quanto gastar para completar?</h3>
<br />
No pŕoximo post, vamos finalmente calcular qual o minimo de dinheiro que você precisa gastar para completar o álbum. Stay tuned!
<br /></div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-67550983289808920222018-03-19T11:35:00.000-03:002018-03-19T11:59:18.936-03:00A Marcha dos Lemmings<div dir="ltr" style="text-align: left;" trbidi="on">
Lemmings são pequenos roedores que vivem na tundra ártica. Eles possuem pelos longos e macios, rabo curto, são herbívoros e explodem.<br />
<br />
Sim, eles explodem! Os lemmings possuem a capacidade de ligar um contador luminoso sobre suas cabeças. A cada passo que dão, o contador decrementa, até que finalmente explodem quando o contador chega a zero.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDRjZfk18mALgxFIUi_mzRh-6_EL42K_b36wkMIwSfUstko2K8TrZwNpCzRp2JRnuTJ91I6Z5A5wRWGC93TmtpczfdRTeWB7wKQmCpG8UYOynk22tWXyiCnJM5Z2erlvh0IG8c6ysL3UaL/s1600/lemmings.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="516" data-original-width="500" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDRjZfk18mALgxFIUi_mzRh-6_EL42K_b36wkMIwSfUstko2K8TrZwNpCzRp2JRnuTJ91I6Z5A5wRWGC93TmtpczfdRTeWB7wKQmCpG8UYOynk22tWXyiCnJM5Z2erlvh0IG8c6ysL3UaL/s200/lemmings.gif" width="193" /></a></div>
<br />
O que você faria se soubesse que vai explodir em seis passos? Parece que não tem muitos passeios possíveis com exatamente seis passos, mas a intuição é meio falha aqui. Na verdade, o número de passeios possíveis é exponencial no número de passos! Como fazer um algoritmo que calcula o número de caminhos possíveis que o lemming pode fazer antes de explodir?<br />
<br />
<h3 style="text-align: left;">
As Regras do Jogo</h3>
<div>
<br /></div>
Para modelar o problema, vamos supor que os passos que lemming pode fazer são descritos por um grafo orientado. Por exemplo, no grafo abaixo o lemming começa no nó A. Do A ele pode ir para B ou C, de B ele pode ir para C, e de C ele pode ir para A.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTpUyBOJJoJXUtqZ70SkhOOOnVkWYzZHHhyWmWuUgjuLPCwGIZkZUADQcnQSNqxU_ijxh5WBdTDx8T-pXVrTsiTQJJz7cjaXa3DTChdfLSv6WXDASw7AS_P4hKkx9UWodVF1smCjS6z348/s1600/graph.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="142" data-original-width="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTpUyBOJJoJXUtqZ70SkhOOOnVkWYzZHHhyWmWuUgjuLPCwGIZkZUADQcnQSNqxU_ijxh5WBdTDx8T-pXVrTsiTQJJz7cjaXa3DTChdfLSv6WXDASw7AS_P4hKkx9UWodVF1smCjS6z348/s1600/graph.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Se o nosso lemming pode dar seis passos, então existem sete caminhos diferentes que ele pode fazer:<br />
<br />
<div style="text-align: center;">
ABCABCA</div>
<div style="text-align: center;">
ABCACAB</div>
<div style="text-align: center;">
ABCACAC</div>
<div style="text-align: center;">
ACABCAB</div>
<div style="text-align: center;">
ACABCAC</div>
<div style="text-align: center;">
ACACABC</div>
<div style="text-align: center;">
ACACACA</div>
<br />
Logo ##p(6)=7##, e podemos até tabelar ##p(n)## para todos os ##n## de 0 a 6:<br />
<br />
$$p(0..6)=1,2,2,3,4,5,7$$<br />
Dado ##n##, como fazer um algoritmo que calcule ##p(n)##?<br />
<br />
Eu vou mostrar três algoritmos possíveis usando a linguagem do <a href="https://en.wikipedia.org/wiki/Wolfram_Mathematica_(software)">Wolfram Mathematica</a>. Para isso, o grafo precisa ser descrito de alguma maneira que os algoritmos entendam. Eu vou usar uma <a href="https://en.wikipedia.org/wiki/Adjacency_matrix">matriz de adjacências</a>: para um grafo com ##k## nós, a matriz é ##k\times k## e o elemento na linha ##i## da coluna ##j## é 1 se existe uma ligação saindo do nó ##j## e indo em direção ao nó ##i##, e 0 em caso contrário. Para o nosso exemplo, a matriz é:<br />
<br />
$$M=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right]$$<br />
<h3 style="text-align: left;">
Força Bruta</h3>
<div>
<br /></div>
Sempre que eu tenho que fazer um algoritmo do zero, prefiro começar com a <a href="https://en.wikipedia.org/wiki/Brute-force_search">força bruta</a>. É fácil e rápido de implementar, e embora não seja eficiente, serve para conferir o resultado dos algoritmos mais avançados.<br />
<br />
Nesse caso, para implementar a força bruta você passa o grafo, o nó em que o lemming está, quantos passos faltam para terminar, e o resto é uma recursão simples. Como toda recursão, não esqueça de fazer o caso base! Para esse problema, o caso base é ##p(0)=1##, ou seja, tem um único caminho possível sem dar passo nenhum, que é não sair do lugar.<br />
<br />
No fim, a solução em força bruta fica assim:<br />
<pre class="prettyprint">count[graph_, pos_, 0] := 1
count[graph_, pos_, size_] := Sum[
If[graph[[i, pos]] == 1, count[graph, i, size - 1], 0],
{i, 1, Length[graph]}]</pre>
Vamos conferir o resultado:<br />
<pre class="prettyprint">graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]
{1,2,2,3,4,5,7}</pre>
Perfeito! Mas qual é a complexidade desse algoritmo? Bem, ele precisa visitar cada um dos caminhos possíveis, e o número total de caminhos é exponencial, logo a complexidade vai ser do tipo ##O(c^n)##, onde a constante ##c## depende do grafo. Ou seja, você não quer usar esse algoritmo quando o ##n## é grande.<br />
<br />
<h3 style="text-align: left;">
Multiplicação de Matrizes</h3>
<div>
<br /></div>
Como melhorar então? Tem uma maneira calcular ##p(n)## mais rápido usando multiplicação de matrizes. É mais fácil mostrar como funciona por exemplos: no estado inicial, nós temos um lemming no nó A, então vamos montar um vetor correspondente: ##V=[1 \;0\; 0]^T##. Se multiplicarmos ##M## por ##V##, qual o resultado?<br />
<br />
$$M V=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right] \left[\begin{matrix}1\\0\\0\end{matrix}\right]=\left[\begin{matrix}0\\1\\1\end{matrix}\right]$$<br />
<div>
Ou seja, um lemming que está em A vai parar em B ou C. Continuando:</div>
$$M V=\left[\begin{matrix}0 && 0 && 1 \\ 1 && 0 && 0 \\ 1 && 1 && 0\end{matrix}\right] \left[\begin{matrix}0\\1\\1\end{matrix}\right]=\left[\begin{matrix}1\\0\\1\end{matrix}\right]$$<br />
Dado um lemming em B ou C, o resultado é um lemming em A ou C. Se o vetor de entrada mostra quantos lemmings tem em cada nó, multiplicar pela matriz M vai mostrar onde eles podem estar depois de dar um passo.<br />
<br />
Agora é só estender o raciocínio. Se multiplicar por M correponde a um passo, então multiplicar ##n## vezes por M é o equivalente a ##n## passos. E se você começou o processo com um vetor unitário, então a soma do elementos de V é o número de caminhos possíveis partindo do nó inicial!<br />
<br />
Uma implementação possível é a abaixo:<br />
<pre class="prettyprint">count[graph_, pos_, size_] :=
Total[MatrixPower[graph, size] . UnitVector[Length[graph], pos]]</pre>
Conferindo:
<br />
<pre class="prettyprint">graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]
{1,2,2,3,4,5,7}</pre>
Bateu certinho novamente! E qual é a complexidade? Se você implementou a multiplicação e a exponenciação de matrizes do jeito mais simples, vai ser da ordem de ##O(k^3 n)##, que certamente é melhor que exponencial mas ainda não é boa o suficiente para usar na prática.<br />
<br />
No entanto, você pode melhorar isso com implementações melhores! A multiplicação de matrizes pode usar o <a href="https://en.wikipedia.org/wiki/Strassen_algorithm">algoritmo de Strassen</a>, e a exponenciação pode usar o <a href="https://en.wikipedia.org/wiki/Exponentiation_by_squaring">método binário</a>, aí o total fica ##O(k^{2.8} \log_2 n)##, bem melhor; e se a matriz for esparsa dá para ficar ainda mais rápido.<br />
<br />
<h3 style="text-align: left;">
Função Geradora</h3>
<div>
<br /></div>
<div>
Mas tem um método que supera o anterior, que é usar <a href="https://en.wikipedia.org/wiki/Generating_function">funções geradoras</a>! Nós começamos tudo com um grafo orientado, e todo grafo orientado é equivalente a um <a href="https://en.wikipedia.org/wiki/Deterministic_finite_automaton">autômato finito</a>. Por sua vez, todo autômato finito é equivalente a uma <a href="https://en.wikipedia.org/wiki/Regular_grammar">gramática regular</a>, então deve ter um jeito de descrever os caminhos do lemming usando uma gramática. Para o exemplo dado, a gramática fica assim:</div>
<div>
<br /></div>
<div>
$$\begin{align*}\alpha &\to A \;|\; A \beta \;|\; A \gamma \\
\beta &\to B \;|\; B \gamma \\
\gamma &\to C \;|\; C \alpha\end{align*}$$<br />
Todos os caminhos possíveis produzem strings que são aceitas por essa gramática, e a gramática não aceita nenhuma string que não seja um caminho possível.<br />
<br />
O truque agora é usar <a href="https://en.wikipedia.org/wiki/Symbolic_method_(combinatorics)">combinatória analítica</a>, que para o problema de enumerar strings de uma gramática é bem simples: basta trocar cada token por um ##z##, e cada OR por uma adição. A gramática vira um sistema de equações:<br />
<br />
$$\begin{align*}\alpha &= z + z\beta +z \gamma \\ \beta &= z+z \gamma \\ \gamma &= z+z \alpha\end{align*}$$<br />
Resolvendo para ##\alpha##, chegamos na função geradora:<br />
<br />
$$\alpha=\frac{z + 2 z^2 + z^3}{1 - z^2 - z^3}$$<br />
E para que serve a função geradora? Olhe só o que acontece quando abrimos a função em série de potências:<br />
<br />
$$\frac{z + 2 z^2 + z^3}{1 - z^2 - z^3}=z+2z^2+2z^3+3z^4+4z^5+5z^6+7z^7+\dots$$<br />
<div>
Ahá! Os coeficientes de ##z## são exatamente o número de caminhos no grafo! Para calcular ##p(6)##, basta olhar o coeficiente de ##z^7## (você precisa somar um porque a função geradora está contando o número de tokens na strings, e nós queremos o número de passos).</div>
<br />
Podemos ir além agora. Quando a função geradora é uma <a href="https://en.wikipedia.org/wiki/Rational_function">função racional</a>, você sempre consegue inverter e conseguir uma fórmula explícita para ##p(n)##:<br />
<br />
$$p(n)=0.957\times 1.325^n+0.426\times 0.869^n\cos(1.469+ 2.438 n)$$<br />
Essa fórmula é exata (ou seria, se eu não tivesse truncado os números para caber na tela). Note que o resultado sempre vai ser um inteiro, e que a parte oscilante da fórmula sempre vai ser menor que 0.4, então dá para simplificar mais ainda:<br />
<br />
$$p(n)=\left\lfloor 0.5+0.957\times 1.325^n\right\rfloor$$<br />
Basta pegar a parte exponencial e arredondar para o inteiro mais próximo. Podemos observar duas coisas aqui. Primeiro, agora nós temos um algoritmo onde o número de operações independe do valor de ##n##, então efetivamente esse algoritmo é ##O(1)##. Na prática você vai implementar usando bignum, aí não fica ##O(1)## de verdade; mas os outros algoritmos mostrados também vão ficar mais lentos na mesma proporção.<br />
<br />
Segundo, nós provamos que aquele primeiro algoritmo era de fato ##O(c^n)##! Conseguimos até calcular quem é o tal do ##c##: é aproximadamente 1.325; ou, se você quiser o valor exato com radicais:<br />
<br />
$$c=\frac{\sqrt[3]{9+\sqrt{69}}+\sqrt[3]{9-\sqrt{69}}}{\sqrt[3]{18}}$$<br />
Para implementar esse método computacionalmente, é só reescrever aquele sistema de equações em forma matricial:<br />
<br />
$$\left[\begin{matrix}\alpha\\ \beta \\ \gamma\end{matrix}\right] = z \left[\begin{matrix}0 && 1 && 1\\0 && 0 && 1\\ 1 &&0 && 0\end{matrix}\right] \left[\begin{matrix}\alpha\\ \beta \\ \gamma\end{matrix}\right] + z\left[\begin{matrix}1\\ 1\\1\end{matrix}\right] $$<br />
<div>
A matriz ali no meio é a transposta da matriz de adjacências! Agora já podemos implementar:</div>
<pre class="prettyprint">count[graph_, pos_, size_] := With[{n = Length[graph]},
SeriesCoefficient[LinearSolve[
IdentityMatrix[n] - z Transpose[graph],
ConstantArray[z, n]][[pos]],
{z, 0, size + 1}]]</pre>
Como esperado, a resposta é correta:
<br />
<pre class="prettyprint">graph := {{0, 0, 1}, {1, 0, 0}, {1, 1, 0}}
Print[Table[count[graph, 1, n], {n, 0, 6}]]
{1,2,2,3,4,5,7}</pre>
<br />
<h3 style="text-align: left;">
Um Problema em Aberto</h3>
<br />
Nós chegamos em um algoritmo que é ##O(1)##, dá para otimizar mais? Não! Esse é um limite teórico.<br />
<br />
Quer dizer, existem algoritmos que são ##O(0)##. Por exemplo, "crie um algoritmo que, dado uma circunferência e um diâmetro, calcule a razão entre os dois". Esse é algoritmo é ##O(0)## porque a resposta é sempre ##\pi##, independe do input. Mas, se a resposta não é constante, ##O(1)## é o melhor algoritmo possível. (A não ser que você crie um algoritmo que dê a resposta antes do input chegar. Nesse caso me ensine como faz, porque você inventou a viagem no tempo).<br />
<br />
Mas existe uma pequena variação no problema dos lemmings que o torna muito, muito mais difícil. Suponha que agora o lemming não pode passar duas vezes pelo mesmo nó. A força bruta para essa variação ainda é ##O(c^n)##. Mas ninguém bolou um algoritmo que seja melhor que a força bruta nesse caso!<br />
<br />
Quando o grafo é finito, você pode usar a força bruta para enumerar todas as soluções, já que eventualmente o grafo acaba. Mas se o grafo for infinito, aí danou-se. Para esse caso, tem gente procurando a solução desde a década de 50. Já são setenta anos procurando o algoritmo e ninguém achou nada para um grafo genérico, nem mesmo um assintótico! O melhor resultado até hoje é um de 2011 onde acharam o ##c## para um grafo específico, o <a href="https://arxiv.org/abs/1007.0575">lattice hexagonal</a>. Para todos os outros grafos, o melhor que temos são heurísticas e aproximações numéricas.<br />
<br />
Se você estiver sem nada o que fazer no próximo fim de semana, essa pode ser uma boa maneira de ocupar o tempo!</div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-33773823631050084212018-01-19T11:35:00.000-03:002018-01-19T11:36:21.604-03:00O dia que o Knuth ganhou meu cheque<div dir="ltr" style="text-align: left;" trbidi="on">
<span style="font-family: inherit;">No último dia 10, o <a href="https://en.wikipedia.org/wiki/Donald_Knuth">Donald Knuth</a> completou 80 anos de vida. Para comemorar, ele fez uma festinha na remota e gelada cidadezinha de <a href="https://en.wikipedia.org/wiki/Pite%C3%A5">Piteå</a>, no norte da Suécia, e eu tive a sorte de poder participar. A festa foi em formato de simpósio de matemática: ao invés de presentes, cada amigo apresentou uma palestra sobre um tema relacionado ao trabalho do Knuth. (E os amigos dele são os caras mais famosos da computação, estavam lá o <a href="https://en.wikipedia.org/wiki/Robert_Sedgewick_(computer_scientist)">Sedgewick</a>, o <a href="https://en.wikipedia.org/wiki/Richard_M._Karp">Karp</a>, o <a href="https://en.wikipedia.org/wiki/Robert_Tarjan">Tarjan</a>, entre outros).</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwMi0-P7kwLpVbcS1XVhGt24T8kImy50LhyphenhyphenJ-v8yLr8E4-c4jFh1tCIP09KEdDZEwGbTEOoCMW6lHbL4-NxDAt-XV_Jp1j9Z39MTNTDQtPkNT2BH-8nzhVN6PQMxHPpgQhoydPS8GZRbat/s1600/knuth_niver+%25281%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="422" data-original-width="500" height="270" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwMi0-P7kwLpVbcS1XVhGt24T8kImy50LhyphenhyphenJ-v8yLr8E4-c4jFh1tCIP09KEdDZEwGbTEOoCMW6lHbL4-NxDAt-XV_Jp1j9Z39MTNTDQtPkNT2BH-8nzhVN6PQMxHPpgQhoydPS8GZRbat/s320/knuth_niver+%25281%2529.jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Eu também fiz minha contribuição, presenteei o Knuth com vários artigos contendo idéias para futuras revisões dos livros dele. Mas em um dos artigos aconteceu uma coisa engraçada: o Knuth notou que uma das minhas contas podia ser simplificada, e como agradecimento pela sugestão eu tive a oportunidade de inverter expectativas, e dar um cheque para o Knuth :D</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<h3 style="text-align: left;">
<span style="font-family: inherit;">A Soma dos Quadrados</span></h3>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">O artigo em questão era uma sugestão para o </span><a href="https://en.wikipedia.org/wiki/Concrete_Mathematics" style="font-family: inherit;">Concrete Mathematics</a><span style="font-family: inherit;">, que é o meu livro de matemática predileto. Sou fã desse livro desde os 17 anos, quando o usava como referência para estudar para a <a href="http://www.obm.org.br/">Olimpíada de Matemática</a>.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Quem já leu sabe que o livro contém mais de dez demonstrações diferentes da fórmula para a soma dos quadrados. Para ##n\ge 0##, a fórmula é:</span><br />
<span style="font-family: inherit;">$$\sum_{1\le x\le n} x^2=\frac{n(n+1)(2n+1)}{6}$$</span><br />
<span style="font-family: inherit;">As demonstrações vão desde as mais simples (usando <a href="https://en.wikipedia.org/wiki/Mathematical_induction">indução finita</a>), até as mais complexas (usando a <a href="https://en.wikipedia.org/wiki/Euler%E2%80%93Maclaurin_formula">fórmula de Euler-MacLaurin</a>). Porém, uns anos atrás, eu bolei uma demonstração nova que é provavelmente a <b>mais curta</b> demonstração de todas! Ela está na caixa azul abaixo:</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Testando valores pequenos, notamos que a fórmula é verdadeira para ##n=0##, ##n=1##, ##n=2##, ##n=3##. Logo, é verdadeira para qualquer ##n##.
</div>
<br />
Sim, eu sei o que você está pensando: "<i>Ricbit, você tá doido? Só porque funciona para alguns números não quer dizer que funciona para todos os números!</i>". <br />
<br />
A preocupação é válida: o que mais tem na matemática são fórmulas que funcionam para números pequenos mas falham para números grandes. Um exemplo famoso é o <a href="https://en.wikipedia.org/wiki/Lucky_numbers_of_Euler">polinômio sortudo de Euler</a>, ##k^2-k+41##, que produz apenas números primos para ##k=1##, ##k=2##, ##k=3##, etc., mas falha lá na frente quando ##k=41##.<br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Porém, <b>nesse caso</b>, testar números pequenos é suficiente para demonstrar a fórmula! Para entender o motivo precisamos de dois fatos sobre cálculo e polinômios.</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<h3 style="text-align: left;">
<span style="font-family: inherit;">O Cálculo Discreto</span></h3>
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Quem já estudou cálculo com certeza sabe algumas integrais de cabeça. Por exemplo, a integral do monômio:</span><br />
$$\int x^n\;dx=\frac{x^{n+1}}{n+1}$$<br />
O que você talvez não saiba é que o cálculo que a gente aprende nas faculdades de exatas é só um tipo de cálculo: o Cálculo Contínuo. Existem outros tipos, como o <a href="https://en.wikipedia.org/wiki/Finite_difference">Cálculo Discreto</a>, que lida com somatórias ao invés de integrais. Esse cálculo possui várias fórmulas análogas ao cálculo mais comum. Em especial, ele tem uma fórmula análoga à integral do monômio:<br />
$$\sum x^{\underline{n}}\;\delta x=\frac{x^{\underline{n+1}}}{n+1}$$<br />
Nesse fórmula o monômio não usa a potência simples, ele usa a <a href="https://en.wikipedia.org/wiki/Falling_and_rising_factorials">potência fatorial</a>, que é definida da seguinte maneira:<br />
$$x^{\underline n}=x(x-1)(x-2)\dots(x-n+1)$$<br />
É como se fosse um fatorial, mas ao invés de ir até o fim, você pega só os ##n## primeiros termos. (Para pronunciar ##x^{\underline n}##, você fala "<i>x elevado a n caindo</i>").<br />
<br />
Para converter uma potência fatorial em uma potência normal, você pode abrir a definição e multiplicar todos os termos, mas isso dá trabalho. É mais fácil ter à mão uma tabela com os <a href="https://en.wikipedia.org/wiki/Stirling_number">números de Stirling</a> (que vêm em dois tipos, o primeiro tipo ##{n\brack k}##, e o segundo tipo ##{n\brace k}##). Esses números são fáceis de calcular porque eles obedecem uma regra similar ao <a href="https://en.wikipedia.org/wiki/Pascal%27s_triangle">triângulo de Pascal</a>. Tendo a tabela, as fórmulas abaixo fazem a conversão:<br />
$$\begin{align*}<br />
x^{\underline{n}}&=\sum_k{n\brack k}(-1)^{n-k}x^k\\<br />
x^{n}&=\sum_k{n\brace k}x^{\underline{k}}\end{align*}$$<br />
Usando as fórmulas acima e alguma paciência, você consegue demonstrar a fórmula da soma dos quadrados (converta ##x^2## em potências fatoriais, use a fórmula de somatória do cálculo discreto, depois converta de volta as potências fatoriais em potências tradicionais).<br />
<br />
<span style="font-family: inherit;">Mas isso dá muito trabalho. Ao invés disso, note que as fórmulas acima tem uma consequência interessante: com elas é possível ver que <b>a somatória de um polinômio de grau ##n## sempre vai ser um polinômio de grau ##n+1##,</b></span> e em especial a somatória de ##x^2## vai ser um polinômio de grau 3, a gente só não sabe qual polinômio ainda. Mas aí temos outro truque nas mangas!<br />
<br />
<h3 style="text-align: left;">
Interpolação de polinômios</h3>
<br />
Certo, eu não sei qual é o polinômio de grau 3 que resolve a somatória. Vamos então escrever esse polinômio na forma ##P(n)=an^3+bn^2+cn+d##. Apesar de não conhecermos o polinômio, é fácil descobrir os pontos por onde ele passa, basta calcular a somatória! <br />
<br />
Para valores pequenos de ##n##, podemos tabelar ##P(0)=0##, ##P(1)=1##, ##P(2)=1+4=5##, ##P(3)=1+4+9=14##, agora podemos achar os coeficientes usando <a href="https://en.wikipedia.org/wiki/Linear_algebra">álgebra linear</a>:<br />
$$\left[\begin{array}{c}0\\1\\5\\14\end{array}\right]=<br />
\left[\begin{array}{cccc}0^3&0^2&0^1&1\\1^3&1^2&1^1&1\\2^3&2^2&2^1&1\\3^3&3^2&3^1&1\end{array}\right]<br />
\left[\begin{array}{c}a\\b\\c\\d\end{array}\right]$$<br />
Novamente, dá muito trabalho. Ao invés disso, notamos que <b>um polinômio de grau ##n## fica completamente determinado por ##n+1## pontos</b>, e agora podemos finalmente entender a demonstração inicial!<br />
<br />
Olhe novamente para o que queremos demonstrar:<br />
$$\sum_{1\le x\le n} x^2=\frac{n(n+1)(2n+1)}{6}$$<br />
De um lado, temos uma somatória de polinômios de grau 2, que sabemos que é um polinômio de grau 3. Do outro lado, temos um polinômio cujo grau também é 3. Para provar que esses dois polinômios são iguais, é suficiente testar os dois polinômios em quatro pontos. Escolhemos os mais fáceis que são ##n=0##, ##n=1##, ##n=2##, ##n=3##, como a fórmula funciona para esses quatro valores, então funciona para todos os valores, QED.<br />
<br />
<h3 style="text-align: left;">
O cheque do Knuth</h3>
<br />
Quando eu presenteei o artigo com essa demonstração para o Knuth, em segundos após a leitura ele comentou: "<i>Você podia ter usado -1 aqui!</i>".<br />
<br />
É verdade! Essa demonstração é tão curta que dá para fazer de cabeça, mas as contas para ##n=3## ficam meio chatinhas. Ao invés disso, você pode usar -1 no lugar de 3, e aí fica bem mais fácil: do lado esquerdo a soma é vazia e dá 0, do lado direito o termo ##(n+1)## zera, logo o resultado dá zero também. Você precisa expandir o domínio da fórmula, de ##n\ge 0## para ##n\ge -1##, o que significa que a fórmula provada com -1 é até melhor que a fórmula provada com 0!<br />
<br />
Tradicionalmente, o Knuth oferece $0x1 hexadólar para quem acha <a href="https://en.wikipedia.org/wiki/Knuth_reward_check">um erro em um artigo dele</a>, e $0x0.20 para sugestões. Pela simplificação que ele sugeriu, eu achei pertinente oferecer um cheque também!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidhZWH4CzBk7mCZqTEs0eJsPrLj3Mj1WQCW-lOc3X4Ks93Kgn6KrZGnVIENVV-X6WbWf2z4Exi6_TWqWgMmg81jHXRa1ZhwwPN0IRoH8UxexTKE6VzXCBUdrl5M_15YJYIQuHP4uS8Jmk/s1600/26240827_10155012862596594_4638598898474274641_o.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1039" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidhZWH4CzBk7mCZqTEs0eJsPrLj3Mj1WQCW-lOc3X4Ks93Kgn6KrZGnVIENVV-X6WbWf2z4Exi6_TWqWgMmg81jHXRa1ZhwwPN0IRoH8UxexTKE6VzXCBUdrl5M_15YJYIQuHP4uS8Jmk/s320/26240827_10155012862596594_4638598898474274641_o.jpg" width="207" /></a></div>
<br />
Aqui o detalhe do cheque. O Knuth emite cheques pelo <a href="https://cs.stanford.edu/~knuth/boss.html">banco fictício de San Serriffe</a> (eu tenho $0x5.80 lá atualmente). Os meus cheques eu resolvi emitir pelo Banco de Dados:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7FS0kNtJmlba6IbLepcc99SCCKVOfWiCJ9eip-7gTiwJnsYGsxOHNQrrPDQRX5znPyQe5_wFwQSW6owtOO5FCPI9RzIfv9OAZiG8ZT0_zzRxNt85cne210BdEQLMAAsSLAEPEqYH0YRM/s1600/chequeknuth.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="815" data-original-width="1600" height="163" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7FS0kNtJmlba6IbLepcc99SCCKVOfWiCJ9eip-7gTiwJnsYGsxOHNQrrPDQRX5znPyQe5_wFwQSW6owtOO5FCPI9RzIfv9OAZiG8ZT0_zzRxNt85cne210BdEQLMAAsSLAEPEqYH0YRM/s320/chequeknuth.jpg" width="320" /></a></div>
<br />
O Knuth adorou o cheque! Ele ficou impressionado em como eu consegui fazer tão rápido um cheque que parece mesmo um cheque, mas o segredo é simples: quem fez fez a arte foi a <a href="http://www.ilafox.com/">Ila Fox</a>, eu só imprimi no hotel onde estava, usando papel reciclado.<br />
<br />
Sem dúvida foi a melhor festa de aniversário que já participei! O Knuth já tinha feito uma festa dessas quando completou 64 anos, e para manter a simetria ele disse que vai fazer outra quando completar 96 anos. Eu certamente estarei lá mais uma vez :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-75209025795938476572016-09-05T01:21:00.000-03:002019-09-03T01:23:16.771-03:00A Matemática do Pokémon GO<div dir="ltr" style="text-align: left;" trbidi="on">
Você já capturou todos os pokémons do Pokémon GO? Eu ainda não consegui, e também não conheço ninguém que tenha conseguido. O motivo é simples: para cada Pikachu que você encontrar, vai passar por um centena de Zubats antes! Fica difícil pegar todos quando alguns são tão raros!<br />
<br />
<a href="https://www.ilafox.com.br/ricbit/images/pokemon_zubat.jpg"><img alt="pokemon_zubat" class="aligncenter size-full wp-image-377" height="367" src="https://www.ilafox.com.br/ricbit/images/pokemon_zubat.jpg" width="500" /></a><br />
<br />
A pergunta natural agora é: se eu quiser insistir e continuar jogando até pegar todos, quanto tempo vai levar? Ou, de outro modo, qual é o <strong>número esperado de capturas</strong> até completar a coleção? Esse problema é difícil de resolver no caso geral, mas fica mais simples usando <em>Combinatória Analítica</em>!
<br />
<br />
<h3 style="text-align: left;">
Combinatória Analítica Numerada</h3>
<br />
No <a href="http://scienceblogs.com.br/caixaazul/2016/07/totorial-de-combinatoria-analitica/">último post do blog</a> eu mostrei como funciona a Combinatória Analítica, mostrando uma aplicação de cálculo com sequências de <em>cara</em> ou <em>coroa</em>. Mas essa foi só metade da história! Aquele tipo de cálculo só funciona quando os objetos contados são indistinguíveis (um resultado <em>cara</em> não é diferente de outro resultado <em>cara</em>).<br />
<br />
Se você quiser lidar com objetos que são todos diferentes entre si, então você precisa de outra abordagem. Por exemplo, quantas permutações de ##n## elementos existem? Nesse caso, os elementos precisam ser todos diferentes. Quando ##n=3##, existem ##6## permutações:<br />
$$ \enclose{circle}{1} \enclose{circle}{2} \enclose{circle}{3} \longrightarrow
\begin{cases}
\enclose{circle}{1} \enclose{circle}{2} \enclose{circle}{3} \\
\enclose{circle}{1} \enclose{circle}{3} \enclose{circle}{2} \\
\enclose{circle}{2} \enclose{circle}{1} \enclose{circle}{3} \\
\enclose{circle}{2} \enclose{circle}{3} \enclose{circle}{1} \\
\enclose{circle}{3} \enclose{circle}{1} \enclose{circle}{2} \\
\enclose{circle}{3} \enclose{circle}{2} \enclose{circle}{1} \\
\end{cases}$$<br />
Para esse tipo de cálculo nós precisamos da <em>Combinatória Analítica Numerada</em>. Existem duas diferenças principais entre as versões numeradas e não-numeradas da teoria. A primeira que é, ao invés de usar funções geradoras normais, na versão numerada nós usamos <strong>funções geradoras exponenciais</strong> (EGF), que são definidas como abaixo:
<br />
$$EGF(F[n]) = \sum_{n\ge 0}{F[n]\frac{z^n}{n!}}$$<br />
Quando a ordem dos objetos é importante, os resultados tendem a crescer muito rapidamente, então dividir por ##n!## termo a termo ajuda as funções a ficarem bem comportadas.<br />
<br />
A outra diferença é em um dos operadores básicos. A adição continua representando a união de conjuntos, mas a multiplicação, que antes era concatenação, fica um pouco diferente. No caso numerado, você não pode simplesmente concatenar, porque os números precisam ser sempre de ##1## a ##n##, e a concatenação simples te deixaria com duplicatas:<br />
$$ \enclose{circle}{1} \enclose{circle}{2} \star \enclose{circle}{2} \enclose{circle}{1} =\; ?$$<br />
A solução é renomear as bolinhas após concatenar, mas você precisa renomear de todas as maneiras possíveis que deixem a ordem dentro dos subconjuntos originais intacta:<br />
$$ \enclose{circle}{1} \enclose{circle}{2} \star \enclose{circle}{2} \enclose{circle}{1} =
\begin{cases}
\enclose{circle}{1} \enclose{circle}{2} \; \enclose{circle}{4} \enclose{circle}{3} \\
\enclose{circle}{1} \enclose{circle}{3} \; \enclose{circle}{4} \enclose{circle}{2} \\
\enclose{circle}{1} \enclose{circle}{4} \; \enclose{circle}{3} \enclose{circle}{2} \\
\enclose{circle}{2} \enclose{circle}{3} \; \enclose{circle}{4} \enclose{circle}{1} \\
\enclose{circle}{2} \enclose{circle}{4} \; \enclose{circle}{3} \enclose{circle}{1} \\
\enclose{circle}{3} \enclose{circle}{4} \; \enclose{circle}{2} \enclose{circle}{1} \\
\end{cases}$$<br />
Com isso já conseguimos definir as permutações. Elas são formadas do conjunto vazio, ou de um elemento concatenado e renomeado com uma permutação:<br />
$$P=\varepsilon + z\star P$$<br />
Traduzimos direto para a EGF:<br />
$$P=1 +z P\implies P=\frac{1}{1-z}$$<br />
E extraímos os coeficientes por comparação com a definição de EGF:<br />
$$\begin{align*}
\sum_{n\ge 0}P[n]\frac{z^n}{n!} &= \frac{1}{1-z} \\
\sum_{n\ge 0}\frac{P[n]}{n!}z^n &= \sum_{n\ge 0}1\times z^n \\
\frac{P[n]}{n!} &= 1 \\
P[n] &= n!
\end{align*}$$<br />
A quantidade de permutações de tamanho ##n## é ##n!##, então o método funciona!
<br />
<br />
<h3 style="text-align: left;">
O operador SEQ</h3>
<br />
Também podemos definir o operador ##SEQ##, que enumera todas as sequências possíveis de um conjunto:<br />
$$SEQ(B[z]) = \frac{1}{1-B[z]}$$<br />
Note que as permutações também podem ser vistas como as sequências renomeadas de zero ou mais átomos, então a definição acima dá o mesmo resultado.<br />
<br />
Outra definição útil é ##SEQ_k##, que enumera apenas as sequências com ##k## elementos:<br />
$$SEQ_k(B[z]) = (B[z])^k$$
<br />
<br />
<h3 style="text-align: left;">
O operador SET</h3>
<br />
Todas as funções acima são para construções onde a ordem é importante. Quando a ordem não importa, você sempre pode voltar para a Combinatória Analítica Não-Numerada. Mas e se o seu problema envolver os dois tipos ao mesmo tempo?<br />
<br />
Nesse caso, a solução é o operador ##SET##. Ele é um operador numerado que define um conjunto onde a ordem não importa. A definição é a abaixo:<br />
$$SET(B[z]) = e^{B[z]}$$<br />
Por exemplo, quantas sequências possíveis de números de ##1## a ##n## existem, se a ordem não for importante? Isso é dado por:<br />
$$SET(z) = e^z$$<br />
Agora basta abrir a exponencial em série de Taylor e comparar com a definição de EGF:<br />
$$\begin{align*}
\sum_{n\ge 0}F[n]\frac{z^n}{n!} &= e^z \\
\sum_{n\ge 0}\frac{F[n]}{n!}z^n &= \sum_{n\ge 0}\frac{1}{n!}z^n \\
\frac{F[n]}{n!} &= \frac{1}{n!} \\
F[n] &= 1
\end{align*}$$<br />
Ou seja, quando a ordem não é importante, só existe uma única sequência de ##1## a ##n## que usa todos os números de ##1## a ##n##. Faz sentido né.<br />
<br />
(É por causa desse caso que a EGF chama função geradora exponencial. A EGF de uma sequência de ##1##s é a função exponencial.)
<br />
<br />
<h3 style="text-align: left;">
Pokémons uniformes</h3>
<br />
Com as ferramentas em mãos, já conseguimos modelar o problema do Pokémon GO! Mas, antes de começar o problema real, vale a pena estudar um caso mais simples. Vamos supor que os pokémons são uniformes e resolver esse caso mais fácil primeiro, depois a gente generaliza para o caso onde alguns pokémons são mais raros que outros.<br />
<br />
O truque para usar Combinatória Analítica Numerada é achar uma maneira de enxergar seu problema como uma sequência de ##1## de ##n##. No nosso caso podemos fazer da seguinte maneira: para cada pokémon nós temos uma caixinha, e cada vez que achamos um pokémon na rua, nós colocamos dentro da caixinha uma bola com um número indicando a ordem em que ele foi encontrado.<br />
<br />
Por exemplo, suponha que andávamos pela rua e fizemos 6 capturas, nessa ordem: Bulbassauro, Squirtle, Bulbassauro de novo, Charmander, Squirtle de novo, Bulbassauro de novo. As caixinhas ficariam assim:
<br />
<br />
<table style="margin-bottom: 30px;">
<tbody>
<tr><td style="padding-right: 10px;"><img alt="bulbassauro" src="https://www.ilafox.com.br/ricbit/images/bulbasaur.png" /></td><td>##\enclose{box}{\enclose{circle}{1} \,\enclose{circle}{3}\, \enclose{circle}{6}}##</td></tr>
<tr><td style="padding-right: 10px;"><img alt="squirtle" src="https://www.ilafox.com.br/ricbit/images/squirtle.png" /></td><td>##\enclose{box}{\enclose{circle}{2}\, \enclose{circle}{5} }##</td></tr>
<tr><td style="padding-right: 10px;"><img alt="charmander" src="https://www.ilafox.com.br/ricbit/images/charmander.png" /></td><td>##\enclose{box}{\enclose{circle}{4}} ##</td></tr>
</tbody></table>
<br />
Se nenhuma caixinha está vazia, então em algum ponto nós pegamos todos os pokémons (nesse caso, foi na quarta captura). Então, para ##n## capturas, se calcularmos a quantidade de configurações em que nenhuma caixinha está vazia, dividida pela quantidade de configurações totais, nós vamos ter a probabilidade de que a coleção foi completada com ##n## capturas ou menos.<br />
<br />
Vamos lá. Se nós temos ##k## pokémons e ##n## capturas, então a quantidade de configurações totais é ##k^n## (é só atribuir uma pokémon a cada captura).<br />
<br />
Para a quantidade de configurações em que nenhuma caixinha está vazia, precisamos modelar o problema com os operadores que descrevemos antes. Os pokémons são diferentes entre si, e a ordem importa, então vamos usar o operador ##SEQ##. A ordem das bolinhas dentro da caixinha não importa (afinal, ##\enclose{circle}{1} \enclose{circle}{3} \enclose{circle}{6}## é a mesma coisa que ##\enclose{circle}{3} \enclose{circle}{6} \enclose{circle}{1}## nesse contexto: a ordem das capturas é determinada pelos números nas bolinhas, não pela posição delas). Então vamos usar o operador ##SET##. E não podemos esquecer de que nenhuma caixinha pode ser vazia. Então a construção combinatória que descreve o problema é:<br />
$$C=SEQ_k(SET(z)-\varepsilon)$$<br />
..ou seja, uma sequência de ##k## conjuntos não-vazios. A função geradora é, portanto:
<br />
$$C[z]=(e^z-1)^k$$<br />
Note que, se nós já temos a função geradora, então a parte combinatória do problema já acabou. Daqui em diante é só abrir a caixa azul e fazer as contas!
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; margin-bottom: 10px; padding: 10px;">
Nós queremos calcular o valor esperado do número de capturas até completar a coleção. Pela definição, isso é:
$$E[X]=\sum_{n\ge 0}n P(X=n)$$
Onde ##P(X=n)## é a probabilidade de que nós completamos a coleção na captura ##n##. Mas nós não temos isso! O que temos é ##P(x\le n)##, a probabilidade de completamos a coleção com pelo menos ##n## capturas. Felizmente não tem problema, porque dá para converter um no outro:
$$\begin{align*}
E[X]
&= \sum_{n\ge 0} n P(X=n) \\
&= \sum_{n\ge 0} \sum_{i\lt n} P(X=n) \\
&= \sum_{n\ge 0} \sum_{i\ge 0} P(X=n) [i\lt n] \\
&= \sum_{i\ge 0} \sum_{n\ge 0} P(X=n) [n\gt i] \\
&= \sum_{i\ge 0} \sum_{n\gt i} P(X=n) \\
&= \sum_{i\ge 0} P(X\gt i) \\
&= \sum_{i\ge 0} 1-P(X\le i) \\
\end{align*}$$
Esse truque funciona sempre que a distribuição é discreta, porque podemos abrir a probabilidade:
$$P(X>n)=P(X=n+1)+P(X=n+2)+\cdots$$
Vamos substituir agora. A nossa probabilidade para ##X\le n## é a divisão da quantidade de configurações com caixinhas não-vazias (que é o coeficiente ##z^n## da função geradora, vezes ##n!## porque é uma função geradora exponencial) pela quantidade total (que é ##k^n##):
$$P(X\le n)=\frac{n![z^n](e^z-1)^k}{k^n}$$
Logo a expectativa é:
$$\begin{align*}
E[X]
&=\sum_{n\ge 0} 1-P(X\gt n) \\
&=\sum_{n\ge 0} 1-\frac{n![z^n](e^z-1)^k}{k^n} \\
&=\sum_{n\ge 0} 1-n![z^n](e^{z/k}-1)^k
\end{align*}$$
Esse último passo usou uma propriedade de escala das funções geradoras. Se uma sequência ##f(n)## tem função geradora ##F(z)##, então:
$$\begin{align*}
\sum_{n\ge 0}f(n)z^n &= F(z) \\
\sum_{n\ge 0}k^n f(n)z^n &= \sum_{n\ge 0}f(n)(kz)^n = F(kz)
\end{align*}$$
Voltando: nós vimos que a função geradora exponencial da sequência constante de ##1##s é ##e^z##, então dá pra jogar para dentro:
$$\sum_{n\ge 0} 1-n![z^n](e^{z/k}-1)^k = \sum_{n\ge 0} n![z^n]\left(e^z -\left(e^{z/k}-1\right)^k\right)$$
Para prosseguir agora tem um truque muito bom, que funciona assim: se você tem uma função geradora ##F(z)## <a href="https://en.wikipedia.org/wiki/Entire_function">bem comportada</a>, então vale que:
$$\sum_{n\ge 0}n![z^n]F(z)=\int_0^\infty e^{-t}F(t)\,dt$$
Parece meio mágico, mas é só consequência da forma integral do fatorial:
$$n!=\int_0^\infty t^n e^{-t}\,dt$$
Se nós chamarmos os coeficientes da função geradora de ##f_n##, então:
$$\begin{align*}
\sum_{n\ge 0}n![z^n]F(z)
&=\sum_{n\ge 0}n! f_n = \sum_{n\ge 0} f_n n! \\
&=\sum_{n\ge 0}f_n\left(\int_0^\infty t^ne^{-t}\,dt\right) \\
&=\int_0^\infty \left(\sum_{n\ge 0}f_n t^n e^{-t}\right)\,dt\\
&=\int_0^\infty e^{-t}\left(\sum_{n\ge 0}f_n t^n \right)\,dt\\
&=\int_0^\infty e^{-t}F(t)\,dt\\
\end{align*}$$
Aplicando ao nosso caso:
$$\begin{align*}
\sum_{n\ge 0} n![z^n]\left(e^z -\left(e^{z/k}-1\right)^k\right)
&= \int_0^\infty e^{-t} \left(e^t -\left(e^{t/k}-1\right)^k\right) \,dt \\
&= \int_0^\infty \left(1 -e^{-t}\left(e^{t/k}-1\right)^k\right) \,dt \\
&= \int_0^\infty \left(1 -\left(e^{-t/k}\right)^k\left(e^{t/k}-1\right)^k\right) \,dt \\
&= \int_0^\infty \left(1 -\left(1-e^{-t/k}\right)^k\right) \,dt \\
\end{align*}$$
Nesse ponto já daria para jogar num solver numérico e calcular a solução. Mas essa integral tem uma forma fechada! Para calcular, basta fazer a mudança de variável abaixo:
$$\begin{align*}
v &= 1-e^{-t/k} \\
1-v &= e^{-t/k} \\
e^{t/k} &= \frac{1}{1-v}
\end{align*}$$
Agora deriva para achar quanto vale ##dt##:
$$\begin{align*}
\frac{dv}{dt} &= \frac{e^{-t/k}}{k} \\
dt &= k \, e^{t/k}\, dv \\
dt &= \frac{k}{1-v} dv
\end{align*}$$
Arruma os intervalos:
$$\begin{align*}
t=0 &\implies v=1-e^0=1-1=0 \\
t=\infty &\implies v=1-e^{-\infty}=1-0=1
\end{align*}$$
E por fim soca tudo na integral:
$$\begin{align*}
\int_0^\infty \left(1 -\left(1-e^{-t/k}\right)^k\right) \,dt
&= \int_0^1 \left(1 -v^k\right) \frac{k}{1-v} \,dv \\
&= k\int_0^1 \frac{1-v^k}{1-v} \,dv \\
&= k\int_0^1 (1+v+v^2+\cdots+v^{k-1}) \,dv \\
&= k \left[v+\frac{v^2}{2}+\frac{v^3}{3}+\cdots+\frac{v^k}{k} \right]_0^1 \\
&= k \left[1+\frac{1}{2}+\frac{1}{3}+\cdots+\frac{1}{k} \right] \\
&= k H[k] \\
\end{align*}$$
Esse é o resultado final. O número esperado de capturas até você completar sua coleção de pokémons é ##k## vezes o <a href="https://en.wikipedia.org/wiki/Harmonic_number">harmônico</a> de ##k##!
</div>
<br />
Qual seria o número esperado de capturas então? O jogo tem 151 pokémons, mas desses tem seis que ainda não podem ser pegos: Ditto, Articuno, Zapdos, Moltres, Mewtwo e Mew. Para pegar os 145 restantes, você precisaria de ##145\times H(145)\sim 806## capturas.<br />
<br />
Mas isso só vale se os pokémons fossem uniformes! Como alguns são mais raros que outros, precisamos melhorar nossa estimativa.
<br />
<br />
<h3 style="text-align: left;">
Pokémons raros</h3>
<br />
A grande vantagem da Combinatória Analítica é que ela generaliza muito fácil. O problema de completar uma coleção com elementos não-uniformes é bem difícil com técnicas tradicionais, mas aqui fica simples: basta trocar os átomos ##Z_i## por ##p_iZ_i##, onde ##p_i## é a probabilidade de ocorrência dele.<br />
<br />
Por que isso funciona? Suponha que você tem três pokémons, de probabilidades ##p_1=1/6##, ##p_2=1/3## e ##p_3=1/2##. Ao invés de fazer ##SEQ_3(SET(z)-\varepsilon)##, como no caso uniforme, você pode fazer:<br />
$$(SET(Z_1)-\varepsilon)\star (SET(Z_2+Z_2)-\varepsilon)\star (SET(Z_3+Z_3+Z_3)-\varepsilon)$$<br />
Isso vai dar um resultado seis vezes maior que o esperado, mas na proporção correta. Se você jogar esse valor ##1/6## para dentro, a conta fica:
<br />
$$\begin{align*}
&(SET(\frac{Z_1}{6})-\varepsilon)\star (SET(\frac{2Z_2}{6})-\varepsilon)\star (SET(\frac{3Z_3}{6})-\varepsilon)=\\
&(SET(p_1Z_1)-\varepsilon)\star (SET(p_2Z_2)-\varepsilon)\star (SET(p_3Z_3)-\varepsilon)
\end{align*}$$<br />
Exatamente como mencionamos! Isso mostra que o resultado é válido para qualquer probabilidade racional, e você pode fazer um sanduíche se quiser reais também.<br />
<br />
Daqui em diante as contas são as mesmas do caso uniforme, mudando somente o interior da integral:
<br />
$$\begin{align*}
E[X]
&=\int_0^\infty\left( 1-\left(1-e^{-p_1t}\right)\left(1-e^{-p_2t}\right)\cdots\left(1-e^{-p_kt}\right) \right)\,dt\\
&=\int_0^\infty\left( 1- \prod_{1\le i\le k}\left(1-e^{-p_i t}\right) \right)\,dt\\
\end{align*}$$<br />
Infelizmente essa integral não tem forma fechada, precisa ser calculada numericamente mesmo. Usando uma tabela com as <a href="https://pokeassistant.com/main/pokemonstats">probabilidades estimadas de cada pokémon</a>, eu calculei a integral, e o resultado é que você precisaria de 51568 capturas para completar sua coleção!<br />
<br />
Como de costume, eu fiz uma simulação para conferir os valores. Os scripts <a href="https://github.com/ricbit/Oldies/tree/master/2016-09-pokemongo">estão no github</a>, e os resultados são:<br />
<br />
Distribuição <strong>uniforme</strong>, valor teórico <strong>805.8</strong>, computacional <strong>805.1</strong><br />
Distribuição <strong>não-uniforme</strong>, valor teórico <strong>51568</strong>, computacional <strong>50931</strong><br />
<br />
Os resultados bateram como previsto! Vale a pena também estimar o tempo: se você pegar um pokémon a cada 3 minutos, então para fazer 51568 capturas você precisaria jogar por <strong>107 dias seguidos</strong>, sem parar para dormir!<br />
<br />
Na prática você pode completar em menos tempo que isso usando outros recursos do jogo, como evoluções e ovos, mas não tem muito segredo, para ser um mestre pokémon você precisa se dedicar bastante :)<br />
<br />
<em>PS: Como quase não tem literatura em português sobre o tema, as traduções ainda não são padrão. Eu traduzi Labelled Analytic Combinatorics como Combinatória Analítica Numerada, porque não achei nenhuma outra tradução que não fosse feia demais.</em></div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-46443007656075584732016-07-30T01:09:00.000-03:002019-09-03T01:11:35.393-03:00Totorial de Combinatória Analítica<div dir="ltr" style="text-align: left;" trbidi="on">
Se você jogar Cara ou Coroa várias vezes em sequência, qual a chance de nunca aparecer duas caras seguidas?<br />
<br />
Esse é um problema que você resolve com ferramentas da <strong>Combinatória</strong>. Se você estudou para o vestibular, certamente deve lembrar que esse tipo de problema usualmente se resolvia com permutações, arranjos ou combinações.<br />
<br />
Infelizmente, isso é verdade no vestibular, mas não na vida real. Os problemas avançados de Combinatória resistem a essas ferramentas básicas. Em geral, você vai precisar de recorrências complexas e somatórias complicadas; e até recentemente não tinha nenhum método que fosse simples e geral, para usar em qualquer situação.<br />
<br />
A boa notícia é que isso mudou! Nos últimos anos, surgiu um novo ramo da matemática, chamado de <strong>Combinatória Analítica</strong>, que permite calcular esses problemas com facilidade. Como ainda não existe nenhum texto introdutório em português, resolvi escrever um <em>totorial</em> para mostrar como as coisas ficam bem simples com esse método!<br />
<a href="https://www.ilafox.com.br/ricbit/images/totoro.jpg"><img alt="totoro" class="aligncenter size-full wp-image-318" height="422" src="https://www.ilafox.com.br/ricbit/images/totoro.jpg" width="500" /></a><br />
<br />
<h3 style="text-align: left;">
Um exemplo simples</h3>
<br />
Como funciona esse método novo? A analogia mais simples é com desenho geométrico. Com as técnicas tradicionais, você precisa de régua, compasso e uma boa dose de insight para resolver os problemas. Ao invés disso, você pode usar geometria analítica: o problema é transformado em uma série de equações, aí você não precisa pensar, só resolver (e em muitos casos nem precisa resolver, é só jogar num sistema de computação automática como o Wolfram Alpha que ele resolve para você).<br />
<br />
Com combinatória analítica é parecido: você vai transformar a descrição do seu problema de contagem em uma equação, e aí existem técnicas padrão para resolvê-la. Para mostrar como funciona, vamos pegar um problema simples: <strong>Quantas strings binárias de tamanho ##n## existem</strong>?<br />
<br />
Para ##n=3##, por exemplo, existem oito strings: ##000##, ##001##, ##010##, ##011##, ##100##, ##101##, ##110## e ##111##.<br />
<br />
A primeira coisa a ser feita é caracterizar o conjunto de todas as strings. Podemos recursivamente construir as strings binárias válidas com três casos:<br />
<br />
<strong>Caso 1</strong>: A string vazia (##\varepsilon##), ou...<br />
<br />
<strong>Caso 2</strong>: Uma string que começa com ##0##, seguida de uma string válida, ou...<br />
<br />
<strong>Caso 3</strong>: Uma string que começa com ##1##, seguida de uma string válida.<br />
<br />
Pronto, agora podemos agora escrever a equação que descreve o conjunto ##B## das strings binárias. Em combinatória analítica, a regra é que a descrição "<em>ou</em>" vira adição, e a operação "<em>seguida de</em>" vira multiplicação. Vou também substituir o caractere ##0## por ##Z_0## e ##1## por ##Z_1## só para deixar claro que nesse contexto são átomos, e não números:<br />
$$B=\varepsilon+Z_0\times B+Z_1\times B$$<br />
Essa é a construção combinatória correspondente às strings binárias. Agora o pulo do gato: eu vou trocar ##\varepsilon## pelo número ##1##, cada átomo individual pela variável ##z##, e resolver a equação:<br />
$$\begin{align*}
B&=\varepsilon+Z_0\times B+Z_1\times B\\
B&=1+zB+zB\\
B&=\frac{1}{1-2z}
\end{align*}$$<br />
Chegamos então no elemento principal da combinatória analítica: a <b>função geradora</b> do conjunto das strings binárias. E por que ela é tão importante? É que essa função simples tem escondida dentro dela a solução do problema para qualquer ##n##! Basta expandir a função em série (nesse caso, basta reconhecer que essa é a fórmula da soma infinita de uma PG com razão ##2z##):
<br />
$$B=\frac{1}{1-2z}=1+2z+4z^2+8z^3+16z^4+\cdots$$<br />
Olha só, a solução aparece na série! O termo ##8z^3## significa que, para ##n=3##, a solução é ##8##, exatamente como tínhamos visto enumerando as possbilidades.
<br />
<br />
<h3 style="text-align: left;">
O operador SEQ</h3>
<br />
O método acima foi simples né? Mas dá para ficar mais fácil ainda! O pessoal que estuda combinatória analítica faz bastante tempo inventou uma série de operadores que deixam a solução mais fácil de escrever.<br />
<br />
O primeiro deles é o operador ##SEQ##, que serve para definir sequências. Ao invés da definição recursiva com três casos, podemos simplesmente dizer que uma string binária é formada por <strong>uma sequência de zero ou mais átomos</strong>, onde os átomos possíveis são ##0## e ##1##. Daí a construção combinatória sai direto:<br />
$$B=SEQ(Z_0+Z_1)$$<br />
E como transformar isso em função geradora? Se você sabe que um conjunto ##A## tem função geradora ##A(z)##, então a função geradora da sequência de ##A## é:
<br />
$$SEQ(A)=\frac{1}{1-A(z)}$$
<br />
No nosso caso, a função geradora das strings binárias é:
<br />
$$SEQ(Z_0+Z_1)=SEQ(z+z)=\frac{1}{1-2z}$$<br />
Pronto, a função geradora saiu direto, sem precisar resolver nenhuma equação!<br />
<br />
<h3 style="text-align: left;">
O teorema de transferência</h3>
<br />
Ainda resta o problema de abrir a função geradora em série. Nesse caso foi fácil porque conseguimos ver de cara que a função era a soma de uma PG, mas e em casos mais complicados?<br />
<br />
Bem, resolver de maneira exata continua sendo um problema difícil até hoje. Mas na maioria das vezes você não precisa da solução exata, um assintótico para ##n## grande já é suficiente. E para isso a combinatória analítica possui uma série de <em>teoremas de transferência</em>, que dão a resposta assintótica da função geradora sem precisar abrir a série!<br />
<br />
O teorema de transferência usado nesse caso é simples de usar, embora sua definição seja meio assustadora:
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; margin-bottom: 10px; padding: 10px;">
Se a sua função geradora for uma função racional ##f(z)/g(z)##, onde ##f(z)## e ##g(z)## são relativamente primas, ##g(0)\neq 0##, ##g(z)## tem um único pólo ##1/\beta## de módulo mínimo, e ##\nu## é a multiplicidade desse pólo, então um assintótico para a função é dado por:
$$[z^n]\frac{f(z)}{g(z)}=\nu\frac{(-\beta)^\nu f(1/\beta)}{g^{(\nu)}(1/\beta)}\beta^n n^{\nu-1}$$</div>
<br />
Eu juro que é simples, apesar de assustador. Para o nosso caso, ##f(z)=1##, ##g(z)=1-2z##, ##g'(z)=-2##, ##\beta=2##, ##f(1/2)=1##, ##g'(1/2)=-2## e ##\nu=1##. Substituindo os valores, temos:<br />
$$[z^n]B(z)=1\times\frac{(-2)^1\times 1}{-2}\times(2)^n n^{1-1} =2^n $$<br />
Portanto, existem ##2^n## strings binárias de tamanho ##n##, o que bate com nosso resultado de ##8## para ##n=3##.
<br />
<br />
<h3 style="text-align: left;">
Outro exemplo</h3>
<br />
E se eu quiser calcular quantas strings binárias existem, mas só as que não possuem dois zeros seguidos? Esse problema é complicado com combinatória tradicional, mas com combinatória analítica fica simples!<br />
<br />
Primeiro, vamos caracterizar essas strings. Podemos descrevê-las como uma sequência de ##1## ou ##01##, seguida de ##0## ou vazio:
<br />
$$C=SEQ(Z_1+Z_0Z_1)\times(Z_0 + \varepsilon)$$
<br />
Traduzindo para função geradora:<br />
$$C=SEQ(z+z^2)\times(z+1)=\frac{z+1}{1-z-z^2}$$<br />
Agora aplicamos o teorema de transferência. ##f(z)=z+1## e ##g(z)=1-z-z^2##. As raízes de ##g(z)## são ##0.618## e ##-1.618##, a de menor módulo é ##0.618## com multiplicidade ##1##. Então:<br />
$$\begin{align*}
\beta &= 1/0.618 = 1.618 \\
f(1/\beta)&=f(0.618)=1.618 \\
g'(z) &=-2z-1 \\
g'(1/\beta) &=g'(0.618)=-2.236 \\
C[n]&\sim \frac{-1.618\times 1.618}{-2.236}\times 1.618^n \\
C[n] &\sim 1.171\times 1.618^n
\end{align*}$$<br />
Prontinho, transformamos <em>raciocínio</em>, que é díficil, em <em>álgebra</em>, que qualquer chimpanzé bêbado consegue fazer!
<br />
<br />
<h3 style="text-align: left;">
A pergunta original</h3>
<br />
Podemos agora voltar à pergunta original: se você jogar Cara ou Coroa várias vezes em sequência, qual a chance de nunca aparecer duas caras seguidas?<br />
<br />
Ora, essa probabilidade é exatamente igual ao número de strings binárias que não possuem dois zeros seguidos, dividida pelo número total de strings binárias. Esses são os dois casos que analisamos, logo:<br />
$$p\sim\frac{1.171\times1.618^n}{2^n} = 1.171\times 0.809^n$$<br />
Easy! E funciona mesmo? Eu fiz uma simulação computacional para comparar com o nosso assintótico, eis o resultado:<br />
<br />
Para ##n=5##, simulação ##40.78\%##, assintótico ##40.57\%##.<br />
<br />
Para ##n=10##, simulação ##14.17\%##, assintótico ##14.06\%##.<br />
<br />
Para ##n=15##, simulação ##4.84\%##, assintótico ##4.87\%##.<br />
<br />
<a href="https://github.com/ricbit/Oldies/blob/master/2016-07-totorial/nozero.py">Script com a simulação no github</a><br />
<br />
Ou seja, funcionou super bem, e a precisão vai ficando melhor conforme o ##n## aumenta.
<br />
<br />
<h3 style="text-align: left;">
Para saber mais</h3>
<br />
Se você gostou do que viu, a referência definitiva sobre o tema é o livro <strong>Analytic Combinatorics</strong>, do Flajolet, que foi o inventor da técnica. Não é o livro mais didático do mundo, mas está <a href="http://algo.inria.fr/flajolet/Publications/book.pdf">inteiramente disponível na web</a>. No <a href="https://www.coursera.org/">Coursera</a>, de tempos em tempos, o Sedgewick faz um curso online, que eu super recomendo, foi onde eu aprendi inclusive. E na wikipedia... o tópico é tão novo que não tem na wikipedia em português ainda! Fica como exercício para o leitor criar uma página na wikipedia sobre o tema :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-39227717976351157422016-07-25T01:21:00.000-03:002016-07-25T11:14:49.923-03:00A Busca Por Bozo Binário<script src='//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML' type='text/javascript'>
MathJax.Hub.Config({
tex2jax: { inlineMath: [ [String.fromCharCode(35)+String.fromCharCode(35),String.fromCharCode(35)+String.fromCharCode(35)]],
processEscapes: true }
});</script>
Se você trabalhar por tempo o suficiente com Computação, eventualmente uma das suas atribuições vai ser <b>entrevistar candidatos</b> para a sua equipe. Entrevistar é uma arte: existem inúmeros truques e macetes para
descobrir se é esse sujeito na sua frente é mesmo o cara que você quer como seu parceiro. O Joel tem <a href="http://www.joelonsoftware.com/articles/GuerrillaInterviewing3.html">uma boa introdução sobre o tema</a> que vale a pena ler.
<p>
Uma das habilidades que você precisa desenvolver rápido é aprender que <b>o candidato não pensa como você</b>. Em especial, quando ele responde uma pergunta de um jeito diferente daquele que você esperava, não quer dizer necessariamente que ele esteja errado. E esse caso eu já vi de perto, com o algoritmo que eu apelidei de <i>Busca Por Bozo Binário</i>.
<p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBCqbty7EvQA2L1p7s3UEiW6FF9wQ4Flkh_uWzO6orKU0D7RGFLa35tKtM9ue_BfDCA7e6SetC8ClqWLHFW16xx24MUhCnC-HIQVeeSwRw7JeHW4Pl_eBCV4o6TqGMOh3erUVAsHju-34/s1600/bozo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBCqbty7EvQA2L1p7s3UEiW6FF9wQ4Flkh_uWzO6orKU0D7RGFLa35tKtM9ue_BfDCA7e6SetC8ClqWLHFW16xx24MUhCnC-HIQVeeSwRw7JeHW4Pl_eBCV4o6TqGMOh3erUVAsHju-34/s320/bozo.jpg" width="320" height="320" /></a></div>
<p>
Essa história aconteceu faz algum tempo. Eu estava na cantina conversando sobre entrevistas, quando alguém comentou que rejeitou um candidato porque ele não conseguiu implementar uma <b>busca binária</b>. Poxa, isso é grave mesmo! E qual o algoritmo errado que ele fez? A resposta foi essa:
<p>
"Sorteie um número de 1 até o tamanho do vetor, e verifique o elemento que tinha esse índice. Se for igual você achou, então retorna. Se for menor, repete para o lado esquerdo; se for maior, repete para o lado direito."
<p>
Isso é errado, me disseram, porque o pior caso é ##O(n)##. De fato, se o elemento que você quer achar é o primeiro, e o gerador de aleatórios retornar sempre o último, ele vai passar pelo vetor inteiro.
<P>
Mas nesse ponto eu tive que interromper: "Peraí! Você explicitou que queria ##O(\log n)## no pior caso? Se você não falou nada no enunciado, ele pode ter entendido que era ##O(\log n)## no <b>caso médio</b>, e aí o algoritmo dele está certo!"
<p>
Na hora ninguém botou fé que esse algoritmo de Busca por Bozo Binário era de fato ##O(\log n)##, então eu tive que voltar para casa e escrever a demonstração. Se você tem medo de Matemática Discreta, pule a caixa azul:
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Para a Busca por Bozo Binário, nós queremos calcular qual é o valor médio do número de comparações que ele realiza. E como você começa uma análise de caso médio?
<P>
Primeiro você define as características da entrada: eu vou assumir que todos os casos estão distribuídos uniformemente. Depois, é só usar a fórmula do valor esperado: você soma o número de comparações em cada caso, multiplicado pela probabilidade daquele caso ocorrer.
$$ F[x] = \sum_i p[i]C[i] $$
Antes de começar, vejamos os casos extremos. Quando o vetor é vazio, não precisa de nenhuma comparação, então ##F[0]=0##. Se o vetor tiver tamanho um, então só precisa de uma comparação, ##F[1]=1##. É sempre bom ter uns casos pequenos para conferir no final.
<p>
Vamos ver agora o caso de um vetor de tamanho ##n##. Eu não sei qual número vai ser escolhido para cortar o vetor em dois, é o gerador de números aleatórios que escolhe. Então eu vou tirar a média sobre todas as escolhas possíveis. Como os ##n## valores são equiprováveis, então a probabilidade individual é ##1/n##:
$$F[n] = \sum_{0\le k\lt n}\frac{1}{n}F[k,n] = \frac{1}{n}\sum_{0\le k\lt n}F[k,n] $$
Na fórmula acima, ##F[n]## é o número médio de comparações para um vetor de tamanho ##n##, e ##F[k,n]## é o número médio de comparações para o vetor de tamanho ##n##, no caso em que o corte foi feito no elemento ##k##.
<p>
Vamos calcular ##F[k,n]## agora. Esse valor depende de onde está o número que estamos procurando. Eu não sei onde está o número; como estamos calculando o caso médio, precisamos tirar a média sobre todos as posições possíveis.
$$F[k,n]=\sum_{0\le i\lt n}\frac{1}{n}F[i,k,n]=\frac{1}{n}\sum_{0\le i\lt n}F[i,k,n]$$
Agora ##F[i,k,n]## significa o valor médio para um vetor de tamanho ##n##, cortado na posição ##k##, e com número final na posição ##i##.
<p>
Nós temos três casos agora. Se ##i=k##, então você só precisa comparar uma vez, a comparação vai ser verdadeira e o algoritmo termina. Se ##i\lt k##, então você compara uma vez e faz recursão para o lado esquerdo. Se ##i\gt k##, então você compara uma vez e faz recursão para o lado direito. Resumindo:
$$F[i,k,n]=\begin{cases}
1+F[k] &(i \lt k) \\
1 &(i = k) \\
1+F[n-k-1] &(i \gt k) \\
\end{cases}$$
Podemos sintetizar tudo que vimos em uma única recorrência.
$$\begin{align*}
F[0] &= 0 \\
F[n] &= \frac{1}{n} \sum_{0\le k \lt n}\left(
\frac{1}{n}\sum_{0\le i\lt k}\left(1+ F[k]\right) +
\frac{1}{n} +
\frac{1}{n}\sum_{k\lt i\lt n}\left(1+ F[n-k-1]\right)
\right)
\end{align*}$$
Agora é só resolver!
<p>
Antes de mais nada, vamos coletar quem é constante em relação ao índice da somatória. Todos os ##1/n## coletam, e as somatórias mais internas viram constantes.
$$\begin{align*}
F[n] &= \frac{1}{n^2} \sum_{0\le k \lt n}\left(
\sum_{0\le i\lt k}\left(1+ F[k]\right) +
1 +
\sum_{k\lt i\lt n}\left(1+ F[n-k-1]\right)
\right) \\
&= \frac{1}{n^2} \sum_{0\le k \lt n}
k \left(1+ F[k]\right) +
1 +
\left(n-k-1\right)\left(1+ F[n-k-1]\right)
\\
&= \frac{1}{n^2} \sum_{0\le k \lt n}
k + k F[k] +
1 +
n-k-1 +\left(n-k-1\right)F[n-k-1]
\\
&= \frac{1}{n^2} \sum_{0\le k \lt n}
n + k F[k]
+\left(n-k-1\right)F[n-k-1]
\\
&= 1+\frac{1}{n^2} \left(\sum_{0\le k \lt n} k F[k]
+\sum_{0\le k \lt n} \left(n-k-1\right)F[n-k-1] \right)
\\
\end{align*}$$
Vamos focar naquela segunda somatória. Primeiro fazemos ##q=n-k-1##:
$$
\sum_{0\le k \lt n} \left(n-k-1\right)F[n-k-1]
=\sum_{0\le n-q-1 \lt n} qF[q]
$$
Agora é só manipular os índices:
$$\begin{align*}
0\le n-&q-1 \lt n \\
1-n\le -&q \lt 1 \\
-1\lt &q \le n-1 \\
0\le &q \lt n \\
\end{align*}$$
Olha só, a segunda somatória é igual à primeira!
$$
\sum_{0\le n-q-1 \lt n} qF[q]
=\sum_{0\le q \lt n} qF[q]
=\sum_{0\le k \lt n} kF[k]
$$
Substituindo, chegamos em uma fórmula curta para ##F[n]##:
$$\begin{align*}
F[n]
&= 1+\frac{1}{n^2} \left(\sum_{0\le k \lt n} k F[k]
+\sum_{0\le k \lt n} \left(n-k-1\right)F[n-k-1] \right) \\
&= 1+\frac{1}{n^2} \left(\sum_{0\le k \lt n} k F[k]
+\sum_{0\le k \lt n} kF[k] \right) \\
&= 1+\frac{2}{n^2} \sum_{0\le k \lt n} k F[k] \\
\end{align*}$$
A somatória ainda está atrapalhando, o ideal seria sumir com ela. Uma maneira de fazer isso é isolando:
$$\begin{align*}
F[n] &= 1+\frac{2}{n^2} \sum_{0\le k \lt n} k F[k] \\
\sum_{0\le k \lt n} k F[k] &= \frac{n^2}{2}\left(F[n]-1\right)
\end{align*}$$
Essa última fórmula vale para todo ##n##, em especial vale também para ##n-1##:
$$\begin{align*}
\sum_{0\le k \lt n} k F[k] &= \frac{n^2}{2}\left(F[n]-1\right) \\
\sum_{0\le k \lt n-1} k F[k] &= \frac{(n-1)^2}{2}\left(F[n-1]-1\right) \\
\end{align*}$$
Ahá! Agora é só subtrair uma da outra que a somatória desaparece!
$$\begin{align*}
\sum_{0\le k \lt n} k F[k] - \sum_{0\le k \lt n-1} k F[k]
&= \frac{n^2}{2}\left(F[n]-1\right) - \frac{(n-1)^2}{2}\left(F[n-1]-1\right)\\
(n-1) F[n-1]
&= \frac{n^2}{2}\left(F[n]-1\right) - \frac{(n-1)^2}{2}\left(F[n-1]-1\right)\\
(n-1) F[n-1]
&= \frac{n^2}{2}F[n]-\frac{n^2}{2} - \frac{(n-1)^2}{2}F[n-1]+\frac{(n-1)^2}{2}\\
\frac{n^2}{2}F[n]
&= \left(\frac{(n-1)^2}{2}+(n-1)\right)F[n-1]+\left(\frac{n^2}{2} -\frac{(n-1)^2}{2}\right)\\
n^2F[n]
&= \left(n^2-1\right)F[n-1]+(2n-1)\\
\end{align*}$$
Chegamos finalmente em uma recorrência sem somatórias. Melhor ainda, essa recorrência está no ponto certo para usar a técnica do <i>summation factor</i>! Como esse é um truque muito útil de se conhecer, eu vou fazer em câmera lenta. Você pode usar um summation factor sempre que sua recorrência for da forma abaixo:
$$a_n F[n] = b_n F[n-1]+c_n$$
Esse é o nosso caso, se usarmos a correspondência abaixo:
$$\begin{align*}
a_n &= n^2 \\
b_n &= n^2-1 \\
c_n &= 2n-1
\end{align*}$$
O segredo do summation faction é tentar achar uma sequência mágica ##s_n## que tenha a seguinte propriedade:
$$s_n b_n=s_{n-1}a_{n-1}$$
E por que isso é bom? Olha só o que acontece quando você multiplica a recorrência original por ##s_n##:
$$\begin{align*}
a_n F[n] &= b_n F[n-1]+c_n \\
s_n a_n F[n] &= s_n b_n F[n-1]+s_n c_n \\
s_n a_n F[n] &= s_{n-1}a_{n-1}F[n-1]+s_n c_n \\
\end{align*}$$
Agora, se você substituir ##T[n]=s_n a_n F[n]##, temos:
$$\begin{align*}
s_n a_n F[n] &= s_{n-1}a_{n-1}F[n-1]+s_n c_n \\
T[n] &= T[n-1]+s_n c_n
\end{align*}$$
Opa, essa é fácil! Dá pra resolver de cabeça, é praticamente a definição de somatória!
$$\begin{align*}
T[n] &= T[n-1]+s_n c_n\\
T[n] &= T[0] + \sum_{1\le k \le n} s_k c_k \\
s_n a_n F[n] &= s_0 a_0 F[0] + \sum_{1\le k \le n} s_k c_k \\
F[n] &= \frac{1}{s_n a_n} \left(s_0 a_0 F[0] + \sum_{1\le k \le n} s_k c_k \right) \\
\end{align*}$$
Pronto, agora não é mais uma recorrência, é uma fórmula simples. A dificuldade do método é encontrar o tal do ##s_n##. Para achá-lo você pode usar a intuição, chutar, fazer um sacrifício aos deuses; ou então você pode usar o método do porradão. Nesse método você começa com ##s_n=s_{n-1}\left(a_{n-1}/b_n\right)## e abre recursivamente, chegando no monstrinho abaixo:
$$s_n = \frac{a_{n-1}a_{n-2}\ldots a_2 a_1}{b_n b_{n-1}\ldots b_3 b_2}$$
Essa fórmula parece grande, e é grande mesmo. Mas na maioria das vezes tem um monte de cancelamentos, o que deixa o resultado final pequeno. Vamos ver no nosso caso como fica:
$$\begin{align*}
s_n &= \frac{a_{n-1}a_{n-2}\ldots a_2 a_1}{b_n b_{n-1}\ldots b_3 b_2} \\
&= \frac{(n-1)^2 (n-2)^2 \ldots 2^2 1^2}
{\left(n^2-1\right) \left(\left(n-1\right)^2-1\right) \ldots (3^2-1) (2^2-1)} \\
&= \frac{(n-1)^2 (n-2)^2 \ldots 2^2 1^2}
{\left((n+1)(n-1)\right) \left((n-1+1)(n-1-1)\right) \ldots (3+1)(3-1) (2+1)(2-1)} \\
&= \frac{(n-1) (n-2)(n-3) \ldots3\cdot 2\cdot 1}
{(n+1) (n) (n-1)\ldots (4+1) (3+1) (2+1)} \\
&= \frac{2}
{(n+1) (n) } \\
\end{align*} $$
Cancelou mesmo! Bastou lembrar que ##(a^2-b^2)=(a+b)(a-b)## que o resto saiu. Agora é só lembrar que ##F[0]=0## e substituir na fórmula:
$$\begin{align*}
F[n] &= \frac{1}{s_n a_n} \left(s_0 a_0 F[0] + \sum_{1\le k \le n} s_k c_k \right) \\
F[n] &= \frac{n(n+1)}{2n^2} \sum_{1\le k \le n} \frac{2(2k-1)}{k(k+1)}\\
\end{align*}$$
Essa é a solução da recorrência. Dá para achar uma forma fechada? Nesse caso dá, desde que você tope uma forma fechada em função dos números harmônicos ##H[n]##, que são definidos como:
$$H[n]=\sum_{1\le k \le n}\frac{1}{k}$$
Você começa abrindo a fração no somatório:
$$\begin{align*}
\frac{2(2k-1)}{k(k+1)} &= \frac{A}{k} + \frac{B}{k+1}\\
\frac{4k-2}{k(k+1)} &= \frac{A(k+1)+B k}{k(k+1)}\\
4k-2 &= Ak+A +B k \\
4k-2 &= (A+B)k+A \\
A &= -2 \\
B &= 6
\end{align*}$$
Agora você divide o somatório em dois:
$$\begin{align*}
F[n] &= \frac{n(n+1)}{2n^2} \sum_{1\le k \le n} \frac{2(2k-1)}{k(k+1)}\\
F[n] &= \frac{n(n+1)}{2n^2} \sum_{1\le k \le n} \frac{6}{k+1} - \frac{2}{k}\\
F[n] &= \frac{n(n+1)}{2n^2} \left( \sum_{1\le k \le n} \frac{6}{k+1} - \sum_{1\le k \le n} \frac{2}{k} \right) \\
F[n] &= \frac{n(n+1)}{2n^2} \left( \sum_{1\le k \le n} \frac{6}{k+1} - 2\sum_{1\le k \le n} \frac{1}{k} \right) \\
F[n] &= \frac{n(n+1)}{2n^2} \left( \sum_{1\le k \le n} \frac{6}{k+1} - 2H[n] \right)\\
\end{align*}$$
O primeiro somatório precisa de um pouco de massagem:
$$\begin{align*}
\sum_{1\le k \le n} \frac{6}{k+1} &= \sum_{1\le q-1 \le n} \frac{6}{q} \\
&= \sum_{2\le q \le n+1} \frac{6}{q} \\
&= -6+\frac{6}{n+1}+6\sum_{1\le q \le n} \frac{1}{q} \\
&= -6+\frac{6}{n+1}+6H[n] \\
\end{align*}$$
Por fim:
$$\begin{align*}
F[n] &= \frac{n(n+1)}{2n^2} \left( \sum_{1\le k \le n} \frac{6}{k+1} - 2H[n] \right)\\
F[n] &= \frac{n(n+1)}{2n^2} \left( -6+\frac{6}{n+1}+6H[n] - 2H[n] \right)\\
F[n] &= -3 +\frac{2(n+1)}{n}H[n]\\
\end{align*}$$
Ufa, deu trabalho! E isso é ##O(\log n)## mesmo? Bem, podemos verificar usando um assintótico para o harmônico, por exemplo:
$$ H[n] = \ln n + \gamma + O(1/n) $$
Agora você substitui:
$$\begin{align*}
F[n] &= -3 +\frac{2(n+1)}{n}H[n]\\
&= -3 +2H[n] +\frac{H[n]}{n} \\
&= -3 +2\ln n +2\gamma +O(1/n)+ 2\frac{\ln n}{n}+2\frac{\gamma}{n}+O(1/n^2) \\
&= 2\ln n + O\left(\frac{\ln n}{n}\right) \\
&= O(\log n)
\end{align*}$$
Como queríamos demonstrar!
</div>
<p>
Ou seja, realmente a Busca por Bozo Binário é ##O(\log n)##. Em demonstrações grandes assim eu sempre gosto de fazer uma simulação por Monte Carlo para conferir as contas. A minha simulação está no github:
<p>
<a href="https://github.com/ricbit/Oldies/blob/master/2016-07-bozosearch/bozo.py">Busca por Bozo Binário, simulação por Monte Carlo</a>
<p>
Simulando 50000 vezes em um vetor de tamanho 3000, o número médio de comparações foi de ##14.1769##. O valor teórico previsto pela fórmula é de ##14.1732##, nada mau!
<P>
Eu também fiz uma simulação da busca binária tradicional como comparação. Com os mesmos parâmetros, a busca tradicional usou ##10.6356## comparações no caso médio, ou seja, foi mais eficiente que o Bozo Binário.
<p>
Por que isso acontece? Analisando o assintótico fica claro. Para ##n## grande, o valor médio da Busca por Bozo é de ##2 \ln n## (logaritmo natural), enquanto que na busca binária é ##\log_2 n## (logaritmo de base 2). Então, em média, esperamos que Bozo Binário seja mais lento por um fator de:
<p>
$$\begin{align*}
\frac{2\ln n}{\log_2 n} &= {2\frac{\log n}{\log e}} \div {\frac{\log n}{\log 2}} \\
&= 2\frac{\log n}{\log e} \times \frac{\log 2}{\log n} \\
&= 2\frac{\log 2}{\log e} \\
&= 2\ln 2 \\
&\simeq 1.386
\end{align*}$$
Ou seja, mais ou menos uns 38% mais lento, o que bate com a simulação.
<p>
Depois da análise do algoritmo, a dúvida que resta é: <b>e o candidato? </b> Bem, se fosse eu a entrevistar, precisaria coletar mais dados para saber se ele realmente sabia do tempo médio, ou se estava perdido e implementou a primeira coisa que veio na cabeça.
<p>
Um possível follow-up é "<i>Certo, esse método é mais complicado que a busca binária tradicional, mas funciona. Você pode me falar alguma situação onde ele é preferível sobre o tradicional?</i>". Curiosamente, existe sim um caso onde a Busca por Bozo Binário é melhor que a busca binária tradicional! Mas esse caso fica para a próxima :)<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-25835581707178517032015-07-24T15:51:00.003-03:002015-07-24T16:11:58.063-03:00Retrocomputação de Verdade #1<div dir="ltr" style="text-align: left;" trbidi="on">
Quando eu falo que gosto de retrocomputação, a maioria das pessoas assume que eu estou falando de TK90X e MSX. Mas para mim esses aí não são <i>retrocomputação</i>, são só <i>computação</i>. Eu usei essas máquinas quando elas eram o que havia de melhor (na minha faixa de preço). Antes delas, porém, teve uma época mágica onde computadores eram grandes, barulhentos e ocupavam salas inteiras!<br />
<br />
Sempre que eu estou viajando por aí, aproveito a oportunidade para visitar museus de ciência e conhecer esses grandes retrocomputadores do passado. Mas só ver de perto eu não acho tão legal, o que completa a experiência é entender como eles funcionam! Minha idéia original era falar sobre todos os retrocomputadores que já conheci, mas, como ficaria muito grande, vou começar só com os retrocomputadores mecânicos:
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<style scoped="" type="text/css">
@keyframes rotategear1 {
0% {transform: rotate(0deg);-moz-transform: rotate(0deg);}
50% {transform: rotate(180deg);-moz-transform: rotate(180deg);}
100% {transform: rotate(360deg);-moz-transform: rotate(360deg);}
}
@keyframes rotategear2 {
0% {transform: rotate(12.5deg);-moz-transform: rotate(12.5deg);}
50% {transform: rotate(192.5deg);-moz-transform: rotate(192.5deg);}
100% {transform: rotate(372.5deg);-moz-transform: rotate(372.5deg);}
}
.rotatecommon {
animation-duration: 4s;
animation-iteration-count: infinite;
animation-timing-function: linear;
background: 0 !important;
box-shadow: none !important;
border: 0 !important;
}
.myimg1 {
animation-name: rotategear1;
}
.myimg2 {
animation-name: rotategear2;
animation-direction: reverse;
position: relative;
left: -30px;
}
</style>
<img border="0" class="rotatecommon myimg1" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-35H_lpDWiCKxa6LfJh5PMix167M6dwAJ410RYXWs1vPD50bfXGQoC6Iq5JyMCR050IfvWUsj_3PesJINiOCtaCOISkHSMaJ0WBQp3eBK4HkMCeuraFtclkjGCrFRsJ-j8I7PRPGbOmI/s200/engrenagem_ricbit.png" width="200" />
<img border="0" class="rotatecommon myimg2" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkKTstvoffCL-IDOxxP8RGl8GfbAwm_ERtpmKPnSz0WSEwYhs7OOgcg-RLtpxfWYmvkFWILN9uehoZz34xBC-iTQEm2X01O-bQ7hB3Kl3erlWdp3bkQpDYzq_ERAFtiatREpFGXmrp8-E/s200/engrenagem_ilafox.png" width="200" /></div>
<h3 style="text-align: left;">
O Mecanismo de Antikhytera, 200-100 AC.</h3>
<i>Museu Arqueológico Nacional, Atenas, Grécia</i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0DxpLcimGMCWJ6cuvO4jkp6HNFFzVjl2llQY0BquB9VtFgVJdb21s-lFnxW16ViGsOuAMPhtjyZZLjdiwmSF8V8Bo1Vvnp3cVHGn43G1xw2G_rXQ_zYQpzn1gsGMepYq0SaDDAPf9sbY/s1600/DSC05550.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0DxpLcimGMCWJ6cuvO4jkp6HNFFzVjl2llQY0BquB9VtFgVJdb21s-lFnxW16ViGsOuAMPhtjyZZLjdiwmSF8V8Bo1Vvnp3cVHGn43G1xw2G_rXQ_zYQpzn1gsGMepYq0SaDDAPf9sbY/s320/DSC05550.JPG" width="320" /></a></div>
<br />
A maior prova da engenhosidade grega, o Mecanismo de Antikhytera é muitas vezes chamado de <b>o primeiro computador</b><i>.</i> Quer dizer, é um computador analógico não-reprogramável, hoje em dia estaria mais próximo do que a gente chama de calculadora.<br />
<br />
E para que ele serve? O Mecanismo tem a função de <b>calcular intervalos de tempo</b>: você gira uma manivela que aciona o ponteiro dos anos, e ele ajusta sozinho os outros ponteiros, que são muitos. Ele calcula o mês, a fase da Lua, a posição dos planetas, os eclipses mais próximos, a data da Olimpíada, a constelação do Zodíaco e assim por diante.<br />
<br />
Todos esses cálculos são baseados em <b>engrenagens dentadas</b>.<b> </b>Se você tem uma engrenagem com <b>x</b> dentes ligadas a outra com <b>y</b> dentes, a segunda vai rodar com velocidade angular igual a <b>x/y</b> da original. Então você só precisa saber a razão entre a duração de um mês e um ano para fazer um ponteiro que indica o mês.<br />
<br />
Mas os gregos eram realmente espertos, e sabiam que algumas relações você não consegue fazer só com razões. A posição da Lua, por exemplo. Eles sabiam que a Lua anda mais devagar em alguns trechos que em outros (por causa do que hoje conhecemos como <b>segunda Lei de Kepler</b>), e, para simular esse efeito, eles fazem uma das engrenagens não ter o centro fixo, assim ela tem velocidade angular variável com a posição.<br />
<br />
E como sabemos tanto sobre ela, dado que só temos pedaços de um exemplar que foi achado em um naufrágio? É que um dos pedaços era o manual!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPXO2DiPzr9tArxG-ka6M0m_vSjYJxAH9UvKLhunhX7QgVxI5HzuGlf61EXPCSK-ihEYLJWlSQcnbbqe422z7byij-TC_RP8dejKBlRbJNjgET-ecPuRxigNiUUUl8GfP414cSZXh53qM/s1600/DSC05554.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPXO2DiPzr9tArxG-ka6M0m_vSjYJxAH9UvKLhunhX7QgVxI5HzuGlf61EXPCSK-ihEYLJWlSQcnbbqe422z7byij-TC_RP8dejKBlRbJNjgET-ecPuRxigNiUUUl8GfP414cSZXh53qM/s320/DSC05554.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<i>Olhe as inscrições em grego</i></div>
<br />
Essa é a parte legal de visitar museus e ver essas coisas de perto, dá para prestar atenção nos detalhes!<br />
<br />
<h3 style="text-align: left;">
O Motor de Diferenças, 1822</h3>
<i>Museu da História da Computação, Mountain View, USA</i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQG0ebR_nqrdNeqpbcoVPwXLFh5DSJ2He0bEY-JutZkvMbERQlBJwrr9CnVt7LNzlvbkOVSVfpUgCKV1-eHqYHGKLr_cGCYrHizh6tpSFmADsTcRMxbGS9x_pVCO1u4PhiGJgVYPmkVrE/s1600/babbage.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQG0ebR_nqrdNeqpbcoVPwXLFh5DSJ2He0bEY-JutZkvMbERQlBJwrr9CnVt7LNzlvbkOVSVfpUgCKV1-eHqYHGKLr_cGCYrHizh6tpSFmADsTcRMxbGS9x_pVCO1u4PhiGJgVYPmkVrE/s320/babbage.jpg" width="320" /></a></div>
<br />
O século XIX foi a época do Maxwell, Faraday, Tesla, Edison. A engenharia elétrica estava bombando! Mas, para fazer os circuitos funcionarem, você precisa saber resolver equações diferenciais bem complicadas. Essas contas eram resolvidas com <b>tábuas de logaritmos</b> que simplificavam bastante o processo. Mas quem criava as tábuas de logaritmos?<br />
<br />
Em geral elas eram feitas por um batalhão de pessoas que calculavam logaritmos na mão, com seis ou sete dígitos significativos. É um trabalho <b>repetitivo e propenso a erros</b>. Hoje em dia nós sabemos que a melhor maneira de realizar cálculos repetitivos e propenso a erros é um computador, mas o primeiro a ter essa idéia foi o Babbage, em 1822. Nessa época ele conseguiu o financiamento para construir o Motor de Diferenças, que tem uma única finalidade: calcular polinômios. Assim ele teria como aproximar os logaritmos por séries de Taylor equivalentes.<br />
<br />
Depois de sugar dinheiro do governo por vinte anos sem entregar nada, o financiamento foi cortado. (O governo percebeu que com aquela dinheirama toda dava para contratar gente o suficiente para calcular os polinômios e conferir as contas). Mas foi uma pena, porque o projeto do Babbage era sólido. Se tivesse sido terminado, nunca mais alguém precisaria fazer esse tipo de conta.<br />
<br />
Mas será que o Babbage teria conseguido terminar, se não tivesse perdido o contrato? Em 1985, cientistas resolveram seguir o projeto original e montar um Motor de Diferenças de verdade. Eles descobriram que o projeto tinha pequenos erros, mas os erros eram tão bobos que certamente foram introduzidos de propósito, como forma de <b>proteção contra cópia</b> (a técnica de introduzir erros como proteção contra pirataria foi usada desde a Renascença, quem costumava fazer isso era o Leonardo da Vinci). Corrigindo os erros propositais, o Motor funciona! Atualmente tem duas réplicas em funcionamento, uma em Londres e outra na Califórnia.<br />
<br />
Por dentro, o Motor de Diferenças também funciona com engrenagens como a Mecanismo de Antikhytera, mas com uma diferença fundamental. O Mecanismo codificava a informação como um ângulo da engrenagem, se você quisesse mais precisão, precisava aumentar fisicamente o tamanho da engrenagem. O Babbage queria precisões de sete dígitos significativos, aí esse método é inviável. A solução foi abandonar a codificação analógica e usar <b>engrenagens digitais</b>. Cada engrenagem marca um dígito de 0 a 9, e com sete engrenagens em sequência você codifica os sete dígitos desejados.<br />
<br />
Duas engrenagens encaixadas implementam adição ou subtração (já que virando uma delas, a outra vira junto). Mas, se são engrenagens digitais, então falta um detalhe importante: o <b>vai-um</b>. Eu gravei o vídeo abaixo mostrando o carry propagando, garanto que é a coisa mais bonita que você vai ver hoje!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/sA5DbjjJMKs/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/sA5DbjjJMKs?feature=player_embedded" width="320"></iframe></div>
<div style="text-align: center;">
<i>Propagação mecânica de carry</i><br />
<i><br /></i></div>
<h3 style="text-align: left;">
A Bomba de Turing, 1940</h3>
<i>Bletchley Park, UK</i><br />
<i><br /></i>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_6bW0d8eLvarpyCI4GoZioU223oLNz4dT8STz1KYT-AuCzJDSUR5Cnnsb-hiFomp7RCJxsYeGforEzh7rdx-pv4N26ISjk9ss8D6wlvLGdgVW_HMYf44-n1BbgaT5_4lva7ivafTOuTk/s1600/DSC01731.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_6bW0d8eLvarpyCI4GoZioU223oLNz4dT8STz1KYT-AuCzJDSUR5Cnnsb-hiFomp7RCJxsYeGforEzh7rdx-pv4N26ISjk9ss8D6wlvLGdgVW_HMYf44-n1BbgaT5_4lva7ivafTOuTk/s320/DSC01731.JPG" width="320" /></a></div>
<br />
<br />
Sobre a história desse computador eu não preciso falar muito, já que ela foi transformada em um filme: <a href="http://www.imdb.com/title/tt2084970/">O Jogo da Imitação</a> (tem no Netflix). Eles precisaram transformar a história do Turing num romance heterossexual para ficar palatável para as massas, mas tá valendo, pelo menos agora as pessoas sabem quem foi o Turing.<br />
<br />
A máquina que ele criou era conhecida entre eles como a Bomba. Ela tinha uma única função: quebrar o <b>código de criptografia</b> da máquina alemã Enigma, então para entender a Bomba você precisa entender o Enigma antes. As primeiras versões do Enigma era assim:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTgyOeha1Z007BojjzBO9Xo4ejxa4bEf7jz0b5qu7k9nY8QNhiSShyphenhyphennoZ3UeqC3y3UnySPKX4xPq0C4emqE1bmOX7blyjRONu7HHqa4bcZ8paJOgjdvoLWwf1oWaW0GuZdVbsoVjoxQpg/s1600/DSC01647.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTgyOeha1Z007BojjzBO9Xo4ejxa4bEf7jz0b5qu7k9nY8QNhiSShyphenhyphennoZ3UeqC3y3UnySPKX4xPq0C4emqE1bmOX7blyjRONu7HHqa4bcZ8paJOgjdvoLWwf1oWaW0GuZdVbsoVjoxQpg/s320/DSC01647.JPG" width="240" /></a></div>
<div style="text-align: center;">
<i>O Enigma</i></div>
<br />
O coração do Enigma são os <b>rotores</b> (as engrenagens à esquerda, na foto). Ao longo do rotor está sequência alfabética misturada. Quando inserida no slot apropriado, o rotor faz uma <b>cifra de permutação</b>, obedecendo uma tabela como essa:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1_HPLd0ge-I9R2xaObw4rdmRPI6GCq3gxm6-XUtlPA4dTuVrZOGKkea6SkFzFNYLYlqOKUrxhdKDzLElJT9eTlWDW5tqs5gsC0cg42teoa8kicmz3pL2TqmYkxnVXltAyUwyca5rU2b4/s1600/wiringdiagram.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="45" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1_HPLd0ge-I9R2xaObw4rdmRPI6GCq3gxm6-XUtlPA4dTuVrZOGKkea6SkFzFNYLYlqOKUrxhdKDzLElJT9eTlWDW5tqs5gsC0cg42teoa8kicmz3pL2TqmYkxnVXltAyUwyca5rU2b4/s400/wiringdiagram.png" width="400" /></a></div>
<br />
Ou seja, a letra <b>A</b> no original vira <b>Z</b>, a letra <b>B</b> vira <b>P</b>, e assim por diante. Quebrar esse código é fácil, qualquer criança esperta consegue. Mas o Enigma é mais seguro que isso. Ao invés de um único rotor, ele tem três rotores em sequência, e os três são diferentes: a letra <b>A</b> vira <b>Z</b>, depois no rotor seguinte você pega esse <b>Z</b> e vira <b>F</b>, depois no último o <b>F</b> pode virar <b>Q</b>. E, para complicar mais, cada vez que você aperta uma tecla o rotor gira, ou seja, dá um offset de uma letra no primeiro rotor. Quando o primeiro rotor gira por completo, induz o segundo a girar uma posição, e assim por diante.<br />
<br />
Bastante complicado né? Mas isso não é seguro o suficiente em um contexto de <b>guerra mundial</b>. A senha desse sistema é a configuração inicial dos rotores. Você pode encaixar os rotores no slot em qualquer ordem (6 combinações), e iniciando em qualquer ângulo inicial (26^3 combinações). Isso dá um total de 105456 senhas possíveis. Ninguém consegue testar todas essas senhas em um dia. Mas, também, ninguém precisa testar sozinho! É só pegar 105456 soldados e mandar cada um testar uma senha diferente. Um deles vai acertar de primeira! (E 105 mil soldados não é muito, é só lembrar que no Maracanã cheio cabem 80 mil pessoas. 105 mil soldados é um Maracanã e meio só).<br />
<br />
Por isso os alemães colocaram na frente da máquina uns plugues adicionais, que servem para permutar letras na entrada. Se você liga um plugue da posição <b>A</b> para a <b>R</b>, então a letra <b>A</b> vira <b>R</b> e a letra <b>R</b> vira <b>A</b>. Eles usavam em média uns dez plugues, então o número de senhas sobe de 105 mil para 100 bilhões. Parece uma solução esperta, mas isso, na verdade, foi uma burrice dos alemães! Desse jeito, é garantido que a letra <b>A</b> nunca vai virar <b>A</b> de novo. Então eles aumentaram o número de chaves, mas <b>diminuíram o espaço de busca</b>.<br />
<br />
Agora já dá para entender a Bomba de Turing. A Bomba, na verdade, é um <b>emulador de Enigma</b> (o Turing sempre foi fã de emuladores, seu conceito de máquina universal é basicamente um emulador genérico). Ele tem todos os rotores iguais aos do Enigma, e um motor que gira os rotores em todas as posições possíveis, bem rapidamente. Atrás de cada rotor tem um contato elétrico: se você colocar uma palavra codificada de um lado, e uma palavra traduzida do outro, quando os rotores giraram na posição correta, dá o contato e ele avisa que achou a senha. E na prática ele não precisa girar todas as combinações possíveis. Se em alguma rotação tiver uma letra que foi mapeada nela mesma, você pode <b>cortar a árvore de busca</b>, porque essa senha com certeza está errada.<br />
<br />
Então tudo que resta para quebrar o Enigma é achar um par de palavras, uma codificada, e sua correspondente não-codificada. Isso é o que hoje em dia chamamos de <a href="https://en.wikipedia.org/wiki/Known-plaintext_attack">known-plaintext attack</a>. Parece difícil, mas existiam vários truques para conseguir pares assim. <br />
<br />
Um deles era baseado no <b>tamanho da mensagem máxima</b> dos alemães. Quando estourava o limite de tamanho, eles quebravam a mensagem em duas, e a segunda parte começava com "continuando, ...". Então, quando encontravam pacotes de várias mensagens longas, uma seguida da outra, certeza que uma delas começaria com "Fortsetzung".<br />
<br />
Outra maneira era colocando <b>minas explosivas</b> no meio do mar. Os aliados colocavam as minas de um jeito fácil de encontrar, e em um lugar conhecido. Aí era só esperar os alemães acharem as minas. Quando eles achavam, transmitiam as coordenadas pelo rádio, então era só colocar as coordenadas da mina no computador, que eventualmente ele iria achar uma mensagem correspondente.<br />
<br />
Quem viu o filme do Turing deve lembrar que no final eles jogam tudo numa fogueira. Isso aconteceu mesmo, quase tudo sobre o projeto foi destruído, e o que não foi era classificado como ultra-secreto. Só na década de 70 é que o mundo descobriu a existência das Bombas de Turing, e hoje em dia temos informação suficiente para recriar uma delas, como essa que está exposição no Bletchley Park.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ2ljVg41pYKR8YLtmK4V_q0LN1V9NFVoakihQ7A1o3QAOG2knt_I2S0JhYglLsyyaCqzAdGxkPa3E1PkE2Gqbm7N5fqqF7sCPgyTSK5y3WqLEmRXhWAVwdW8ZAolhEEqrcwbamenQkWQ/s1600/DSC01729.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ2ljVg41pYKR8YLtmK4V_q0LN1V9NFVoakihQ7A1o3QAOG2knt_I2S0JhYglLsyyaCqzAdGxkPa3E1PkE2Gqbm7N5fqqF7sCPgyTSK5y3WqLEmRXhWAVwdW8ZAolhEEqrcwbamenQkWQ/s320/DSC01729.JPG" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<i>A Bomba de Turing por dentro</i></div>
<br />
Ainda tem mais computadores que eu conheci de perto, mas os outros ficam para a parte 2 :)<br />
<br /></div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-4702946200323412302015-05-19T01:02:00.000-03:002019-09-03T01:04:23.602-03:00Livros de Colorir são Equivalentes a Sudoku<div dir="ltr" style="text-align: left;" trbidi="on">
De tempos em tempos a mídia impressa inventa uma modinha para vender mais papel. A modinha da vez são os <strong>livros de colorir</strong>. Eles são uma sensação! Os livros não param nas lojas, existem grupos e grupos falando disso na internet, rola até competição para ver quem pinta melhor!<br />
<br />
Mas, se você tem memória boa, deve lembrar que mesma coisa aconteceu dez anos atrás. Ao invés de livros de colorir, a modinha era <strong>Sudoku</strong>. Era uma sensação! Haviam livros e livros nas lojas, todo mundo falava disso na internet, rolava até competição para ver quem resolvia mais rápido!<br />
<br />
À primeira vista, os dois passatempos são bem distintos: o livro de colorir precisa de sensibilidade artística, o sudoku de raciocínio lógico. Mas hoje eu vou provar que, na verdade, os dois são <strong>equivalentes</strong>! E tudo começa com a regra não escrita dos livros de colorir.<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/colorirerrado.png">
</a> <a href="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_pintando.jpg"><img alt="ricbit_ilafox_pintando" class="aligncenter size-medium wp-image-257" height="352" src="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_pintando.jpg" width="550" /></a>
</div>
<h2>
<br /></h2>
<h3 style="text-align: left;">
A Regra dos Livros de Colorir</h3>
<br />
Livros de colorir tem uma <strong>regra implícita</strong>. Todo mundo concorda que isso aqui é uma pintura válida, certo?<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/colorircerto-1.png"><img alt="colorircerto (1)" class="aligncenter size-full wp-image-288" height="200" src="https://www.ilafox.com.br/ricbit/images/colorircerto-1.png" width="195" /></a>
</div>
<br />
Mas isso aqui é trapacear:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/colorirerrado.png"><img alt="colorirerrado" class="aligncenter size-full wp-image-256" height="200" src="https://www.ilafox.com.br/ricbit/images/colorirerrado.png" width="195" /></a> </div>
<br />
A regra implícita dos livros de colorir é que <strong>pintar duas regiões adjacentes com a mesma cor não vale</strong>. Afinal, tem uma linha preta ali, e ela está ali por uma razão, que é separar regiões de cores diferentes. Se você ignorar a linha preta, não está aproveitando todos os detalhes da figura.<br />
<br />
Se você for a uma papelaria atualmente, verá que todas as caixas de lápis de cor com 48 cores estão esgotadas. O motivo é que os livros de colorir tem muitas regiões adjacentes, então quanto mais cores, mais fácil de pintar.<br />
<br />
E qual é o mínimo de cores que você precisa? Um estojo de 12 cores é suficiente? Incrivelmente, doze cores dá e sobra. Na verdade, <strong>com apenas 4 cores você consegue pintar qualquer desenho</strong>, sem deixar regiões adjacentes com a mesma cor! Essa proposição é o lendário <a href="http://en.wikipedia.org/wiki/Four_color_theorem">Teorema das Quatro Cores</a>, que ficou famoso por ter sido o primeiro grande teorema da matemática provado por computador.<br />
<br />
Além disso, o problema de pintar um desenho com a menor quantidade de cores possíveis não é apenas teórico. Ele tem várias utilidades práticas! Por exemplo, compiladores usam pintura para fazer <a href="http://en.wikipedia.org/wiki/Register_allocation">alocação de registros</a>, e companhias aéreas usam pintura para fazer <a href="http://onlinelibrary.wiley.com/doi/10.1002/atr.5670260204/abstract">alocação de aviões em rotas aéreas</a>.<br />
<br />
O problema da pintura mínima também serve para resolver Sudokus. Mas antes de mostrar como isso pode ser feito, vamos resolver um problema mais simples, o Bidoku.<br />
<br />
<h3 style="text-align: left;">
Bidoku</h3>
<br />
O Bidoku é a versão fácil do Sudoku. Você tem um grid 2x2, e precisa preenchê-lo com 0 e 1, de modo não tenha dois números iguais na mesma linha e na mesma coluna:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidoku.png"><img alt="bidoku" class="aligncenter size-full wp-image-259" height="84" src="https://www.ilafox.com.br/ricbit/images/bidoku.png" width="87" /></a> </div>
<br />
Esse puzzle é bem fácil, dá para resolver de cabeça né? Mas vamos ver como é o raciocínio usando pintura. Para isso, vamos dar um nome para cada uma das células do puzzle:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidoku-letter1.png"><img alt="bidoku-letter" class="aligncenter size-full wp-image-263" height="84" src="https://www.ilafox.com.br/ricbit/images/bidoku-letter1.png" width="87" /></a> </div>
<br />
Cada uma das letras pode ser 0 ou 1. Vamos enumerar as possiblidades: <strong>A</strong> pode ser 0 ou 1, <strong>B</strong> pode ser 0 ou 1, e assim por diante:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokugraph1.png"><img alt="bidokugraph1" class="aligncenter wp-image-261" height="194" src="https://www.ilafox.com.br/ricbit/images/bidokugraph1.png" width="100" /></a> </div>
<br />
Agora vamos ligar com uma linha todos os nós que não podem acontecer ao mesmo tempo. Começando do <b>B</b>, nós sabemos que ou o <b>B</b> é zero, ou o <b>B</b> é um. Então coloco uma linha entre <strong>B0</strong> e <strong>B1</strong>. Além disso, se <b>B</b> for 0, então nem <b>A</b> e nem <strong>C</strong> podem ser 0. Da mesma maneira, se <b>B</b> for 1, então nem <b>A</b> e nem <strong>C</strong> podem ser 1. Desenhando:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokugraph2.png"><img alt="bidokugraph2" class="aligncenter wp-image-262" height="194" src="https://www.ilafox.com.br/ricbit/images/bidokugraph2.png" width="100" /></a> </div>
<br />
Agora é só repetir o que fizemos com o <strong>B</strong> para todas as outras letras:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokugraph3.png"><img alt="bidokugraph3" class="aligncenter wp-image-264" height="199" src="https://www.ilafox.com.br/ricbit/images/bidokugraph3.png" width="148" /></a> </div>
<br />
Este é o <a href="http://en.wikipedia.org/wiki/Graph_(mathematics)">grafo</a> equivalente do Bidoku. Com o grafo em mãos, podemos achar a figura de colorir equivalente, basta achar um desenho onde as regiões adjacentes são exatamente aquelas indicadas pelas linhas do grafo. Qualquer desenho que satisfaça essa condição serve, pode ser esse aqui, por exemplo:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokudrawing2.png"><img alt="bidokudrawing" class="aligncenter size-full wp-image-268" height="200" src="https://www.ilafox.com.br/ricbit/images/bidokudrawing2.png" width="200" /></a> </div>
<br />
Com o desenho em mãos, podemos resolver o Bidoku através da coloração!. Começa assim: você pega todos os números que foram dados, e pinta de verde. No Bidoku exemplo acima, era dado que o <strong>A</strong> valia 1, então nós pintamos de verde o <strong>A1</strong>:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokugreen.png"><img alt="bidokugreen" class="aligncenter size-full wp-image-269" height="200" src="https://www.ilafox.com.br/ricbit/images/bidokugreen.png" width="200" /></a> </div>
<br />
Agora é só pintar o restante com o menor número de cores possível, respeitando a regra de que regiões adjacentes precisam ter cores distintas. Se você fizer a pintura, vai notar que é possível pintar com apenas duas cores, de uma única maneira:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokufull.png"><img alt="bidokufull" class="aligncenter size-full wp-image-270" height="200" src="https://www.ilafox.com.br/ricbit/images/bidokufull.png" width="200" /></a> </div>
<br />
Agora você pega todas as regiões verdes e retorna para o Bidoku original:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokusolved.png"><img alt="bidokusolved" class="aligncenter size-full wp-image-271" height="84" src="https://www.ilafox.com.br/ricbit/images/bidokusolved.png" width="87" /></a> </div>
<br />
Funciona mesmo! O Bidoku foi <strong>resolvido pela coloração mínima</strong>!<br />
<br />
Uma dúvida pertinente é o que acontece com a coloração se você tentar pintar um Bidoku insolúvel. Por exemplo, o Bidoku abaixo não tem solução (o <strong>B</strong> não pode ser 0 e nem 1):<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokuunsolvable.png"><img alt="bidokuunsolvable" class="aligncenter size-full wp-image-272" height="84" src="https://www.ilafox.com.br/ricbit/images/bidokuunsolvable.png" width="87" /></a> </div>
<br />
Se você tentar a coloração mínima, vai notar que são necessárias no mínimo três cores para respeitar as regras:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bidokuthree1.png"><img alt="bidokuthree" class="aligncenter size-medium wp-image-274" height="200" src="https://www.ilafox.com.br/ricbit/images/bidokuthree1.png" width="200" /></a> </div>
<br />
Essa regra vale sempre, se a sua coloração mínima tem mais de duas cores, então o Bidoku original era insolúvel.<br />
<br />
Agora que já dominamos o Bidoku, podemos passar para a solução do Sudoku.
<br />
<br />
<h3 style="text-align: left;">
A Coloração do Sudoku</h3>
<br />
O Sudoku usa o mesmo raciocínio do Bidoku, mas o problema é muito maior. No Bidoku a célula <strong>A</strong> tinha duas possibilidades, então nós criávamos dois nós: <strong>A0</strong> e <strong>A1</strong>. No Sudoku, ela tem nove possibilidades, então a célula <strong>A</strong> gera os nós <strong>A1</strong>, <strong>A2</strong>, <strong>A3</strong>, .., até <strong>A9</strong>. E você precisa adicionar linhas ligando os números, as linhas, as colunas, e as caixas (eu chamo de caixas aqueles blocos 3x3). O grafo final do Sudoku, levando em conta tudo isso, fica assim:<br />
<br />
<div style="text-align: center;">
<a href="http://www.ricbit.com/code/sudoku.svg"><img alt="sudokugraph" class="aligncenter wp-image-279 size-medium" height="614" src="https://www.ilafox.com.br/ricbit/images/sudokugraph-620x614.png" width="620" /></a></div>
<div style="text-align: center;">
<em>Clique na imagem para ver o SVG original</em></div>
<br />
São 729 nós e 11664 linhas, só <em>um pouquinho</em> maior que o Bidoku, que tinha 8 nós e 12 linhas. Além disso, no Bidoku nós conseguimos fazer um desenho equivalente ao grafo. No Sudoku isso não é possível, porque esse grafo não é <a href="http://en.wikipedia.org/wiki/Planar_graph">planar</a> (ou melhor, você <em>pode</em> fazer um desenho, mas vai ser um desenho multidimensional em uma geometria possivelmente não-euclideana). Isso não afeta nossa proposição, já você pode colorir diretamente os nós do grafo, ao invés de colorir as regiões de um desenho equivalente.<br />
<br />
E como fazemos isso? Da mesma maneira que o Bidoku, você parte de um Sudoku que você quer resolver, por exemplo esse aqui, que eu peguei no <a href="http://www.theguardian.com/lifeandstyle/2009/apr/27/sudoku-1235-easy">The Guardian</a>:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/SUD1235E_2704.jpg"><img alt="SUD1235E_2704" class="aligncenter wp-image-281" height="200" src="https://www.ilafox.com.br/ricbit/images/SUD1235E_2704.jpg" width="200" /></a> </div>
<br />
Nós vamos pintar de verde todas as células correspondentes no grafo do Sudoku (eu não vou mostrar as linhas dessa vez, assim fica mais visível):<br />
<br />
<div style="text-align: center;">
<a href="http://www.ricbit.com/code/sudoku1.svg"><img alt="sudoku1" class="aligncenter wp-image-282 size-medium" height="614" src="https://www.ilafox.com.br/ricbit/images/sudoku1-620x614.png" width="620" /></a></div>
<div style="text-align: center;">
<em>Clique na imagem para ver o SVG original</em></div>
<br />
Pois bem, a minha proposição é que se você fizer a pintura mínima nesse grafo, o resultado do Sudoku vai aparecer nas células que forem pintadas de verde. Nós vimos um exemplo de que isso funciona no Bidoku, mas <strong>um exemplo não é uma demonstração</strong>, pode ter sido coincidência. Eu vou provar abaixo que isso sempre é verdade. Se você tem medo de demonstrações, pule a caixa azul para ver o Sudoku pintado.
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Antes de mais nada, quantas cores precisamos para pintar esse grafo? O instinto é falar que precisamos de 4 cores, por causa do Teorema das Quatro Cores. Mas esse teorema só funciona se o grafo for planar! E o grafo do Sudoku claramente não é planar, já que ele admite o K<sub>5</sub> como subgrafo, e, portanto, viola o <a href="http://en.wikipedia.org/wiki/Kuratowski%27s_theorem">teorema de Kuratowski</a> para grafos planares.<br />
<br />
Vamos tentar achar qual o número de cores então. Pegue uma célula qualquer, por exemplo uma célula <strong>A</strong>. Nós temos 9 nós correspondentes, de <strong>A1</strong> até <strong>A9</strong>, e nenhum desses nós pode ter a mesma cor que o outro. Então o grafo da célula A é um <a href="http://en.wikipedia.org/wiki/Clique_%28graph_theory%29">clique</a> de tamanho 9. Como o número cromático de um grafo é sempre maior ou igual ao grau do maior clique, então sabemos que o grafo tem pelo menos 9 cores.<br />
<br />
Mas, se esse grafo foi construído a partir de um Sudoku que tem solução, então nós podemos construir uma coloração com 9 cores para ele. Você começa pintando todos os nós da solução com a cor 0. Depois, para cada tripla <strong>(i, j, digito)</strong> que foi pintada com a cor 0, você pinta a tripla <strong>(i, j, digito + 1)</strong> com a cor 1 (o incremento no dígito é mod 9). Essa pintura não viola o grafo, porque ele é simétrico nos dígitos. Repetindo o processo para as cores 2..9, você tem um grafo totalmente pintado e sem nenhuma violação.<br />
<br />
Se o número mínimo de cores é 9, e nós sempre conseguimos construir uma pintura com 9 cores, então o número cromático é 9. Done.<br />
<br />
Ainda falta um detalhe: ninguém garante que existe uma única pintura possível com 9 cores (e na prática vai ter mais de uma mesmo). Mas todas as pinturas possíveis resolvem o Sudoku! Como o número cromático é 9, então cada célula <strong>(i,j)</strong> vai ter um elemento pintado com cada uma das cores possíveis (já que a célula é um clique de tamanho 9). Como cada célula tem todas as cores, então cada célula vai ter um elemento com a cor 0; e se todas as células tem a cor 0, então toda pintura possível embute uma solução do Sudoku. QED.
</div>
<br />
Se você tiver paciência de fazer a pintura mínima naquele grafo gigante, o resultado será esse aqui:<br />
<br />
<div style="text-align: center;">
<a href="http://www.ricbit.com/code/sudoku3.svg"><img alt="sudoku3" class="aligncenter wp-image-283 size-medium" height="614" src="https://www.ilafox.com.br/ricbit/images/sudoku3-620x614.png" width="620" /></a></div>
<br />
<div style="text-align: center;">
<em>Clique na imagem para ver o SVG original</em></div>
<br />
Transcrevendo as células verdes para o diagrama original, temos a solução do Sudoku, como prometido!<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/solved.png"><img alt="solved" class="aligncenter wp-image-285" height="200" src="https://www.ilafox.com.br/ricbit/images/solved.png" width="200" /></a> </div>
<br />
Na prática, esse é provavelmente um dos piores métodos para resolver o Sudoku. Mesmo um algoritmo no estado da arte em graph coloring vai levar um bom tempo para achar uma solução através de pintura mínima. Mas pelo menos você pode dizer que <strong>Sudoku é equivalente a um livro de pintura</strong>, desde que você não ligue de fazer pinturas mínimas em livros multidimensionais de geometria possivelmente não-euclideana :)<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_pintando2.jpg"><img alt="ricbit_ilafox_pintando2" class="aligncenter size-full wp-image-286" height="320" src="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_pintando2.jpg" width="500" /></a>
</div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-6163275306313310752015-05-10T23:47:00.001-03:002018-01-19T11:36:38.555-03:00A Intuição do Knuth<div dir="ltr" style="text-align: left;" trbidi="on">
Às vezes eu me pergunto se as pessoas da minha área têm noção de quão sortudos nós somos. Os físicos adorariam viajar no tempo para conversar com o <a href="http://en.wikipedia.org/wiki/Isaac_Newton">Newton</a>, os matemáticos adorariam conversar com o <a href="http://en.wikipedia.org/wiki/Euclid">Euclides</a>, os biólogos adorariam conversar com o <a href="http://en.wikipedia.org/wiki/Charles_Darwin">Darwin</a>. Mas nós podemos conversar com o <a href="http://en.wikipedia.org/wiki/Donald_Knuth">Knuth</a>!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8jQJ6ERZ-sUS7Itr91yowHH4lxLz5cpsvrcDjCtTpK51oyiZLYMF5ZsRZ8aKCR8ExYyAu5YoIrCRQQIdRbZg3nuWAAk6IuyHdgWhX43U0owncf_6b-AKwbr5UPdIfGVJ1x-HfS7NUFoY/s1600/ricbit_knuth.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8jQJ6ERZ-sUS7Itr91yowHH4lxLz5cpsvrcDjCtTpK51oyiZLYMF5ZsRZ8aKCR8ExYyAu5YoIrCRQQIdRbZg3nuWAAk6IuyHdgWhX43U0owncf_6b-AKwbr5UPdIfGVJ1x-HfS7NUFoY/s320/ricbit_knuth.jpg" width="320" /></a></div>
<br />
Nós temos a sorte de viver no mesmo período de tempo que o criador da <a href="http://en.wikipedia.org/wiki/Analysis_of_algorithms">análise de algoritmos</a>, que é uma das bases da Ciência da Computação. Se você gosta do assunto, vale a pena juntar uns trocos e viajar até a Califórnia para assistir a uma das palestras dele (dica: todo fim de ano, inspirado nas árvores de Natal, ele faz uma <a href="http://www-cs-faculty.stanford.edu/~uno/musings.html">palestra de estrutura de dados, falando sobre árvores</a>; elas também estão online se você não tiver como ver ao vivo).<br />
<br />
Eu fiz a peregrinação em 2011, quando consegui assistir a uma das palestras dele. Aproveitei para ir todo contente pegar minha <a href="http://www-cs-faculty.stanford.edu/~uno/books.html">recompensa por ter achado um erro</a> no Art of Computer Programming, mas ele, marotamente, me disse que aquilo que eu achei não era um erro, era uma <b>pegadinha</b>, e eu <b>caí</b>! (Mas eu não vou falar qual a pegadinha, vá na página 492 do TAOCP volume 4A, primeira edição, e confira você mesmo :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7VbM1PG2dlDhnJUbR_Z4beXwrUZghZNqX0P4Jvatbsza-TI7aZ4rxAn4IERlwRu7dLsiw2-XUORwoSkJCY91OEONrGi12js6f7hJDxRQ_IBskz2eQqsmnYoxY2jHUbBPn8essfuYAt8U/s1600/eueoknuth.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7VbM1PG2dlDhnJUbR_Z4beXwrUZghZNqX0P4Jvatbsza-TI7aZ4rxAn4IERlwRu7dLsiw2-XUORwoSkJCY91OEONrGi12js6f7hJDxRQ_IBskz2eQqsmnYoxY2jHUbBPn8essfuYAt8U/s320/eueoknuth.jpg" width="320" /></a></div>
<div style="text-align: center;">
<i>Eu e Knuth, o trollzinho</i></div>
<br />
Nesse dia perguntaram que opinião ele tinha sobre o problema mais difícil da nossa geração, <b>P=NP</b>. A intuição dele é que provalmente é verdade, mas ele acredita que se acharmos a demonstração, ela vai ser não-construtiva. O que isso significa? O que é uma demonstração não-construtiva?<br />
<br />
<h3 style="text-align: left;">
Demonstrações construtivas e não-construtivas</h3>
<br />
Em análise de algoritmos, as demonstrações construtivas são as mais comuns. Por exemplo, digamos que eu quero provar que é possível calcular <b>x</b> elevado a <b>y</b> em tempo <b>O(y)</b>. Isso é fácil, basta construir um algoritmo assim:<br />
<script src="https://gist.github.com/ricbit/cdaf28a10d04c8d7f680.js"></script>
E se eu quiser provar que esse mesmo problema pode ser resolvido em tempo <b>O(log y)</b>? Novamente, tudo que eu preciso fazer é exibir um algoritmo que implemente isso:<br />
<br />
<script src="https://gist.github.com/ricbit/5f0541022db0afc6a9ee.js"></script>
(Nesse caso eu também precisaria provar que esse algoritmo é de fato <b>O(log y)</b>, já não é óbvio por inspeção). Nos dois casos temos exemplos de demonstrações <b>construtivas</b>: se eu quero provar uma propriedade P, basta exibir um algoritmo que tenha essa propriedade P.<br />
<br />
As demonstrações <b>não-construtivas</b> são diferentes. Nelas, eu posso provar a propriedade P sem mostrar o algoritmo, através de alguma propriedade matemática do modelo.<br />
<br />
Por exemplo, imagine que eu tenho uma lista ordenada de números. Se eu fizer uma <b>busca binária</b>, posso achar a posição de um número dado com <b>O(log n)</b> comparações. Mas é possível criar um algoritmo mais rápido que isso? Eu digo que <b>não é possível</b>, e para isso vou fazer uma prova não-construtiva de que esse é o mínimo que um algoritmo de busca precisa para funcionar.<br />
<br />
<h3 style="text-align: left;">
A Teoria de Shannon aplicada à busca binária</h3>
<br />
Para isso eu vou usar a <a href="http://en.wikipedia.org/wiki/Information_theory">teoria da informação</a> de <a href="http://en.wikipedia.org/wiki/Claude_Shannon">Shannon</a>. Essa teoria é surpreendentemente intuitiva, e se baseia no conceito de surpresa. Se eu te falar que o <b>céu ficou escuro às 19h,</b> você não vai achar nada de mais, nessa hora o Sol está se pondo, então é natural que o céu fique escuro. Mas e se eu falar que o céu ficou escuro <b>às 10 da manhã</b>? Foi uma tempestade? Um eclipse? A nave do Independence Day?<br />
<br />
Intuitivamente, quanto mais surpresos nós ficamos com uma sentença, mais informação ela tem. O Shannon definiu então a quantidade de informação como sendo uma função monotônica da probabilidade do evento acontecer:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/kop3xdp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="I(m)=\log\left(\frac{1}{p(m)}\right)" border="0" src="http://mathurl.com/kop3xdp.png" /></a></div>
<br />
Se o evento é raro, tem bastante informação; se o evento é comum, tem pouca informação. A base do logaritmo fornece a unidade de medida, se a base for 2, então a informação é medida em <b>bits</b>.<br />
<br />
E quanta informação nós ganhamos com uma comparação? Se a chance de dar verdadeiro ou falso for a mesma, então a chance é <b>p(m)=1/2</b>, logo a informação é <b>I(m)=1</b>. Você ganha exatamente um bit de informação com uma comparação.<br />
<br />
Qual o resultado do nosso algoritmo de busca? O resultado é um índice, se nós temos <b>n</b> elementos no vetor, então a resposta é um índice que varia de <b>0</b> a <b>n-1</b>. Logo, a probabilidade de você escolher o índice certo ao acaso é <b>p(m)=1/n</b>, já que a escolha é uniforme.<br />
<br />
Quanta informação tem essa escolha, então? Fazendo a conta:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/jvjhyrs.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/jvjhyrs.png" height="40" width="320" /></a></div>
<br />
Se você precisa de <b>log n</b> bits para descrever a resposta, e você ganha só <b>1</b> bit por comparação, então não tem como um algoritmo rodar em menos que <b>O(log n)</b>: a informação tem que vir de algum lugar! Com isso, nós mostramos que qualquer algoritmo precisa rodar no mínimo em tempo <b>O(log n)</b>, e sem precisar mostrar o algoritmo em si. Essa é uma demonstração não-construtiva.<br />
<br />
Pressinto a pergunta: "mas RicBit, e a busca com hash table, ela não é <b>O(1)?</b>". Sim, ela é! Mas ela não usa comparações, e a nossa análise foi exclusivamente para métodos baseados em comparações. Com um acesso a uma hash você pode ganhar mais que 1 bit de informação por operação.<br />
<br />
<h3 style="text-align: left;">
O limite da ordenação</h3>
<br />
Um outro exemplo é achar o limite dos algoritmos de ordenação. Suponha que eu tenho um vetor com elementos bagunçados e quero ordená-los usando comparações. Eu sei que cada comparação ganha 1 bit de informação, então só preciso saber quanta informação tem na saída.<br />
<br />
Qual o resultado do algoritmo? Um vetor ordenado. Mas os valores do vetor em si são irrelevantes, o que importa mesmo é saber a ordem relativa entre eles. Essa ordem relativa pode ser expressa como uma permutação dos itens originais.<br />
<br />
Quantas permutações existem? Se o vetor tem tamanho <b>n</b>, então existem <b>n!</b> permutações, logo a probabilidade é <b>1/n!</b>. Fazendo as contas:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/kd5en5u.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img alt="\begin{align*}I(m)&=\log\left(\frac{1}{p(m)}\right)=\log\left(1/\frac{1}{n!}\right)=\log \left(n!\right)\\&\sim\log\left(n^n e^{-n}\sqrt{2\pi n}\right)\\&\sim n\log n-n-\frac{1}{2}\log\left(2\pi n\right)\\&\sim O(n \log n)\end{align*}" border="0" src="http://mathurl.com/kd5en5u.png" height="138" width="320" /></a></div>
<br />
Primeiro você usa a <a href="http://en.wikipedia.org/wiki/Stirling%27s_approximation">aproximação de Stirling</a>, depois joga fora todos os termos assintoticamentes menores que o dominante. O resultado é que nós provamos que nenhuma ordenação pode ser melhor que <b>O(n log n)</b>, sem precisar mostrar nenhum algoritmo!<br />
<br />
Novamente, esse resultado só vale para ordenações baseadas em comparações. Sem usar comparações, você tem métodos como radix sort e <a href="http://blog.ricbit.com/2008/04/otimizando-com-bacos.html">ábaco sort</a> que são melhores que <b>O(n log n)</b>.<br />
<br />
<h3 style="text-align: left;">
A análise por quantidade de informação</h3>
<br />
Esse método de análise da quantidade de informação pode ser utilizado em qualquer algoritmo, desde que você note um detalhe muito importante: o método acha um limite inferior para a complexidade, mas não prova que esse algoritmo existe! Tudo que conseguimos provar como ele é que, <i>se o algoritmo existir</i>, então ele não pode ser melhor que o limite achado.</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-82341687277555697902014-06-02T00:51:00.000-03:002019-09-03T01:24:35.063-03:00A Equação da Torre Eiffel<div dir="ltr" style="text-align: left;" trbidi="on">
No mês de maio eu completei <strong>cinco anos de casado</strong>! Quando eu casei, aproveitei a lua-de-mel em Paris para falar sobre <a href="http://blog.ricbit.com/2009/07/cidade-da-ciencia.html">a ciência da Torre Eiffel</a>, então nada mais apropriado que aproveitar o aniversário para falar sobre outro mistério da torre: a equação que descreve o seu formato!<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/mini_ilafox_ricbit_paris.jpg"><img alt="mini_ilafox_ricbit_paris" class="aligncenter wp-image-203 size-full" height="350" src="https://www.ilafox.com.br/ricbit/images/mini_ilafox_ricbit_paris.jpg" width="500" /></a>
</div>
<h3 style="text-align: left;">
A torre mais alta do mundo</h3>
<br />
O desafio proposto pelo <a href="http://en.wikipedia.org/wiki/Gustave_Eiffel">Gustave Eiffel</a> era grandioso: construir <strong>a torre mais alta do mundo</strong>. Na época, o recorde era o <a href="http://en.wikipedia.org/wiki/Washington_Monument">Monumento de Washington</a>, com 169 metros. O Eiffel pretendia erguer sua torre com 324 metros, quase o dobro do recorde anterior.<br />
<br />
Para entender o desafio, vale a pena comparar a Torre Eiffel com outras torres da antiguidade. O desenho abaixo está em escala, e mostra a Torre Eiffel comparada com a <a href="http://en.wikipedia.org/wiki/Great_Pyramid_of_Giza">Grande Pirâmide de Giza</a> no Egito, e com o <a href="http://en.wikipedia.org/wiki/St_Mark's_Campanile">Campanário de São Marcos</a> em Veneza:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/compare.jpg"><img alt="compare" class="aligncenter wp-image-205 size-full" height="227" src="https://www.ilafox.com.br/ricbit/images/compare.jpg" width="450" /></a></div>
<div style="text-align: center;">
<em style="color: #999999;">Imagem: Wikipedia (CC-SA)</em></div>
<br />
O primeiro desafio ao fazer uma torre é garantir que ela consiga <strong>suportar seu próprio peso</strong>. Os egípcios resolveram isso com força bruta: usaram blocos de pedra maciços e fizeram a base bem maior que o topo. O resultado final certamente funcionou, a pirâmide está aí de pé 4500 anos depois. Mas ela é um <strong>desperdício de material</strong>, com tecnologia moderna você não precisa de tanta matéria-prima para chegar nessa altura.<br />
<br />
O Campanário de São Marcos é o extremo oposto. Ele foi construído em 1514, e nessa época a tecnologia já permitia criar uma torre com a largura da base igual à do topo. O problema é que a torre <strong>caiu</strong>! Em 1902 ela não resistiu ao próprio peso e colapsou (nenhuma vida humana foi perdida, mas o gato do zelador morreu). A torre que você visita hoje em Veneza é uma reconstrução.<br />
<br />
Como fazer, então, uma torre alta que suporte o próprio peso? O Eiffel sabia a resposta porque tinha experiência no assunto. Antes de criar os ícones pelo qual é mais lembrado, ele construía<strong> pontes</strong>. Existem pontes do Eiffel por todo o mundo, desde a França e a Romênia, até o Vietnã e o Chile.<br />
<br />
Para entender a tecnologia do Eiffel, vale a pena comparar com uma ponte antiga: a <a href="http://en.wikipedia.org/wiki/Pont_du_Gard">Pont du Gard</a>, um aqueduto romano construído dois mil anos atrás. Ele cobre uma distância de 275 metros a uma altura de 48 metros. Para isso, ela precisa de <strong>cinco apoios</strong> no chão e três níveis de arcos para aguentar seu próprio peso:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/Pont_du_gard_v1_082005.jpg"><img alt="Pont_du_gard_v1_082005" class="alignnone size-medium wp-image-201" height="194" src="https://www.ilafox.com.br/ricbit/images/Pont_du_gard_v1_082005-620x194.jpg" width="620" /></a></div>
<div style="text-align: center;">
<span style="color: #999999;"><em>Imagem: Wikipedia (CC-SA)</em></span></div>
<br />
Agora, compare com uma das mais belas pontes construídas pelo Eiffel: a <a href="http://en.wikipedia.org/wiki/Maria_Pia_Bridge">Ponte Dona Maria</a>, na Cidade do Porto, em Portugal. Ela cobre 353m a 60m de altura, em ambos os casos maior que a ponte romana. E faz isso usando só <strong>dois apoios</strong> no chão! O segredo do Eiffel para conseguir esse feito é a magia da <strong><a href="http://en.wikipedia.org/wiki/Truss">treliça</a></strong>. Ao usar uma treliça, você consegue suportar mais peso com menos material.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/Ponte_Maria_Pia_-_Porto.jpg"><img alt="Ponte_Maria_Pia_-_Porto" class="alignnone size-medium wp-image-202" height="275" src="https://www.ilafox.com.br/ricbit/images/Ponte_Maria_Pia_-_Porto-620x275.jpg" width="620" />
</a>
<span style="color: #999999;"><em>Imagem: Wikipedia (CC-SA)</em></span></div>
<br />
Todo mundo sabia que o Eiffel era expert no assunto, e que ele saberia construir uma torre que suportasse o próprio peso. Mas, ainda assim, na época ele ouviu muitas críticas. Uma coisa é você construir uma ponte na horizontal, outra é construir uma torre na vertical. Estruturas na vertical tem um problema adicional: será que o Eiffel sabia como fazer a torre <strong>resistir ao vento</strong>?
<br />
<br />
<h3 style="text-align: left;">
O método geométrico do Eiffel</h3>
<br />
Quando a torre ficou pronta, o Eiffel calou os críticos. Quem já visitou a torre sabe que no terceiro piso venta. Venta muito mesmo! Porém, a torre não se abala. Fica lá, imóvel, mesmo com os ventos mais fortes.<br />
<br />
E qual foi o truque do Eiffel para deixar a torre tão estável com o vento? Até pouco tempo atrás, <strong>ninguém sabia</strong>. As plantas da torre Eiffel são conhecidas, você pode ver todas as especificações em um <a href="http://www.taschen.com/pages/en/catalogue/classics/all/44903/facts.the_eiffel_tower.htm">livrão publicado pela Taschen</a> (todas mesmo, tem até o diâmetro e comprimento de cada parafuso). Mas as plantas não falam o truque da estabilidade, tudo que dizem é que o Eiffel determinou o formato da torre de forma a minimizar a ação do vento, e fez isso usando "<em>métodos geométricos baseados em observações empíricas</em>".<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/DSC09174.jpg"><img alt="DSC09174" class="aligncenter size-medium wp-image-223" height="465" src="https://www.ilafox.com.br/ricbit/images/DSC09174-620x465.jpg" width="620" /></a> </div>
<br />
O formato da torre foi um mistério por mais de um século, até que <a href="http://www.sciencedaily.com/releases/2005/01/050106111209.htm">Patrick Weidman</a>, professor da Universidade de Colorado, matou o problema, e da maneira mais simples possível. Ele foi atrás dos <strong>diários do Eiffel</strong> e achou lá a dica que resolve o problema! O trecho do diário dizia:<br />
<br />
"<em>Suponha que as faces de uma treliça simples formam uma parede resistente às forças cisalhantes do vento, cujas componentes horizontais são P', P'', P''', P''''. Sabemos que, para calcular as forças atuantes no corte pelo plano MN, precisamos determinar a resultante P de todas as forças exteriores atuando acima da secção, e decompor essa resultante nas três forças passando pelas peças cortadas. Se a forma do sistema for tal que, para cada corte horizontal MN, a extensão das peças externas da treliça se intersectam na força exterior P, então as forças na barra interna serão zero e podemos excluir esse membro.</em>"<br />
<br />
O diário tinha esse desenho acompanhando:<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/trelica_original.png"><img alt="trelica_original" class="aligncenter wp-image-208 size-full" height="365" src="https://www.ilafox.com.br/ricbit/images/trelica_original.png" width="303" /></a></div>
<br />
Lendo com cuidado dá para entender direitinho a motivação do formato da torre. Vamos recriar o raciocínio em câmera lenta. Nós começamos supondo que o vento é uma carga horizontal atuando sobre toda a torre (que ele representou como as forças <strong>P'</strong>, <strong>P''</strong>, etc). O truque agora é pegar um corte horizontal arbitrário (que ele chamou de <strong>MN</strong>), e <strong>tirar toda a parte de cima da torre</strong>. As forças na estrutura não mudam se substituirmos a parte de cima por um ponto material de mesma massa, localizado no <a href="http://en.wikipedia.org/wiki/Centroid">centróide</a> do que foi tirado.<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/diagrama.png"><img alt="diagrama" class="aligncenter size-full wp-image-210" height="342" src="https://www.ilafox.com.br/ricbit/images/diagrama.png" width="303" /></a> </div>
<br />
Vamos então ver qual é o efeito do vento. Eu tenho uma força <strong>P</strong> horizontal, que é a resultante do vento. Tenho ainda três forças <strong>F1</strong>, <strong>F2</strong>, <strong>F3</strong>, que são as forças atuando sobre as vigas da treliça. Quando uma treliça está em equilíbrio, as forças sobre ela são sempre de tração ou compressão, ou seja, estão na mesma direção de suas vigas respectivas.<br />
<br />
A torre está em equilíbrio estático, logo a soma das forças é zero. Além disso, ela também não está rotacionando; logo a soma dos <a href="http://en.wikipedia.org/wiki/Torque">torques</a> também é zero. O torque é o produto da força pela distância; mas não é qualquer distância, é a distância na direção perpendicular à força.<br />
<br />
Agora o truque do Eiffel é claro. Ele construiu as laterais da torre de modo que <strong>as tangentes às vigas mais externas sempre passam pelo centróide</strong>. Quando você faz isso, a direção das forças <strong>P</strong>, <strong>F1</strong> e <strong>F3</strong> apontam para o centróide, então o torque delas é zero. Só a força <strong>F2</strong> está aplicando torque no centróide!<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/diagrama2.png"><img alt="diagrama2" class="aligncenter size-full wp-image-213" height="342" src="https://www.ilafox.com.br/ricbit/images/diagrama2.png" width="303" /></a></div>
<br />
Podemos calcular a soma dos torques agora. A distância associada à força <strong>F2</strong> é a distância <strong>d</strong>, em verde na figura. Note que ela é estritamente positiva.<br />
$$\begin{align*}
P\times 0 +F_1\times 0+F_2\times d+F_3\times 0&=0\\
F_2\times d&=0\\
F_2&=0
\end{align*}$$<br />
Ahá! Se as tangentes externas apontam para o centróide, então <strong>a força na viga interna é zero</strong>! Se o vento horizontal fosse a única força atuando sobre a torre, então nós poderíamos até tirar a viga interna que não faria diferença!<br />
<br />
É claro que na prática não podemos tirar a viga. Ela não ajuda a resistir ao vento, mas ajuda a sustentar o peso. Por outro lado, essa viga pode ser mais fraca que as outras. Com a idéia do Eiffel, <strong>só as vigas externas precisam ser fortes</strong>. Nessa foto que eu tirei do pilar oeste isso é bem visível: as vigas externas são largas e feitas de material rígido, enquanto que as internas são finas e feitas de mini-treliças, o que deixa a estrutura como um todo mais leve. E quanto mais leve, mais alto você pode subir.<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/ouest.jpg"><img alt="ouest" class="aligncenter size-medium wp-image-226" height="465" src="https://www.ilafox.com.br/ricbit/images/ouest-620x465.jpg" width="620" /></a></div>
<br />
<h3 style="text-align: left;">
A equação da Torre Eiffel</h3>
<br />
Agora que nós sabemos o truque do Eiffel, podemos calcular qual é o <strong>lugar geométrico dos pontos cujas tangentes passam pelo centróide</strong>, isso vai nos dar a equação que descreve o fomato da torre. Aparentemente o Eiffel nunca precisou fazer isso, já que ele usou "<em>um método geométrico baseado em observações empíricas</em>" (para mim isso é um jeito de dizer que ele calculou uma solução numérica ao invés de resolver a equação de fato).<br />
<br />
Mas ele poderia ter resolvido a equação se quisesse. Vamos fazer as contas na caixa azul para conferir, pule se você tem medo de equações diferenciais:
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Começaremos supondo que o formato da torre pode ser descrito por uma função <strong>f(x)</strong>. Para simplificar as contas (e, acredite, simplifica mesmo), nós vamos rotacionar o gráfico de modo a colocar o eixo <strong>x</strong> na vertical, orientado para baixo, e o <strong>f(x)</strong> na horizontal. Vamos ainda supor que <strong>f(x)</strong> é real, contínua e pode ser diferenciada muitas vezes.
<a href="https://www.ilafox.com.br/ricbit/images/grapheiffel.png"><img alt="grapheiffel" class="aligncenter size-full wp-image-227" height="405" src="https://www.ilafox.com.br/ricbit/images/grapheiffel.png" width="291" /></a><br />
No gráfico acima, <strong>f(x)</strong> está desenhada em azul. Por um ponto qualquer <strong>x</strong> eu traço as retas tangentes (em vermelho), elas vão se encontrar em um ponto <strong>x<span style="font-size: 75%; vertical-align: text-top;">C</span></strong>, que nós estamos supondo que sempre será o centróide da parte superior da torre (ou seja, do trecho que vai do ponto escolhido <strong>x</strong>, até o topo da torre <strong>x<span style="font-size: 75%; vertical-align: text-top;">T</span></strong>).
A posição do centróide é dado diretamente pela definição:
$$x_C=\frac{\int_{x_T}^{x}t f(t)\,dt}{\int_{x_T}^{x} f(t)\,dt}$$
A tangente podemos calcular de duas maneiras. Nós sabemos que a tangente que passa por <strong>x</strong> é dada diretamente por <strong>f'(x)</strong>. Por outro lado, ela também é a tangente do ângulo formado pelo eixo <strong>x</strong> e pela reta vermelha, que você calcula direto como cateto oposto pelo adjacente:
$$f'(x)=\frac{\Delta y}{\Delta x}=\frac{f(x) -0}{x-x_C}=\frac{f(x)}{x-x_C}$$
Podemos isolar <strong>x<span style="font-size: 75%; vertical-align: text-top;">C</span></strong> nessa última equação:
$$\begin{align*}
x-x_C&=\frac{f(x)}{f'(x)}\\
x_C&=\frac{x f'(x)-f(x)}{f'(x)}
\end{align*}$$
E depois igualar as duas equações:
$$\begin{align*}
\frac{x f'(x)-f(x)}{f'(x)} &= \frac{\int_{x_T}^{x}t f(t)\, dt}{\int_{x_T}^{x} f(t)\, dt}\\
\left(x f'(x)-f(x)\right)\int_{x_T}^{x} f(t)\, dt &= f'(x)\int_{x_T}^{x}t f(t)\, dt\\
\left(f'(x)\int_{x_T}^{x}x f(t)\, dt \right)-\left(f(x)\int_{x_T}^{x} f(t)\, dt\right) &= f'(x)\int_{x_T}^{x}t f(t)\, dt\\
f(x)\int_{x_T}^{x} f(t)\, dt &= f'(x)\int_{x_T}^{x}(x-t) f(t)\, dt
\end{align*}$$
Na última linha, veja que o <strong>x</strong> passou para dentro da integral, você pode fazer isso porque ele é constante em relação à variável de integração, que é <strong>t</strong>.
Antes de prosseguir, vamos dar nomes às integrais, chamando-as de <strong>g(x)</strong> e <strong>h(x)</strong>. Note que essas integrais são funções de <strong>x</strong>.
$$\begin{align*}
g(x)&=\int_{x_T}^{x} f(t)\, dt \\
h(x)&= \int_{x_T}^{x}(x-t) f(t)\, dt
\end{align*}$$
Vamos substituir as integrais pelas funções que nomeamos, e depois derivar dos dois lados. Quando você deriva dos dois lados, você está comendo uma constante, então no final podem aparecer soluções espúrias. Depois de acabar tudo, temos que pegar as soluções e voltar para o começo, conferindo se elas são realmente válidas.
$$\begin{align*}
f(x)g(x)&=f'(x)h(x)\\
f'(x)g(x)+f(x)g'(x) &= f''(x)h(x)+f'(x)h'(x)
\end{align*}$$
Como calcular <strong>g'(x)</strong> e <strong>h'(x)</strong>? Vamos começar por <strong>g'(x)</strong>. Note, primeiro, que você pode usar o teorema fundamental do cálculo para escrever <strong>g(x)</strong> em função da primitiva de <strong>f(x)</strong>, que iremos chamar de <strong>F(x)</strong>:
$$g(x)=\int_{x_T}^x f(t)\,dt=F(x) -F(x_T)
$$
Já que <strong>F(x<span style="font-size: 75%; vertical-align: text-top;">T</span>)</strong> é uma constante, sua derivada é zero. <strong>F(x)</strong> é uma primitiva, e a derivada da primitiva de uma função é a própria função (desde que ela seja contínua):
$$\begin{align*}
g(x)&=F(x) -F(x_T)\\
g'(x)&=F'(x)-0=f(x)
\end{align*}
$$
Então <strong>g'(x)=f(x)</strong>, essa foi fácil. Para calcular <strong>h'(x)</strong> é mais complicado. Lembremos a regra de <a href="http://en.wikipedia.org/wiki/Integration_by_parts">integração por partes</a> para integrais definidas:
$$\begin{align*}
\int_c^d a(t)b'(t)\,dt&=[a(t)b(t)]_c^d-\int_c^d a'(t)b(t)\,dt
\end{align*}
$$
Vamos escolher quem são <strong>a(t)</strong> e <strong>b(t)</strong>:
$$\begin{align*}
a(t)=x-t&\implies a'(t)=-1\\
b(t)=F(t)&\implies b'(t)=f(t)\\
\end{align*}
$$
Agora podemos quebrar <strong>h(x)</strong> em duas partes:
$$\begin{align*}
h(x)&=\int_{x_T}^x(x-t)f(t)\,dt\\
&=[(x-t)F(t)]_{x_T}^x-\int_{x_T}^x-F(t)\,dt\\
&=(x-x)F(x)-(x-x_T)F(x_T)+\int_{x_T}^xF(t)\,dt\\
&=-(x-x_T)F(x_T)+\int_{x_T}^xF(t)\,dt\\
&=-(x-x_T)F(x_T)+P(x)-P(x_T)\\
\end{align*}
$$
No último passo, por falta de notação melhor, eu chamei de <strong>P(x)</strong> a primitiva de <strong>F(x)</strong>. Agora podemos derivar <strong>h(x)</strong>. Note que, se <strong>P(x)</strong> é a primitiva de <strong>F(x)</strong>, então <strong>F(x)</strong> é a derivada de <strong>P(x)</strong>. Além disso, como <strong>P(x<span style="font-size: 75%; vertical-align: text-top;">T</span>)</strong> é constante, sua derivada é zero:
$$\begin{align*}
h(x)&=-(x-x_T)F(x_T)+P(x)-P(x_T)\\
h'(x)&=-F(x_T)+F(x)-0\\
h'(x)&=\int_{x_T}^xf(t)\,dt\\
h'(x)&=g(x)\\
\end{align*}
$$
Concluímos então que <strong>h'(x)</strong> é o mesmo <strong>g(x)</strong> que tínhamos visto antes! Antes de continuar, vale a pena resumir o que nós temos até agora:
$$\begin{align*}
f(x)g(x)&=f'(x)h(x)\\
f'(x)g(x)+f(x)g'(x) &= f''(x)h(x)+f'(x)h'(x)\\
f(x)&=g'(x)\\
f'(x)&=g''(x)\\
f''(x)&=g'''(x)\\
h'(x)&=g(x)
\end{align*}$$
Note que dá para reescrever quase tudo usando só <strong>g(x)</strong> e suas derivadas. Fica faltando <strong>h(x)</strong>, mas isso não é problema porque temos duas equações usando <strong>h(x)</strong>, então é só isolar. Da primeira equação temos:
$$\begin{align*}
f(x)g(x)&=f'(x)h(x)\\
h(x)&=\frac{f(x)g(x)}{f'(x)}\\
h(x)&=\frac{g'(x)g(x)}{g''(x)}\\
\end{align*}$$
Da segunda equação, temos:
$$\begin{align*}
f'(x)g(x)+f(x)g'(x)&=f''(x)h(x)+f'(x)h'(x)\\
f''(x)h(x)&=f'(x)g(x)+f(x)g'(x)-f'(x)h'(x)\\
h(x)&=\frac{f'(x)g(x)+f(x)g'(x)-f'(x)h'(x)}{f''(x)}\\
h(x)&=\frac{g''(x)g(x)+g'(x)g'(x)-g''(x)g(x)}{g'''(x)}\\
h(x)&=\frac{g'(x)^2}{g'''(x)}\\
\end{align*}$$
Igualando as duas expressões para <strong>h(x)</strong>:
$$\begin{align*}
\frac{g'(x)g(x)}{g''(x)} &= \frac{g'(x)^2}{g'''(x)} \\
\frac{g'(x)}{g(x)} &= \frac{g'''(x)}{g''(x)}
\end{align*}$$
Tudo que falta agora é resolver a equação para <strong>g(x)</strong> e finalmente temos o formato da torre. Mas essa equação é uma <strong>equação diferencial não-linear de terceira ordem</strong>. E isso é os que os matemáticos usam para assustar os filhos: "se você não comer, vai aparecer o bicho-papão, o homem do saco e a equação diferencial não-linear de terceira ordem para te pegar!"
Felizmente, essa equação pode ser resolvida se você tiver a visão além do alcance. O que acontece quando você tenta diferenciar <strong>log g(x)</strong>?
$$\begin{align*}
\frac{d}{dx}\log(g(x))=\frac{1}{g(x)}\times g'(x)=\frac{g'(x)}{g(x)}
\end{align*}$$
Ahá! Essa é exatamente a forma da equação. Fazendo o análogo no outro lado, podemos reescrever a equação original:
$$\begin{align*}
\frac{g'(x)}{g(x)} &= \frac{g'''(x)}{g''(x)}\\
\frac{d}{dx}\log g(x) &= \frac{d}{dx}\log g''(x)
\end{align*}$$
Agora nós podemos integrar dos dois lados (lembrando que vai aparecer uma constante), e depois tirar a exponencial dos dois lados:
$$\begin{align*}
\frac{d}{dx}\log g(x) &= \frac{d}{dx}\log g''(x)\\
\log g(x) &= \log g''(x) + C\\
g(x) &= k g''(x) \\
\end{align*}$$
Agora ficou fácil né? A equação medonha virou uma <strong>equação diferencial linear de segunda ordem</strong>, essa é bem-comportada, dá a patinha e finge de morta. A solução geral é da forma abaixo:
$$g(x)=A e^{B x}+C e^{-B x}$$
Como <strong>f(x)=g'(x)</strong>, então <strong>f(x)</strong> também tem a forma acima, só os coeficientes que serão diferentes.
A natureza da função depende dos coeficientes; por exemplo, se <strong>A=0.5</strong>, <strong>C=0.5</strong> e <strong>B=i</strong>, então <strong>f(x)=cos(x)</strong>. Mas nem todas as combinações de coeficientes são soluções válidas! Para determinar os coeficientes corretos, precisamos voltar o <strong>f(x)</strong> lá na equação original:
$$f(x)\int_{x_T}^{x} f(t)\, dt = f'(x)\int_{x_T}^{x}(x-t) f(t)\, dt
$$
Daqui em diante é só fazer as contas. Se você substituir tudo, vai notar que o único jeito de conseguir uma solução real válida para qualquer <strong>x</strong> é fazendo <strong>C=0</strong> e <strong>x<span style="font-size: 75%; vertical-align: text-top;">T</span>=-∞</strong>; nesse caso <strong>A</strong> pode ser um real qualquer, e <strong>B</strong> pode ser um real positivo qualquer. Estritamente falando, a torre teria que ser infinitamente alta para satisfazer a essa equação, mas na prática você pode aproximar "<em>infinitamente alta"</em> por "<em>muito, muito alta"</em>.
Concluindo, a solução final para o formato da torre Eiffel é:
$$f(x)=A e^{B x}
$$
Ou seja, a torre tem um perfil exponencial!
</div>
<h2>
</h2>
<h3 style="text-align: left;">
Matemática e Engenharia</h3>
<br />
Agora que sabemos qual o formato teórico da torre, podemos usar os dados reais da torre para estimar os coeficientes da exponencial. Eu rodei um best fit e cheguei em <strong>A=3.34323</strong> e <strong>B=0.0102592</strong>:<br />
<br />
<a href="https://www.ilafox.com.br/ricbit/images/bestfit.png"><img alt="bestfit" class="aligncenter size-full wp-image-237" height="230" src="https://www.ilafox.com.br/ricbit/images/bestfit.png" width="360" /></a><br />
<br />
A aproximação é boa, mas... parece que não encaixa direitinho? É meio frustrante fazer esse monte de contas, e no fim a curva real não ser tão parecida assim com a equação deduzida.<br />
<br />
Mas tem um motivo para isso, e o motivo é que o Eiffel era <strong>engenheiro</strong>, não matemático. Quando você plota uma exponencial em um <a href="http://en.wikipedia.org/wiki/Semi-log_plot">gráfico semi-log</a>, o resultado tem que ser uma linha. Mas olha só o que acontece com os dados reais da torre: eles formam <strong>duas</strong> linhas!<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/semilog1.png"><img alt="semilog" class="aligncenter size-full wp-image-239" height="240" src="https://www.ilafox.com.br/ricbit/images/semilog1.png" width="360" /></a> </div>
<br />
Coisas que você aprende na sua primeira aula de engenharia: se você faz o projeto de um elevador, e determina que o cabo do elevador precisa aguentar no mínimo <strong>10 toneladas</strong>, qual o cabo que você coloca no elevador? Um matemático diria que é um cabo de 10 toneladas, mas o engenheiro vai falar que o mínimo são <strong>14 toneladas</strong>.<br />
<br />
Isso é o resultado do <a href="http://en.wikipedia.org/wiki/Factor_of_safety">fator de segurança</a>. Por mais que o seu modelo matemático seja correto, sempre tem alguma coisa que você não levou em conta (pode ser que o aço que você usou tenha impurezas, ou que o solo não é tão firme quanto você achava, e assim por diante). Por isso, é costume sempre multiplicar o valor final por um fator de segurança para levar em contas esses imprevistos.<br />
<br />
O Eiffel sabia disso, e no diário ele explica que imaginava que a base da torre estaria sujeita a torques maiores que o topo, e por isso usou um fator de segurança diferente na base e no topo. Quando você faz o best fit com duas exponenciais ao invés de uma só, o gráfico fica bem melhor!<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/twofit.png"><img alt="twofit" class="aligncenter size-full wp-image-240" height="230" src="https://www.ilafox.com.br/ricbit/images/twofit.png" width="360" /></a> </div>
<br />
Agora finalmente podemos dar o mistério por resolvido: o formato da Torre Eiffel é descrito por duas exponenciais, escolhidas para minimizar o material necessário para resistir à carga do vento.<br />
<br />
A Torre Eiffel é o literalmente o maior monumento à ciência construído, da próxima vez que passar por Paris aproveite para fazer uma reverência aos cientistas do passado :)<br />
<br />
<a href="https://www.ilafox.com.br/ricbit/images/eiffeltower.jpg"><img alt="eiffeltower" class="aligncenter size-medium wp-image-241" height="362" src="https://www.ilafox.com.br/ricbit/images/eiffeltower-620x362.jpg" width="620" /></a></div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-86541240777196654602014-03-31T00:36:00.000-03:002019-09-03T01:24:21.801-03:00Matemática x Tanques de Guerra<div dir="ltr" style="text-align: left;" trbidi="on">
Quando eu era criança pequena lá na década de 80, eu tinha um brinquedo de tanque de guerra. Ele chamava <a href="https://www.youtube.com/watch?v=t3t97lqcWIg">Panzer</a>, e era fabricado pela <a href="http://www.brinquedosmimo.com.br/">brinquedos Mimo</a>. Para a época era bem divertido: tinha controle remoto, e, além de andar, ainda disparava mísseis!
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/panzer.jpg"><img alt="panzer" class=" wp-image-188 aligncenter" height="262" src="https://www.ilafox.com.br/ricbit/images/panzer.jpg" width="400" /></a></div>
<br />
Mas eu não percebia a ironia do brinquedo. O Panzer não era só um brinquedo, ele existiu de verdade. Na vida real, o Panzer era uma <a href="http://en.wikipedia.org/wiki/German_tanks_in_World_War_II">máquina nazista de destruição</a>. A série Panzer de tanques alemães foi responsável pela morte direta de incontáveis aliados. Ver um Panzer surgindo no horizonte era como ver a morte vindo em sua direção. Era um mimo esse Panzer.<br />
<br />
Por outro lado, o Panzer era forte, mas não era invencível. Os aliados tinham armas capazes de deter o Panzer, o problema era descobrir quantas dessas armas eles deveriam levar para o front. Poucas semanas antes do <a href="http://en.wikipedia.org/wiki/Normandy_landings">Dia D</a>, os alemães começaram a usar o novo modelo Panzer V, e os aliados não tinham como estimar quantas armas levar sem saber quantos Panzer V os inimigos fabricaram.<br />
<br />
<span style="line-height: 1.6em;">Foi então que os aliados usaram a arma definitiva contra o Panzer: a <em>Matemática!</em> Usando um truque muito esperto, eles estimaram que os alemães estavam fabricando 270 Panzers por mês. Depois que a guerra acabou, eles foram nos arquivos alemães conferir os números, e na verdade estavam fazendo 276 Panzers por mês. A estimativa foi muito boa!</span>
<br />
<span style="line-height: 1.6em;"><br /></span>
<br />
<h3 style="text-align: left;">
A Quantidade de Tanques</h3>
<br />
O truque dos aliados se baseava em um erro dos alemães e em uma hipótese estatística. O erro dos alemães era que os tanques tinham <em>número de série sequencial</em>. Quando saíam da fabrica, eram numerados como 1, 2, 3, e assim por diante. Ao fazer isso, eles deixaram uma brecha para os hackers aliados.<br />
<br />
Suponha que você capturou um Panzer alemão, e o serial dele era 14. Você consegue estimar quantos tanques eles produziram baseado só nesse número? No caso geral não, mas você pode fazer uma hipótese simplificadora, que é supor que o tanque que você capturou é aleatório (ou seja, os alemães estavam distribuindo igualmente os tanques por toda a Europa).<br />
<br />
Com essa hipótese a intuição é clara. Vamos supor, hipoteticamente, que os alemães fizeram 200 tanques. Qual chance de você capturar um tanque de serial menor ou igual a 14 nessa situação? É 14/200 né? E se os alemães fizeram só 20 tanques? Aí a chance é 14/20, dez vezes maior que 14/200. Com a hipótese de tanque aleatório, é mais provável que os alemães tenham feito 20 tanques do que 200 tanques.<br />
<br />
Você pode formalizar esse argumento usando o teorema de Bayes, e calcular o <strong>valor esperado</strong> do número de tanques. Vamos fazer as contas na caixa azul:
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
<span style="line-height: 1.6em;">Suponha que conseguimos capturar <strong>k</strong> tanques. Dentre esses <strong>k</strong> tanques, o maior serial encontrado tem o valor <strong>m</strong>. Com essas informações, vamos calcular qual é o valor esperado do número de tanques produzidos, que vamos chamar de <strong>n</strong>. Podemos abrir esse valor esperado pela definição:</span><br />
$$E[n\;|\;m,k] = \sum_{n} n \; p(n\; |\; m,k)$$<br />
O problema então é achar a probabilidade condicional de <strong>n</strong>, dados <strong>m</strong> e <strong>k</strong>. Nós ainda não sabemos quanto vale essa probabilidade, mas podemos abri-la usando o <a href="http://en.wikipedia.org/wiki/Bayes'_theorem">teorema de Bayes</a>:<br />
$$p(n\;|\;m,k)=\frac{p(m,k\;|\;n)\;p(n)}{p(m,k)}$$<br />
Essas três probabilidades nós conseguimos calcular! A primeira delas, <strong>p(m,k|n)</strong>, sai por combinatória. Dado um vetor de tamanho <strong>n</strong>, de quantas maneiras conseguimos escolher <strong>k</strong> elementos, de modo que o maior deles seja <strong>m</strong>?<br />
<br />
Um dos elementos precisa necessariamente ser o <strong>m</strong>, então só precisamos calcular a posição dos outros <strong>k-1</strong> restantes. E eles precisam ser menores que <strong>m</strong>, então só podem estar nas <strong>m-1</strong> posições menores que <strong>m</strong>. Por isso, essa quantidade vale binomial(<strong>m-1</strong>, <strong>k-1</strong>). Note, entretanto, que isso só vale quando <strong>n>=m</strong>, caso contrário a probabilidade é zero (não tem como o máximo ser <strong>m</strong> se o vetor tem um tamanho menor que <strong>m</strong>).<br />
<br />
Para calcular a probabilidade desejada, temos que achar de quantas maneiras podemos escolher <strong>k</strong> elementos dentre <strong>n</strong>, sem considerar a restrição do maior deles ser <strong>m</strong>. Mas isso é o próprio binomial(<strong>n</strong>, <strong>k</strong>). Então, a probabilidade é:<br />
$$p(m,k\;|\;n) =
\frac{\displaystyle{m-1\choose k-1}}
{\displaystyle{n\choose k}}[n\ge m]$$<br />
Vamos agora para a segunda, <strong>p(n)</strong>. Eu não tenho nenhuma informação sobre quantos tanques foram produzidos, então não sei qual é a probabilidade de ter <strong>n</strong> tanques. Mas eu posso chutar que a distribuição é uniforme. A chance de ter 15 tanques, ou de ter 500 tanques, a priori, deve ser a mesma. Por isso, vamos fazer <strong>p(n)</strong> ser uma constante independente de <strong>n</strong>:<br />
$$p(n)=c$$<br />
A última, <strong>p(m,k)</strong>, é a probabilidade do máximo ser <strong>m</strong> em <strong>k</strong> escolhas, independente do valor de <strong>n</strong>. Para isso, podemos usar a <a href="http://en.wikipedia.org/wiki/Law_of_total_probability">lei da probabilidade total</a>:
<br />
$$p(m,k) = \sum_n p(m,k\;|\;n) \;p(n)$$
<br />
Essas duas já sabemos calcular! É só substituir:<br />
$$\begin{align*}
p(m,k) &= \sum_n p(m,k\;|\;n) \;p(n) \\
&= \sum_n
\frac{{m-1\choose k-1}}
{{n\choose k}}[n\ge m]\;c\\
&= \sum_{n\ge m} c
{m-1\choose k-1}
{n\choose k}^{-1}\\
&= c {m-1 \choose k-1} \sum_{n\ge m}
{n\choose k}^{-1}\\
\end{align*}
$$<br />
A somatória tem uma forma fechada, que você pode achar com o <a href="http://scienceblogs.com.br/caixaazul/2014/03/o-objetivo-final-da-matematica/">algoritmo de Gosper</a>:<br />
$$\sum_{n=m}^{\infty}{n\choose k}^{-1}=\frac{m}{k-1}{m\choose k}^{-1}$$
<br />
Substituindo:<br />
$$\begin{align*}
p(m,k)
&= c {m-1 \choose k-1} \sum_{n\ge m}
{n\choose k}^{-1}\\
&= c {m-1 \choose k-1} \times \frac{m}{k-1}{m\choose k}^{-1} \\
&= \frac{cm}{k-1} {m-1 \choose k-1} {m\choose k}^{-1} \\
\end{align*}
$$<br />
Agora podemos voltar e substituir as três probalidades na fórmula de Bayes:<br />
$$
\begin{align*}
p(n\;|\;m,k)&=\frac{p(m,k\;|\;n)\;p(n)}{p(m,k)} \\
&= \frac{\displaystyle{m-1\choose k-1}{n\choose k}^{-1}[n\ge m]\times c}
{\displaystyle\frac{cm}{k-1}{m-1\choose k-1}{m\choose k}^{-1}}\\
&= \frac{k-1}{m}{m\choose k}{n\choose k}^{-1}[n\ge m]
\end{align*}$$<br />
Por fim, podemos substituir na fórmula do valor esperado:<br />
$$\begin{align*}
E[n\;|\;m,k] &= \sum_{n} n \; p(n\; |\; m,k)\\
&= \sum_n n \frac{k-1}{m}{m\choose k}{n\choose k}^{-1}[n\ge m]\\
&= \frac{k-1}{m}{m\choose k}\sum_{n\ge m} n {n\choose k}^{-1}\\
\end{align*}$$<br />
Essa somatória também tem uma forma fechada por Gosper:<br />
$$
\sum_{n=m}^{\infty} n{n\choose k}^{-1}=\frac{m(m-1)}{k-2}{m\choose k}^{-1}$$<br />
Chegamos então na substituição final:<br />
$$\begin{align*}
E[n\;|\;m,k]
&= \frac{k-1}{m}{m\choose k}\sum_{n\ge m} n {n\choose k}^{-1}\\
&= \frac{k-1}{m}{m\choose k}\times\frac{m(m-1)}{k-2}{m\choose k}^{-1} \\
&= \frac{(k-1)(m-1)}{k-2} \\
\end{align*}$$</div>
<h2>
</h2>
<h3 style="text-align: left;">
</h3>
<h3 style="text-align: left;">
A Estimativa na Prática</h3>
<br />
A fórmula final é super simples, mas ela funciona mesmo na prática? Podemos testá-la fazendo uma simulação numérica. Para isso, eu fiz dez mil simulações. Em cada uma eu sorteio o número de tanques fabricados, e escolho aleatoriamente 3 deles. Aí eu calculo a estimativa pela fórmula, e normalizo o erro. O código <a href="https://github.com/ricbit/Oldies/blob/master/2014-03-germantank/germantank.py">está no github</a>, e o histograma resultante é o abaixo. A normalização é tal que o valor 0 é uma estimativa totalmente correta:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/germantank3.png"><img alt="germantank3" class="size-full wp-image-189 aligncenter" height="227" src="https://www.ilafox.com.br/ricbit/images/germantank3.png" width="360" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
A estimativa não ficou muito boa não! Ao invés de ter uma gaussiana em torno do zero, o estimador tem um <a href="http://en.wikipedia.org/wiki/Bias_(statistics)">bias</a> muito forte para os negativos. Felizmente, esse problema só acontece porque capturamos apenas 3 tanques. Se, ao invés disso, tivéssemos capturado 30 tanques, então o histograma seria bem melhor. Compare o histograma de 30 tanques sobreposto ao de 3 tanques:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/germantank30.png"><img alt="germantank30" class="size-full wp-image-190 aligncenter" height="226" src="https://www.ilafox.com.br/ricbit/images/germantank30.png" width="360" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Bem melhor né? Agora quase todas as estimativas estão bem próximas do zero. Esse método é bastante sensível ao número de tanques capturados. Os aliados sabiam disso, por isso que a estimativa deles foi tão boa (estimaram 270 tanques/mês, quando o valor real era 276). Sabe quantos tanques eles capturaram para fazer essa estimativa? Só <em>dois tanques</em>!</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Claro que tem um truque. A estimativa com dois tanques é muito ruim, mas eles notaram que os alemães colocavam serial em todas as <em>peças</em> do tanque. Em especial, cada tanque tinha 48 rodas, cada uma com um serial único. Por isso, eles conseguiram usar a fórmula com <strong>k</strong>=96, o que deu a precisão alta que queriam. E no fim a matemática ganhou a guerra :)</div>
<div style="text-align: center;">
</div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-6338561333944028452014-03-01T14:59:00.000-03:002019-09-03T01:24:07.603-03:00O Objetivo Final da Matemática <div dir="ltr" style="text-align: left;" trbidi="on">
Quem acompanhou as contas na caixa azul do <a href="https://www.blogger.com/scienceblogs.com.br/caixaazul/2014/02/o-paradoxo-do-pokemon-twitch/">post sobre o Pokèmon Twitch</a> deve ter notado que eu pulei um pedaço. Em certo ponto da demonstração, eu magicamente tirei do chapéu uma fórmula nada óbvia:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/presto-1.jpg"><img alt="presto (1)" class="size-full wp-image-123 aligncenter" height="194" src="https://www.ilafox.com.br/ricbit/images/presto-1.jpg" width="500" /></a></div>
<br />
E como foi que eu cheguei nessa fórmula? Simples, eu calculei no <a href="http://www.wolframalpha.com/">Wolfram Alpha</a>:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/wolframalpha1.png"><img alt="wolframalpha" class="size-full wp-image-107 aligncenter" height="236" src="https://www.ilafox.com.br/ricbit/images/wolframalpha1.png" width="420" /></a></div>
<br />
"<em>Ricbit, você <strong>trapaceou</strong>! Que roubalheira! Um matemático de verdade jamais faria isso!</em>"<br />
<br />
Mas será que é trapaça mesmo? Nesse post, eu vou mostrar como se resolve essa conta sem usar o Wolfram Alpha; e, se você acha que é trapaça, talvez acabe mudando de idéia :)
<br />
<br />
<h3 style="text-align: left;">
A progressão hipergeométrica</h3>
<br />
Para resolver essa somatória sem usar o Wolfram Alpha, nós precisamos relembrar as progressões que aprendemos no colégio. A primeira delas foi a <strong>progressão aritmética</strong>, onde a <em>diferença</em> entre dois números consecutivos é constante:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="17" src="https://www.ilafox.com.br/ricbit/images/kysxuja.png" width="136" /> </div>
<br />
A segunda delas é a <strong>progressão geométrica</strong>, onde a <em>razão</em> entre dois números consecutivos é constante:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/ln88zff.png" width="89" /> </div>
<br />
Uma progressão extremamente útil que não ensinam no colégio é a <strong>progressão hipergeométrica</strong>. Ela parece com a progressão geométrica, porque é definida a partir da razão entre dois elementos consecutivos. Mas agora, ao invés dessa razão ser constante, nós permitimos que a razão seja uma <a href="http://en.wikipedia.org/wiki/Rational_function">função racional</a> (que é só um jeito chique de dizer que ela é o quociente entre dois polinômios):<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/mc4l467.png" width="120" /> </div>
<br />
Naquela somatória que queremos resolver, os termos sendo somados formam uma progressão hipergeométrica. Confira:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="143" src="https://www.ilafox.com.br/ricbit/images/ppj4ast.png" width="491" /> </div>
<br />
O numerador é um polinômio em <strong>q</strong>, o denominador é um polinômio em <strong>q</strong>. Então confere com a nossa definição de progressão hipergeométrica.
<br />
<br />
<h3 style="text-align: left;">
A soma da progressão hipergeométrica</h3>
<br />
Certamente vocês lembram que as progressões do colégio tinham uma fórmula para a soma de seus termos. A soma dos <strong>n</strong> primeiros termos da progressão aritmética é:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="46" src="https://www.ilafox.com.br/ricbit/images/mzed6jg.png" width="172" /></div>
<br />
A soma dos <strong>n</strong> primeiros termos da progressão geométrica é:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="46" src="https://www.ilafox.com.br/ricbit/images/kmlwhuu.png" width="160" /> </div>
<br />
A soma dos <strong>n</strong> primeiros termos da progressão hipergeométrica é... oops! <strong>Não existe</strong> uma fórmula geral para a soma de termos hipergeométricos.<br />
<br />
Antigamente, achar uma fórmula para uma soma hipergeométrica era tão raro que elas até ganhavam nomes especiais. Por exemplo, a igualdade abaixo tem um nome, é o <a href="http://en.wikipedia.org/wiki/Binomial_theorem">Binômio de Newton</a>:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="48" src="https://www.ilafox.com.br/ricbit/images/ns7x5yc.png" width="194" /> </div>
<br />
Outras somas hipergeométricas não tem fórmula, mas são tão úteis, que <em>a própria soma</em> ganha um nome e passa a valer como se fosse uma fórmula:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="47" src="https://www.ilafox.com.br/ricbit/images/o8t5adj.png" width="192" /></div>
<br />
Por muitos séculos esse foi o estado da arte. Alguém acha uma fórmula aqui, alguém nomeia uma soma ali. Mas isso mudou no meio do século 20.
<br />
<br />
<h3 style="text-align: left;">
O algoritmo de Gosper</h3>
<br />
O primeiro progresso na direção de um método geral de resolução de somas hipergeométricas foi feito pela <a href="http://en.wikipedia.org/wiki/Mary_Celine_Fasenmyer">Mary Celine Fasenmyer</a> em 1945. Depois de se formar em matemática, ela se juntou à ordem religiosa <a href="http://en.wikipedia.org/wiki/Sisters_of_Mercy">Sisters of Mercy</a> (sim, a mesma que inspirou <a href="http://www.youtube.com/watch?v=TMETa77dUrg">o nome da banda</a>), e lá ela criou o <a href="http://mathworld.wolfram.com/SisterCelinesMethod.html">Método da Irmã Celine</a>, um algoritmo que consegue resolver automaticamente alguns tipos de somas hipergeométicas.<br />
<br />
Em 1978 o método foi aprimorado por <a href="http://en.wikipedia.org/wiki/Bill_Gosper">Bill Gosper</a> e se tornou o <strong>algoritmo de Gosper</strong> (muito embora eu desconfie que, se a Irmã Celine fosse homem, o algoritmo seria de Gosper-Celine). O método de Gosper se baseia em um teorema surpreendente: A solução de uma soma hipergeométrica é outro hipergeométrico, <a href="http://en.wikipedia.org/wiki/If_and_only_if">se e somente se</a> ela puder ser escrita na forma abaixo, onde <strong>R[j]</strong> é uma função racional.
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/oayftfe.png" width="134" /></div>
<br />
Essa somatória parece esquisita. Se <strong>j</strong> é o índice da somatória, então o que ele está fazendo no lado direito da equação? E cadê os limites da somatória?<br />
<br />
A resposta é que essa é uma <a href="http://en.wikipedia.org/wiki/Indefinite_sum">somatória indefinida</a>. Funciona do mesmo jeito que no cálculo: lá você tem integrais definidas e integrais indefinidas, aqui nós temos somatórias definidas (com limites), e somatórias indefinidas (sem limites). Para uma somatória indefinida, sempre vale a propriedade abaixo:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/p6ghlbc.png" width="316" /> </div>
<br />
Essas duas equações são tudo que nós precisamos para usar o algoritmo de Gosper. Vamos à caixa azul calcular a nossa somatória original:
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Começamos substituindo uma equação na outra:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/n5mpykc.png" width="417" /></div>
<br />
Podemos então dividir tudo por <strong>h[j]</strong>:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/o5zlmxz.png" width="197" /> </div>
<br />
Opa, a razão entre os <strong>h</strong> nós já calculamos lá em cima para a nossa somatória!<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="39" src="https://www.ilafox.com.br/ricbit/images/n6ludc3.png" width="246" /> </div>
<br />
Falta achar <strong>R[j]</strong>. Eu sei que ele é um quociente de polinômios, mas não sei qual o grau desses polinômios. Eu vou <em>chutar</em> que o grau é 1, se der errado depois eu volto e aumento o grau. Pois bem, se o grau é 1, então <strong>R[j]</strong> é da forma abaixo:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="38" src="https://www.ilafox.com.br/ricbit/images/k6ghwzv.png" width="99" /> </div>
<br />
Agora eu vou substituir. Aperte o cinto que lá vem turbulência:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="238" src="https://www.ilafox.com.br/ricbit/images/q9hqrap.png" width="516" /> </div>
<br />
O resultado é um polinômio gigante em <strong>j</strong> que é identicamente nulo. Se ele é identicamente nulo, então cada um dos seus coeficientes precisa ser zero. Com isso podemos montar um sistema de equações:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="120" src="https://www.ilafox.com.br/ricbit/images/n5rfwx6.png" width="505" /> </div>
<br />
Agora é "só" resolver o sistema! Vou poupá-los de páginas e páginas, o resultado é o seguinte:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="92" src="https://www.ilafox.com.br/ricbit/images/oud59yq.png" width="93" /> </div>
<br />
Se o sistema não tivesse solução, eu teria que voltar lá em cima e aumentar o grau do polinômio (se as contas foram titânicas com grau 1, imagine com grau 2). Mas o processo não é infinito, existem métodos que permitem achar um limite superior para o grau do polinômio. Se você atingir esse limite sem achar nenhuma solução, então você <em>provou</em> que sua somatória não pode ser escrita como um hipergeométrico.<br />
<br />
Voltando à nossa equação original, agora nós sabemos quanto vale a somatória indefinida:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="142" src="https://www.ilafox.com.br/ricbit/images/m4klzh6.png" width="365" /> </div>
<br />
Nós queremos a soma de <strong>1</strong> até <strong>m</strong>, então precisamos converter a soma indefinida em uma soma definida, usando o teorema fundamental do cálculo discreto:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="52" src="https://www.ilafox.com.br/ricbit/images/lpsyotc.png" width="340" /> </div>
<br />
Aplicando o teorema, temos:<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="237" src="https://www.ilafox.com.br/ricbit/images/kwgmadr.png" width="453" /> </div>
<br />
Pronto, esse é o resultado final. Eu acho muito importante que a pessoa resolva o algoritmo de Gosper uma vez na vida, para <strong>nunca mais cometer a sandice de repetir o processo</strong>. O método é mecânico, você só precisa seguir as regras e não errar as contas. O computador faz isso melhor que você. Use o Wolfram Alpha e economize grafite.
</div>
<h2>
</h2>
<h3 style="text-align: left;">
O objetivo final da Matemática</h3>
<br />
No final do século 19, o <a href="http://en.wikipedia.org/wiki/Bertrand_Russell">Bertrand Russell</a> e o <a href="http://en.wikipedia.org/wiki/A._N._Whitehead">Alfred Whitehead</a> tiveram uma idéia. Ele bolaram um plano para axiomatizar toda a matemática, de uma maneira completa e livre de contradições. Se o plano funcionasse, então toda a Matemática seria reduzida à manipulação de símbolos, que é um processo puramente mecânico e automatizável. O plano começou muito bem, a ponto do Whitehead fazer a seguinte declaração:<br />
<br />
"<em>A Civilização avança estendendo o número de operações importantes que podemos fazer sem ter que pensar.</em>"<br />
<br />
Baseado nessa citação, o <a href="http://www.amazon.com/gp/product/0201558025/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201558025&linkCode=as2&tag=bradum0c-20">Concrete Mathematics</a><img alt="" border="0" height="1" src="https://ir-na.amazon-adsystem.com/e/ir?t=bradum0c-20&l=as2&o=1&a=0201558025" style="border: none !important; margin: 0px !important;" width="1" /> do <a href="http://en.wikipedia.org/wiki/Donald_Knuth">Knuth</a> vai além e conclui:<br />
<br />
"<em>O objetivo final da matemática é eliminar a necessidade de todo pensamento inteligente.</em>"<br />
<br />
É verdade, o objetivo sempre foi esse. Para os babilônios, resolver uma equação de segundo grau era dificílimo, só os grandes mestres conseguiam. Hoje em dia, nós transformamos a equação de segundo grau em uma fórmula que é ensinada para crianças de 14 anos. Você não precisa ser inteligente para usar a fórmula, é só substituir os números e fazer as contas.<br />
<br />
Imagine se fosse possível fazer isso com <em>toda</em> a matemática, ao invés de só com equações de segundo grau! Você poderia usar computadores para fazer as contas, no lugar de crianças de 14 anos. A Matemática seria um <em>problema resolvido</em>, e aí você pode se concentrar no que interessa, que são as aplicações. Um físico acabou de descobrir uma propriedade quântica nova, será que ela permite criar um aparelho de teletransporte? Eu monto a equação, jogo no computador, e tcharam! Está aqui a resposta!<br />
<br />
Toda a saga por trás do plano do Russell e do Whitehead é contada em quadrinhos no fabuloso <a href="https://www.amazon.com/gp/product/1596914521/ref=as_li_tf_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1596914521&linkCode=as2&tag=bradum0c-20">Logicomix</a><img alt="" border="0" height="1" src="https://ir-na.amazon-adsystem.com/e/ir?t=bradum0c-20&l=as2&o=1&a=1596914521" style="border: none !important; margin: 0px !important;" width="1" />. Infelizmente, o final da história é que o plano <em>falhou</em>. E falhou <em>espetacularmente</em>. Em 1931, o <a href="http://en.wikipedia.org/wiki/Kurt_G%C3%B6del">Gödel</a> provou que <em>nenhum</em> sistema axiomático é capaz de provar todas as sentenças verdadeiras que é capaz de descrever, acabando de uma vez por todas com o sonhado plano da axiomatização completa.<br />
<br />
E o século 20 desceu ladeira abaixo. O <a href="http://en.wikipedia.org/wiki/Alonzo_Church">Church</a> provou que, mesmo se a axiomatização completa tivesse tido sucesso, seria impossível criar um método que prove um teorema qualquer a partir dos axiomas. O <a href="http://en.wikipedia.org/wiki/Alan_Turing">Turing</a> provou que é impossível criar um método que analise um programa de computador e diga se ele termina ou se entra em loop infinito. O <a href="http://en.wikipedia.org/wiki/Yuri_Matiyasevich">Matiyasevich</a> provou que é impossível criar um método que resolva todas as <a href="http://en.wikipedia.org/wiki/Diophantine_equation">equações diofantinas</a>. O <a href="http://en.wikipedia.org/wiki/Richardson's_theorem">Richardson</a> provou que é impossível criar um método que prove todas as identidades trigonométricas. Foi o fim da matemática automatizada... ou não?<br />
<br />
Claro que não! Todos esses teoremas provam a impossibilidade do <em>caso geral</em>. Mas você sempre pode automatizar um <em>caso específico</em>. Não existe uma fórmula geral para a soma hipergeométrica, mas o algoritmo de Gosper prova que <em>pelo menos um</em> caso especial pode ser automatizado: o caso da soma hipergeométrica cujo resultado também é um termo hipergeométrico. E esse caso é <em>imensamente útil</em> na prática, especialmente se você estuda computação ou combinatória.<br />
<br />
E no fim das contas, usar o Wolfram Alpha para resolver uma somatória <strong>é trapacear ou não</strong>? Por dentro, tudo que o Wolfram Alpha faz é usar o algoritmo de Gosper. Se você acredita que usar um método computacional é roubar, então por consistência precisa abrir de mão de todos os outros métodos computacionais. Não pode mais resolver a equação de segundo grau usando a fórmula de Bhaskara, não pode mais calcular o máximo divisor comum com o <a href="http://en.wikipedia.org/wiki/Euclidean_algorithm">algoritmo de Euclides</a>, não pode nem calcular quanto é 463 vezes 367 sem antes decorar a tabuada do 367.<br />
<br />
Para mim isso não faz sentido. E por isso eu uso o Wolfram Alpha sem medo de ser feliz :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-71508326859500461162014-02-23T14:43:00.000-03:002019-09-03T01:23:54.917-03:00O Paradoxo do Pokémon Twitch<div dir="ltr" style="text-align: left;" trbidi="on">
A última moda da internet é o <a href="http://www.twitch.tv/twitchplayspokemon">Pokémon Twitch</a>. Como essas modas evaporam tão rapidamente quanto surgem, vale a pena explicar do que se trata:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/poketwitch.jpg"><img alt="poketwitch" class=" wp-image-90 aligncenter" height="280" src="https://www.ilafox.com.br/ricbit/images/poketwitch-620x350.jpg" width="496" /></a></div>
<br />
O twitch é um site para livestreaming de videogames. Você conecta seu videogame no site, e as pessoas do mundo todo podem te assistir enquanto você joga. Ele tem também uma janela de chat, então as pessoas podem comentar enquanto assistem.
<br />
<br />
Mas algum gênio aprimorou a idéia original. Ele ligou o twitch em um emulador de Game Boy, e configurou o emulador para pegar o texto do chat e interpretar como se fosse o joystick. Com isso, qualquer um que tenha uma conta lá pode controlar o personagem do jogo (no caso, a versão original de Pokémon). E melhor ainda, como todos estão controlando o mesmo jogo, então o que ele fez foi criar uma espécie de <em>crowdgaming</em>, onde a sabedoria das massas escolhe o melhor caminho do personagem.
<br />
<br />
Quer dizer, na teoria. Na prática, a internet será a internet. Para cada um que tenta jogar sério, tem um trollzinho que fica mandando o comando oposto. No pico de popularidade, ele chegou a ter mais de cem mil pessoas jogando ao mesmo tempo. Imagine metade disso sacaneando ao invés de jogando, e dá pra ter uma idéia da loucura que é o Pokémon Twitch (ou então <a href="http://www.dorkly.com/article/59505/the-majesty-of-twitch-plays-pokemon">leia o FAQ</a> para entender tudo que já aconteceu até agora).
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/pikachu_muitcholoco.jpg"><img alt="pikachu_muitcholoco" class=" wp-image-91 aligncenter" height="250" src="https://www.ilafox.com.br/ricbit/images/pikachu_muitcholoco.jpg" width="400" /></a></div>
<br />
Quando eu fiquei sabendo do Pokémon Twitch, eu fiz o que qualquer pessoa normal faria: criei um modelo matemático do jogo. No meu modelo simplificado, o personagem só anda na vertical, um passo por iteração. O número de jogadores e o número de trolls é o mesmo, então a cada passo ele tem 50% de chance de andar uma unidade para cima ou para baixo. Assuma que ele começa da origem. Nesse modelo, eu faço duas perguntas:
<br />
<ol>
<li>Depois de <strong>n</strong> iterações, onde você espera que o personagem esteja?</li>
<li>Depois de <strong>n</strong> iterações, qual você espera que seja a distância do jogador à origem?</li>
</ol>
Eu sei, eu sei: "<em>Ricbit, deixa de ser bobo! As duas perguntas são iguais! Suponha que a resposta da primeira pergunta é <strong>y</strong>. Então a resposta da segunda é <strong>y </strong>menos <strong>0</strong>, ou seja, o mesmo valor <strong>y</strong></em>".<br />
<br />
Paradoxalmente, isso está errado! A resposta das duas perguntas é diferente! Probabilidade é um tópico que escapa facilmente da nossa intuição, então vale a pena fazer as contas com cuidado para entender a solução.
<br />
<br />
<h3 style="text-align: left;">
O valor esperado da posição</h3>
<br />
Para a resposta da primeira pergunta, vamos calcular qual é o <a href="http://en.wikipedia.org/wiki/Expected_value">valor esperado</a> da posição. Na iteração <strong>0</strong> ele ainda não executou nenhum movimento, então sabemos a posição deterministicamente: ele está na origem.
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="17" src="https://www.ilafox.com.br/ricbit/images/nz2ophv.png" width="82" /></div>
<br />
E na iteração <strong>n+1</strong>? Depende de onde ele estava na iteração <strong>n</strong>. Tem 50% de chance de ter subido, e 50% de ter descido:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="51" src="https://www.ilafox.com.br/ricbit/images/ktrb7jr.png" width="229" /></div>
<br />
Com isso podemos calcular o valor esperado de <strong>y(n+1)</strong> direto da <a href="http://en.wikipedia.org/wiki/Law_of_total_expectation">lei da expectativa total</a>:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="67" src="https://www.ilafox.com.br/ricbit/images/kz8ftuk.png" width="494" /></div>
<br />
Ou seja, o valor esperado em qualquer iteração é sempre zero, na média nós esperamos que ele fique andando em círculos e nunca fique muito longe da origem.
<br />
<br />
Entretanto, note que só porque o valor esperado é zero não quer dizer que ele vai sempre terminar na origem: a média é zero, mas a variância não é. Um exercício bastante curioso é calcular qual a probabilidade exata do personagem estar na origem após <strong>n</strong> iterações. Isso é fácil e dá pra resolver com combinatória de colégio. Imagine que você tem <strong>n</strong> caixinhas:
<br />
<br />
<table style="border: none; margin: 0 auto;">
<tbody>
<tr>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
</tr>
</tbody>
</table>
<br />
Cada uma dessas caixinhas você pode preencher com <strong>+1</strong> ou <strong>-1</strong>; se a soma de todas elas for zero, então o personagem termina na origem. De quantas maneiras podemos fazer isso? Note que nós só precisamos escolher as posições dos <strong>+1</strong>:
<br />
<br />
<table style="border: none; margin: 0 auto;">
<tbody>
<tr>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;">+1</td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;">+1</td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;">+1</td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;">+1</td>
<td style="border: 1px solid black; height: 30px; text-align: center; width: 30px;"></td>
</tr>
</tbody>
</table>
<br />
Sabendo onde estão os <strong>+1</strong>, a posição dos <strong>-1</strong> restantes fica unicamente determinada. Precisamos então descobrir de quantas maneiras podemos encaixar <strong>n/2</strong> números <strong>+1</strong> em <strong>n</strong> caixinhas. Mas isso é a definição do <a href="http://en.wikipedia.org/wiki/Binomial_coefficient">binomial</a>:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="41" src="https://www.ilafox.com.br/ricbit/images/mrs7on9.png" width="45" /></div>
<br />
Agora é só dividir pelo total. Como cada caixinha tem duas opções possíveis, <strong>+1</strong> e <strong>-1</strong>, então o número total de caixinhas é <strong>2^n</strong>. Portanto, a probabilidade dele terminar na origem é:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="41" src="https://www.ilafox.com.br/ricbit/images/koz4rtz.png" width="172" /></div>
<div style="text-align: left;">
<br />
Essa fórmula tem um problema: embora ela seja exata, é muito ruim de calcular quando o <strong>n</strong> é grande (tente calcular para <strong>n=1000</strong>, o número de dígitos da sua calculadora vai acabar rapidinho). Podemos achar um <a href="http://en.wikipedia.org/wiki/Asymptotic_analysis">assintótico</a> usando a aproximação do <a href="http://en.wikipedia.org/wiki/Central_binomial_coefficient">coeficiente binomial central</a>:<br />
<br /></div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="46" src="https://www.ilafox.com.br/ricbit/images/l9a8app.png" width="235" /></div>
<div style="text-align: left;">
<br />
Mais fácil né? Agora podemos calcular facilmente que, para <strong>n=1000</strong>, a chance de terminar na origem é aproximadamente <strong>2.5%</strong>.<br />
<br />
Se você tem o olho bom, deve ter percebido uma pegadinha na derivação da fórmula acima. Ela só funciona quando <strong>n</strong> é par! De fato, quando <strong>n</strong> é ímpar, não tem como o número de <strong>+1</strong> e <strong>-1</strong> serem iguais, e portanto a chance dele terminar na origem é zero. Olha que curioso: quando o <strong>n</strong> é ímpar, ele nunca termina em zero, apesar do valor esperado ser zero!</div>
<div style="text-align: left;">
<br /></div>
<h3 style="text-align: left;">
O valor esperado da distância</h3>
<div style="text-align: left;">
<br />
Vamos agora à segunda pergunta, que é o valor esperado da distância. Por que afinal esse valor é diferente do anterior?</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Para exemplificar, vamos supor que <strong>n=2</strong>. Em uma das amostras, digamos que os jogadores ganharam e ele andou <strong>2</strong> unidades para cima, então a distância para a origem é <strong>2</strong>. Em outra amostra, suponha que os trolls ganharam, então ele andou <strong>2</strong> unidades para baixo. Qual a distância da origem? É <strong>2</strong> também! Distâncias são sempre positivas!</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Em outras palavras, o valor esperado da distância é o valor esperado do <em>módulo</em> da posição! E quanto é esse valor? As contas aqui são um pouco mais complicadas, então vou precisar da caixa azul:</div>
<div style="text-align: left;">
<br /></div>
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Dessa vez nós vamos ter que calcular o valor esperado pela definição:
<br />
<br />
<div style="text-align: center;">
<img alt="" class="aligncenter" height="40" src="https://www.ilafox.com.br/ricbit/images/oae9mn6.png" width="270" /></div>
<div style="text-align: left;">
<br />
Quando <strong>k=0</strong> o somando vale zero também. Vamos então supor <strong>k</strong> positivo e usar a mesma idéia das caixinhas. A soma era zero quando a quantidade de <strong>+1</strong> e de <strong>-1</strong> era igual. Dessa vez, nós queremos que a soma seja <strong>k</strong>, então precisamos ter <strong>k</strong> números <strong>+1</strong> a mais que o número de <strong>-1</strong>. Portanto, a chance dele terminar em um valor <strong>k</strong> positivo é:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="41" src="https://www.ilafox.com.br/ricbit/images/k5hcmjd.png" width="303" /></div>
<div style="text-align: left;">
<br />
Quando <strong>k</strong> é negativo, a fórmula é exatamente a mesma! Afinal, é só trocar todos os <strong>+1</strong> por <strong>-1</strong> e vice versa. Portanto, o valor esperado da distância é:</div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="47" src="https://www.ilafox.com.br/ricbit/images/krjguxs.png" width="213" /></div>
<div style="text-align: left;">
<br />
Essa fórmula é difícil de manipular, para deixar mais fácil vamos supor que <strong>n</strong> é par. Se <strong>n</strong> é par, podemos escrevê-lo como <strong>n=2m</strong>. Mas olha só, se <strong>n</strong> for par, então você nunca vai terminar numa distância <strong>k</strong> que seja ímpar. Se você tirar <strong>k</strong> ímpar de <strong>n</strong> par, o que sobra é ímpar, então não tem como ter soma zero. Portanto podemos supor <strong>k</strong> par, e fazer <strong>k=2q</strong>. Substituindo:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="156" src="https://www.ilafox.com.br/ricbit/images/l7xxymp.png" width="265" /></div>
<div style="text-align: left;">
<br />
A somatória é uma <a href="http://en.wikipedia.org/wiki/Hypergeometric_function">soma hipergeométrica</a>, então você pode resolvê-la com o <a href="http://en.wikipedia.org/wiki/Gosper's_algorithm">algoritmo de Gosper</a>, chegando assim na forma fechada. Mas se você não souber usar o algoritmo de Gosper, não tem problema, é só provar a identidade abaixo por <a href="http://en.wikipedia.org/wiki/Mathematical_induction">indução finita</a>:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="47" src="https://www.ilafox.com.br/ricbit/images/mled6ck.png" width="257" /></div>
<div style="text-align: left;">
<br />
Substituindo a fórmula temos:</div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="180" src="https://www.ilafox.com.br/ricbit/images/kkvgs3m.png" width="236" /></div>
<div style="text-align: left;">
<br />
Agora é só usar a aproximação do binomial central:</div>
<div style="text-align: center;">
<img alt="" class="aligncenter" height="182" src="https://www.ilafox.com.br/ricbit/images/ox2kggu.png" width="187" /></div>
<div style="text-align: left;">
<br />
Chegamos então no resultado final, o valor esperado da distância, que é raiz de <strong>2n</strong> sobre <strong>pi</strong>.</div>
</div>
<h2 style="text-align: left;">
</h2>
<h3 style="text-align: left;">
<br />A matemática experimental</h3>
<div style="text-align: left;">
<br />
Probabilidade tem uma vantagem grande sobre outros ramos da matemática: é muito fácil conferir as contas. Basta escrever uma simulação computacional com uma quantidade razoável de amostras!</div>
<div style="text-align: left;">
Eu escrevi um script em python (o código está <a href="https://github.com/ricbit/Oldies/blob/master/2014-02-randomwalk/simulation.py">no github</a>), e os resultados para <strong>n=1000</strong> e cem mil amostras foram os seguintes:</div>
<div style="text-align: left;">
<br /></div>
<ul>
<li><strong>Posição</strong>: calculado <strong>0.00</strong>, medido <strong>0.05</strong></li>
<li><strong>Distância</strong>: calculado <strong>25.23</strong>, medido <strong>25.27</strong></li>
<li><strong>Término na origem</strong>: calculado <strong>2.52%</strong>, medido <strong>2.53%</strong></li>
</ul>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Ou seja, a matemática experimental concordou com a matemática teórica.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
A lição que fica dessa brincadeira é tomar muito cuidado com suas intuições sobre probabilidade, porque ela tem uma tendência a nos enganar bem facilmente :)</div>
<div style="text-align: left;">
</div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-22487328788336024912014-02-20T14:29:00.000-03:002019-09-03T01:23:43.471-03:00Como Provar um Axioma<div dir="ltr" style="text-align: left;" trbidi="on">
Eu sei o que você está pensando: "<em>Tá maluco Ricbit? Não dá pra provar um <a href="http://en.wikipedia.org/wiki/Axiom">axioma</a>! Eles são proposições tão simples que sua verdade é auto-evidente, não necessitando de prova! Se eu pudesse prová-lo, então não seria um axioma, seria um <a href="http://en.wikipedia.org/wiki/Theorem">teorema</a>!</em>"<br />
<br />
No fundo, essa é a essência do <a href="http://en.wikipedia.org/wiki/G%C3%B6del's_incompleteness_theorems">segundo teorema da incompletude de Gödel</a>: um sistema formal não pode provar sua auto-consistência. Mas tem uma pegadinha aqui. Embora um sistema não possa provar sua auto-consistência, nada impede que você use um sistema mais forte para provar outro sistema mais fraco. Para mostrar como isso pode ser feito, vamos usar como exemplo o mais antigo de todos os axiomas.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/euclides.jpg"><img alt="euclides" class=" wp-image-75 aligncenter" height="284" src="https://www.ilafox.com.br/ricbit/images/euclides.jpg" width="400" /></a></div>
<h3 style="text-align: left;">
</h3>
<h3 style="text-align: left;">
O primeiro axioma de Euclides</h3>
<br />
O primeiro sistema axiomático foi aquele publicado nos Elementos de Euclides: os cinco postulados que definem as regras da geometria. O axioma que abre a lista não poderia ser mais simples:<br />
<br />
<strong style="line-height: 1.6em;">Axioma 1: "Entre dois pontos é sempre possível traçar uma reta".</strong>
<br />
<br />
Mas essa simplicidade é traiçoeira! Na verdade, essa afirmação só faz sentido se você souber de antemão o que é um <em>ponto</em> e o que é uma <em>reta</em>. O Euclides sabia desse problema, por isso, antes de apresentar seus cinco axiomas, ele começa o livro com uma série de 23 definições. Para nós, as definições importantes são as 1, 2 e 4:
<br />
<strong><br /></strong>
<strong>Definição 1: "Um ponto é aquilo que não tem parte".</strong><br />
<br />
Isso faz sentido. O ponto é o elemento indivisível, o que não pode ser quebrado em pedaços, o átomo. Em linguagem moderna, o ponto é um elemento de dimensão 0.
<br />
<strong><br />Definição 2: "Uma linha é um comprimento sem largura".</strong>
<br />
<br />
Isso também faz sentido. A linha é uma coisa comprida e infinitamente fina. Hoje em dia, diríamos que a linha é um elemento de dimensão 1.
<br />
<strong><br />Definição 4: "Uma reta é uma linha que se põe igualmente com seus pontos".</strong>
<br />
<br />
E aqui, my friends, é onde a porca torce o rabo. O que raios o Euclides quis dizer com isso? Eu fiquei um tempão tentando interpretar essa frase e não tive sucesso. No desespero, tentei até achar o <a href="http://farside.ph.utexas.edu/euclid/Elements.pdf">original em grego</a>, mas mesmo no original a frase é ambígua.
<br />
<br />
Sem outra alternativa, o jeito foi apelar para alguém que entenda mais que eu. No caso, o grego <a href="http://en.wikipedia.org/wiki/Proclus">Proclus</a>, que viveu no século 5 AD, e publicou uma versão comentada dos Elementos. Segundo Proclus, Euclides queria dizer que "de todas as linhas, somente a linha reta ocupa uma distância igual àquela entre seus pontos. Pois a distância de dois pontos entre si é a mesma que o comprimento da linha reta que os tem como extremidades; e esse é o sentido de se pôr igualmente com os seus pontos".
<br />
<br />
Ou seja, pela interpretação de Proclus, a quarta definição é equivalente a dizer que, de todas as linhas que ligam dois pontos, a reta é a menor delas!
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_caminho.jpg"><img alt="ricbit_ilafox_caminho" class=" wp-image-67 aligncenter" height="256" src="https://www.ilafox.com.br/ricbit/images/ricbit_ilafox_caminho.jpg" width="400" /></a></div>
<br />
Estritamente falando, essa definição só joga a sujeira debaixo do tapete, porque agora você precisa definir o que é <em>distância</em>. Mas isso já é suficiente para nosso propósito! Nós temos um sistema que define o que é distância: o <a href="http://scienceblogs.com.br/caixaazul/2014/02/a-redacao-de-riemann/">tensor métrico de Riemann</a>! Se eu assumir que o cálculo sobre os números reais existe, então eu posso usá-los para demonstrar o primeiro axioma de Euclides (ou seja, vou usar um sistema formal mais forte para provar um mais fraco).
<br />
<br />
Embora o axioma seja simples, as contas para demonstrá-lo não são. Se você fica assustado facilmente com equações diferenciais parciais, pule a caixa azul:
<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Vamos começar com o tensor métrico de Riemann. Na geometria euclideana, ele é o próprio teorema de Pitágoras:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom1.png"><img alt="axiom1" class="size-full wp-image-68 aligncenter" height="19" src="https://www.ilafox.com.br/ricbit/images/axiom1.png" width="119" /></a></div>
<br />
Nós podemos multiplicar e dividir por <strong>dx^2 </strong>(sim, eu sei), e depois tirar a raiz quadrada dos dois lados:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom2.png"><img alt="axiom2" class="size-full wp-image-69 aligncenter" height="151" src="https://www.ilafox.com.br/ricbit/images/axiom2.png" width="185" /></a></div>
<br />
Vamos supor que a função final será <strong>y=f(x)</strong>. Então <strong>dy/dx</strong> é a derivada de <strong>f(x)</strong>. E nesse ponto podemos integrar dos dois lados:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom3.png"><img alt="axiom3" class="size-full wp-image-70 aligncenter" height="70" src="https://www.ilafox.com.br/ricbit/images/axiom3.png" width="185" /></a></div>
<br />
Se a linha entre <strong>A</strong> e <strong>B</strong> é descrita pela função <strong>f(x)</strong>, então seu comprimento será <strong>S</strong>. O nosso objetivo é achar a função <strong>f(x)</strong> que minimiza <strong>S</strong>.
<br />
<br />
Quem já estudou cálculo deve estar pensando "é só derivar e igualar a zero!". A intuição é essa mesmo, mas os detalhes são diferentes. Nós não queremos achar um ponto que minimiza uma função, queremos achar uma função que minimiza uma integral.
<br />
<br />
Esse tipo de problema é resolvido pelo <a href="http://en.wikipedia.org/wiki/Calculus_of_variations">Cálculo de Variações</a>. A teoria em geral é bastante complicada, mas nesse caso em específico tem uma fórmula que resolve o problema sozinha, a <a href="http://en.wikipedia.org/wiki/Euler-Lagrange_equation">equação de Euler-Lagrange</a>. A sua forma é a seguinte: suponha que você tenha uma função <strong>L[x, f(x), f'(x)]</strong>. Então o extremo da integral abaixo...
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom4.png"><img alt="axiom4" class="size-full wp-image-72 aligncenter" height="42" src="https://www.ilafox.com.br/ricbit/images/axiom4.png" width="201" /></a></div>
<br />
.. é dado pela solução dessa equação diferencial:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom5.png"><img alt="axiom5" class="size-full wp-image-73 aligncenter" height="38" src="https://www.ilafox.com.br/ricbit/images/axiom5.png" width="127" /></a></div>
<br />
No nosso caso, a função <strong>L[x, f(x), f'(x)]</strong> é dada por:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom6.png"><img alt="axiom6" class="size-full wp-image-74 aligncenter" height="21" src="https://www.ilafox.com.br/ricbit/images/axiom6.png" width="227" /></a></div>
<br />
Agora eu vou fazer as contas em câmera lenta porque elas são levemente diferentes do cálculo normal que estamos acostumados. O primeiro termo da equação é zero, porque <strong>L</strong> é constante em relação a <strong>f(x)</strong>. Confira: na expressão de <strong>L</strong>, aparece <strong>f'(x)</strong>, mas não aparece <strong>f(x)</strong>.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom7.png"><img alt="axiom7" class="size-full wp-image-79 aligncenter" height="38" src="https://www.ilafox.com.br/ricbit/images/axiom7.png" width="53" /></a></div>
<br />
Para a segunda derivada parcial, é só aplicar a regra da cadeia. Esse passo tem uma pegadinha:
<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom8.png"><img alt="axiom8" class="size-full wp-image-80 aligncenter" height="127" src="https://www.ilafox.com.br/ricbit/images/axiom8.png" width="233" /></a></div>
<br />
Peraí, ali na segunda linha não tinha que multiplicar pela derivada segunda de <strong>f</strong>? Teria, se você estivesse derivando em relação a <strong>x</strong>, mas você está derivando em relação a <strong>f'(x)</strong>!
Podemos fazer a última derivada agora:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom9.png"><img alt="axiom9" class="size-full wp-image-81 aligncenter" height="256" src="https://www.ilafox.com.br/ricbit/images/axiom9.png" width="498" /></a></div>
<br />
Agora é só plugar na equação original:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom10.png"><img alt="axiom10" class="size-full wp-image-83 aligncenter" height="87" src="https://www.ilafox.com.br/ricbit/images/axiom10.png" width="139" /></a></div>
<br />
Opa, o denominador é sempre positivo, então dá pra jogar fora!
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom11.png"><img alt="axiom11" class="size-full wp-image-84 aligncenter" height="68" src="https://www.ilafox.com.br/ricbit/images/axiom11.png" width="129" /></a></div>
<br />
Tcharam! A menor linha que liga dois pontos realmente é uma reta! Mas ainda não acabou, falta provar que a reta é única. Isso vem direto das condições de contorno:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/axiom12.png"><img alt="axiom12" class="size-full wp-image-85 aligncenter" height="136" src="https://www.ilafox.com.br/ricbit/images/axiom12.png" width="148" /></a></div>
<br />
Pronto, <strong>C1</strong> e <strong>C2</strong> são unicamente definidos pelos pontos nas extremidades, então a reta que os une é única. QED.
<br />
<br /></div>
<br />
Minha telepatia anda forte ultimamente, então eu ainda sei o que você está pensando: "<em>Pra que isso Ricbit? Você pegou uma proposição da geometria que era intuitiva, e a transformou em um monte de contas horrendas!</em>"
<br />
<br />
Sim, é verdade. Eu fiz isso de propósito, para ilustrar um ponto importante: pra que você quer se limitar ao que sua intuição alcança? Nós temos à disposição cinco milênios de matemática, eles nos permitem trabalhar com coisas muito além do que nossa intuição consegue visualizar!
<br />
<br />
Nessas contas da caixa azul nós usamos o tensor métrico para achar o que é uma reta no espaço euclideano. Mas o mesmo raciocínio pode ser usado para achar retas em qualquer tipo de métrica. E sabe quem mais anda em linha reta? A <strong>luz</strong>!
<br />
<br />
Mesmo quando o tensor métrico está muito além da minha intuição, como nas proximidades de um buraco negro (onde vale a <a href="http://en.wikipedia.org/wiki/Schwarzschild_metric">métrica de Schwarzschild</a>), eu ainda assim sei fazer as contas que me mostram como a luz se comporta por ali. Às vezes até a matemática feia tem sua utilidade :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-32000352789465090922014-02-17T01:32:00.000-03:002019-09-03T01:23:29.706-03:00A Redação de Riemann<div dir="ltr" style="text-align: left;" trbidi="on">
<br />
Dia desses a <a href="http://cybergi.wordpress.com/">Giseli</a> me pediu para escrever alguma coisa sobre o Riemann. Naturalmente eu topei, até porque eu e o Riemann temos uma anedota em comum! Georg Riemann nasceu em 1826, em uma linhagem de pastores luteranos: seu pai, avô e bisavô eram pastores. Desde cedo ele sempre ia muito bem na escola, a ponto do pai dele ter que contratar um professor particular, porque só a escola comum não era o suficiente para ele. Mas isso também não ajudou muito, em certo ponto o tutor disse que estava aprendendo com o Riemann mais do que estava ensinando para ele. A solução foi achar uma escola melhor.
<br />
<br />
Aos quatorze anos, o Riemann saiu de casa para ir morar com a avó, ficando assim muito mais perto do respeitável Gymnasium de Hannover, o melhor da região. E é aqui que as nossas histórias se interceptam, eu também saí de casa aos quatorze para ir morar com a avó, mas no meu caso foi para estudar na respeitável ETFSP (que hoje em dia chama <a href="http://www.ifsp.edu.br/">IFSP</a>).
<br />
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/ricbit_riemann.jpg"><img alt="ricbit_riemann" class="wp-image-28 aligncenter" height="230" src="https://www.ilafox.com.br/ricbit/images/ricbit_riemann.jpg" width="440" /></a></div>
<div>
<br /></div>
Quando completou o ginásio, a idéia original do Riemann era estudar Teologia para ser pastor, como mandava a tradição familiar. Mas nesse meio tempo ele teve uma experiência transformadora: ele assistiu a uma palestra do <a href="http://en.wikipedia.org/wiki/Carl_Friedrich_Gauss">Gauss</a>, onde ele apresentava pela primeira vez o método dos mínimos quadrados. A palestra deve ter sido muito boa, já que, desse momento em diante, o Riemann decidiu que iria seguir a carreira de Matemática.<br />
<div>
<br />
Depois de conseguir seu doutorado, o Riemann tentou uma posição de professor na mesma universidade do Gauss. Como teste de admissão, o Gauss pediu para que ele escrevesse uma redação. Mas teria que ser sobre um tema que ambos gostassem, então a proposta foi a seguinte: o Riemann iria sugerir três tópicos, e dos três o Gauss escolheria qual seria o tema da redação. Os temas que o Riemann escolheu foram:
<br />
<br />
<ul>
<li>Séries de Fourier</li>
<li>Sistemas de Equações Quadráticas</li>
<li>Fundamentos da Geometria</li>
</ul>
O Gauss acabou escolhendo o terceiro tema, e essa escolha é bem clara quando você conhece o espírito da época. Para explicar por que o Gauss escolheu esse tema, temos que rebobinar até a Grécia antiga.
<br />
<br />
<h3 style="text-align: left;">
Os Elementos de Euclides</h3>
<div style="text-align: left;">
<br />
Pelo menos até metade do século 20, <a href="http://en.wikipedia.org/wiki/Euclid's_Elements">Elementos de Euclides</a> era o segundo livro mais publicado no mundo, perdendo só para a Bíblia (mas hoje em dia ele deve ter perdido para o Harry Potter e o Crepúsculo).</div>
</div>
<div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Um dos motivos de ser um livro tão publicado é que trata-se de um livro muito didático, começando de princípios bem simples, e construindo em cima deles teoremas cada vez mais complexos. Tem um motivo para o livro ser assim: Euclides era professor, e os Elementos são a apostila com notas de aula! <span style="line-height: 1.6em;">Provavelmente poucos teoremas do livro são de autoria dele, o mérito do Euclides foi reunir todos os trabalhos da época e colocar em notação consistente. Infelizmente, os originais de onde o Euclides tirou os teoremas foram todos destruídos nos muitos incêndios da </span><a href="http://en.wikipedia.org/wiki/Library_of_Alexandria" style="line-height: 1.6em;">Grande Biblioteca de Alexandria</a><span style="line-height: 1.6em;">. </span>
</div>
<div style="text-align: left;">
<span style="line-height: 1.6em;"><br /></span>
</div>
<span style="line-height: 1.6em;">Pois bem, quais são os princípios básicos usados por Euclides? O livro começa com cinco deles:</span><br />
<br />
<br />
<ul>
<li>Por dois pontos você sempre pode traçar uma reta.</li>
<li>Um segmento de reta sempre pode ser estendido indefinidamente.</li>
<li>Dado um centro e um raio, você sempre pode construir um círculo.</li>
<li>Todos os ângulos retos são congruentes.</li>
<li>Se você desenhar duas retas que intersectam uma terceira, de maneira que o ângulo interno em um dos lados seja menor que dois ângulos retos, então as duas linhas originais necessariamente se intersectam nesse lado, se você as estender o suficiente.</li>
</ul>
Você não precisa entender de geometria para ver que tem algo errado aqui. Os quatro primeiros princípios são curtinhos, mas o quinto é enorme. Por que isso acontece? O problema é que o Euclides não conseguiu provar esse fato usando só os princípios anteriores, então ele simplesmente assumiu que era verdade sem provar. Por dois milênios, um monte de matemáticos tentou consertar esse buraco nos Elementos, mas ninguém conseguia. Foi só no século 19 que tivemos algum progresso.
<br />
<br />
<h3 style="text-align: left;">
Lobachevsky e Bolyai</h3>
<br />
Como você tentaria provar a quinta proposição? A maneira mais natural é tentar um ataque por contradição: você assume o contrário do que quer provar, e tenta chegar numa contradição. Assumindo a negação da proposição, você conclui um monte de fatos que são intuitivamente absurdos. Por exemplo, que dado uma reta, existem infinitas retas paralelas a ela passando por um ponto dado, que o teorema de Pitágoras é falso, que a soma dos ângulos internos de um triângulo é menor que 180 graus.<br />
<br />
Mas apesar de serem absurdos, nenhum desses fatos é uma contradição de verdade. Para a demonstração funcionar, você precisa de algum fato que contradiga os quatro princípios iniciais dos Elementos. Contradizer o que vem depois no livro não serve! Foi nesse ponto que, de maneira independente, o russo <a href="http://en.wikipedia.org/wiki/Nikolai_Lobachevsky">Lobachesvsky</a> e o húngaro <a href="http://en.wikipedia.org/wiki/J%C3%A1nos_Bolyai">Bolyai</a> tiveram um insight: e se na verdade a quinta proposição fosse <em>independente</em> das demais? Ou seja, tanto faz se você usa ela do jeito original ou a sua negação, o sistema continua consistente do mesmo jeito?<br />
<br />
Isso é a base do que hoje chamamos de <a href="http://en.wikipedia.org/wiki/Non-Euclidean_geometry">geometrias não-Euclideanas</a>. Essa era uma idéia chocante para a época, de que podem existir geometrias que funcionam apesar de não seguirem todas as regras dos Elementos. O pai do Bolyai ficou tão orgulhoso com o resultado do filho que mandou uma carta pro Gauss avisando da descoberta. O Gauss respondeu "olha, eu não posso elogiar seu filho, porque eu tive a mesma idéia 30 anos atrás, e seria indelicado elogiar a mim mesmo."
<br />
<br />
<h3 style="text-align: left;">
A Curvatura de Gauss</h3>
<br />
Tecnicamente falando, era verdade. O Gauss realmente tinha pensado nisso, mas ele nunca publicou o resultado porque achou que era uma bobagem. Eu não tenho como saber o que se passava na cabeça dele, mas imagino que foi algo assim: "Essa geometria é perversa, se eu assumir que ela existe, então a soma dos ângulos internos do triângulo dá menos de 180 graus, e isso só seria verdade se o plano fosse curvo, o que é um absurdo. Por outro lado, eu posso usar isso para <em>definir</em> o que é curvatura de uma superfície! Eu defino então a <a href="http://en.wikipedia.org/wiki/Gaussian_curvature">curvatura intrínseca</a> de uma superfície como sendo uma função de quanto a soma dos ângulos desvia de 180 graus."<br />
<br />
Depois de definir o que é curvatura intrínseca, o Gauss provou uma proposição tão legal que ele chamou de <a href="http://en.wikipedia.org/wiki/Theorema_Egregium">Theorema Egregium</a> (em português é Teorema Incrível, e se o Gauss chamou de incrível é porque a coisa é quente mesmo). O enunciado é assim:<br />
<br />
"<em>A curvatura intrínseca de uma superfície depende só das distâncias entre os pontos, e não de suas posições</em>".<br />
<br />
Isso parece... óbvio? Quer dizer, imagine que recorto um triângulo de papel reciclado como o abaixo:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/triangulo.jpg"><img alt="triangulo" class=" wp-image-31 aligncenter" height="250" src="https://www.ilafox.com.br/ricbit/images/triangulo-620x416.jpg" width="372" /></a></div>
<br />
Se eu mudar a posição dos pontos, por exemplo, fazendo uma translação, obviamente a soma dos ângulos do triângulo não muda:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/triangulo-1.jpg"><img alt="triangulo (1)" class=" wp-image-32 aligncenter" height="221" src="https://www.ilafox.com.br/ricbit/images/triangulo-1.jpg" width="330" /></a></div>
<br />
Mas a sacada do Gauss é que você pode <em>torcer</em> o triângulo sem mudar a soma de seus ângulos! Torcer o triângulo sem esticar é a mesma coisa que transladar cada ponto para um lugar diferente.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/torcido.jpg"><img alt="torcido" class=" wp-image-34 aligncenter" height="273" src="https://www.ilafox.com.br/ricbit/images/torcido.jpg" width="346" /></a></div>
<br />
Mas peraí, se você torceu o triângulo, então você mudou as distâncias entre os pontos! Por exemplo, nessa imagem, fica claro que o caminho entre os vértices indicados é menor que do seria se o triângulo não estivesse torcido!
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/tri-fly.jpg"><img alt="tri-fly" class=" wp-image-35 aligncenter" height="279" src="https://www.ilafox.com.br/ricbit/images/tri-fly.jpg" width="325" /></a></div>
<br />
Mas para o Gauss, o que importa é a distância que uma formiga faria se estivesse andando no seu triângulo. Formiga não voa, ela precisa andar pelo papel, e por isso a distância que ela percorre é a mesma.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/tri-formiga.jpg"><img alt="tri-formiga" class=" wp-image-36 aligncenter" height="279" src="https://www.ilafox.com.br/ricbit/images/tri-formiga.jpg" width="325" /></a></div>
<br />
Com isso, você consegue mostrar que a curvatura do plano é diferente da curvatura da esfera. Eu tentei colocar o meu triângulo numa bolinha roubada da <a href="https://www.youtube.com/watch?v=-k6XYai0bR8">gata Luvinha</a>, e olhe o que acontece: se eu tento forçar um dos lados do triângulo a ficar na esfera, o outro vértice vai para fora.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/bolinha11.jpg"><img alt="bolinha1" class=" wp-image-38 aligncenter" height="304" src="https://www.ilafox.com.br/ricbit/images/bolinha11.jpg" width="338" /></a></div>
<br />
E se eu tento forçar todos os vértices a ficarem na bola, o papel amassa. Claramente, a distância de formiga entre dois pontos na esfera é sempre menor ou igual à distância de formiga sobre o plano, não importando como eu tento encaixar o plano.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/enruga1.jpg"><img alt="enruga" class="size-full wp-image-40 aligncenter" height="298" src="https://www.ilafox.com.br/ricbit/images/enruga1.jpg" width="391" /></a></div>
<br />
Mas a parte menos intuitiva é que eu posso colocar o triângulo sobre uma latinha de Schweppes sem amassar o papel! A lateral do cilindro tem curvatura igual à do plano, então os ângulos dos triângulos sobre o cilindro somam sempre 180 graus.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/schweppes1.jpg"><img alt="schweppes" class=" wp-image-42 aligncenter" height="366" src="https://www.ilafox.com.br/ricbit/images/schweppes1.jpg" width="343" /></a></div>
<br />
Isso tem uma conseqüência prática interessante. Como a curvatura intrínseca do cilindro é igual à do plano, eu posso colocar um rótulo de Coca Zero na mesa sem amassar.
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/cocazero.jpg"><img alt="cocazero" class=" wp-image-45 aligncenter" height="262" src="https://www.ilafox.com.br/ricbit/images/cocazero.jpg" width="494" /></a></div>
<br />
Mas não dá para fazer isso com a superfície de um globo! Por isso, todo mapa-mundi necessariamente introduz algum tipo de distorção, como a <a href="http://en.wikipedia.org/wiki/Mercator_projection">projeção de Mercator</a>, que distorce horrivelmente a Antártica e a Groenlândia:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/mercator.jpg"><img alt="mercator" class=" wp-image-43 aligncenter" height="316" src="https://www.ilafox.com.br/ricbit/images/mercator-620x526.jpg" width="372" /></a><br />
<br /></div>
<h3 style="text-align: left;">
A Redação de Riemann</h3>
<br />
Agora já temos como entender o que o Riemann escreveu na sua redação. O título era "<i>Über die Hypothesen welche der Geometrie zu Grunde liegen</i>" (Sobre as hipóteses que formam a base da geometria), e tem uma <a href="http://www.emis.de/classics/Riemann/WKCGeom.pdf">tradução online</a> para quem quiser ler. Note que não é um paper, é uma redação mesmo! Tem só uma fórmula e o resto é praticamente só texto.<br />
<br />
A idéia principal do Riemann é a seguinte: segundo o Lobachevsky e o Bolyai, existem geometrias não-Euclideanas onde o espaço é curvo. Já segundo o Gauss, a curvatura do espaço depende da distância entre os pontos. Então podemos usar a função que mede distâncias para <em>definir</em> a geometria usada.<br />
<br />
Por exemplo, na geometria clássica de Euclides, a distância entre dois pontos é dada pelo teorema de Pitágoras, cuja versão diferencial é a abaixo:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/riemann1.png"><img alt="dS^2 = dx^2 + dy^2 + dz^2" class="size-full wp-image-46 aligncenter" height="19" src="https://www.ilafox.com.br/ricbit/images/riemann1.png" width="163" /></a><br />
<br /></div>
Mas eu posso extrapolar essa fórmula, por exemplo, adicionando outros tipos de dependências:
<br />
<br />
<div style="text-align: center;">
<a href="https://www.ilafox.com.br/ricbit/images/riemann2.png"><img alt="\begin{align*} dS^2 = &g_{11}\, dx^2 + g_{12}\, dx\, dy + g_{13}\, dx\, dz \\ &g_{21}\, dy\, dx + g_{22}\, dy^2 + g_{23} \,dy\, dz \\ &g_{31}\, dz\, dx + g_{32}\, dz\, dy + g_{33}\, dz^2 \\ \end{align*}" class="size-full wp-image-47 aligncenter" height="73" src="https://www.ilafox.com.br/ricbit/images/riemann2.png" width="261" /></a><br />
<br /></div>
Cada um dos g<sub>ij</sub> pode ser um número qualquer, ou até uma função qualquer! Quando escrito em forma tensorial, essa equação acima define o <a href="http://en.wikipedia.org/wiki/Metric_tensor">tensor métrico de Riemann</a>. E a partir dele podemos derivar todas as outras características da geometria, incluindo a curvatura intrínseca em cada ponto.<br />
<br />
Quando o Gauss leu a redação, quase esboçou um meio sorriso! (Diz a lenda que essa foi a única vez que Gauss elogiou um aluno em público. Era marrento esse Gauss). O Riemann foi admitido como professor, e trabalhou em diversas áreas, incluindo teoria dos números, onde enunciou a lendária <a href="http://en.wikipedia.org/wiki/Riemann_hypothesis">hipótese de Riemann</a>, um problema que está em aberto até hoje (e paga um milhão de dólares para quem resolver).<br />
<br />
Mas, para mim, a parte mais profética da redação do Riemann são os parágrafos finais. Eu vou traduzir esse trecho final:<br />
<br />
"<em>Supondo que os corpos existem independentemente da posição, então a curvatura é constante em todo ponto, e segue pelas medidas astronômicas que não pode ser diferente de zero, ou ao menos que seu inverso seja uma área tal que o alcance de nossos telescópios seja desprezível. Mas se essa independência dos corpos da posição não existe, então não podemos tirar conclusões das relações métricas entre distâncias grandes e infinitamente pequenas; nesse caso a curvatura em cada ponto pode ter um valor arbitrário em três dimensões, desde que a curvatura total de cada porção mensurável do espaço não seja muito diferente de zero.</em>"<br />
<br />
É isso. Em 1854, o Riemann cantou a bola da <a href="http://en.wikipedia.org/wiki/General_relativity">Relatividade Geral</a>. Só faltou o Einstein deduzir, sessenta anos depois, que a tal curvatura variável em cada ponto era uma função da massa, e daí segue naturalmente que a gravidade é uma deformação do espaço-tempo. Nada mal para o menino que saiu aos 14 anos de casa para estudar!<br />
<br /></div>
</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-11174992840647575122013-12-24T21:30:00.001-03:002013-12-24T21:30:23.185-03:00Mande Mais Dinheiro usando MIP<div dir="ltr" style="text-align: left;" trbidi="on">
Era uma vez, há mais de cem anos atrás, uma família de matemáticos. O avô era matemático, o pai era matemático, e o filho mudou de cidade para estudar matemática na melhor universidade do país. O pai lhe deu algum dinheiro para pagar o aluguel enquanto estivesse estudando, mas ele não contava com os preços altos. Naquela cidade, o dinheiro que ele tinha não era suficiente para alugar um quartinho.<br />
<br />
O filho então precisava mandar uma mensagem para o pai, pedindo mais dinheiro. Naquela época, o meio de comunicação mais rápido era o telégrafo, mas você pagava por cada letra enviada. Por isso, a mensagem tinha que ser bastante sucinta. Depois de pensar um pouco, mandou para o pai a mensagem "SEND+MORE=MONEY".<br />
<br />
Ao receber a mensagem, primeiro o pai ficou perplexo. Quanto dinheiro ele precisava mandar? Depois de pensar um pouco, ele logo concluiu: "Ah, eu preciso mandar $10652 para ele!".<br />
<br />
Como o matemático pai chegou a essa conclusão?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR7PDxhWB1vO7VpSsfm-9pQqO2eyB5kpM8ef7sxuM1ZWDplVdJkG5upCc9zb526XLdjw9BpHidLv4cZNjTLiJXuLXAe8xYLdnT0B5nYKvVCzz1EvWJQ_7w3chXa5IvQqLKtUPPo3aALcU/s1600/matematico.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR7PDxhWB1vO7VpSsfm-9pQqO2eyB5kpM8ef7sxuM1ZWDplVdJkG5upCc9zb526XLdjw9BpHidLv4cZNjTLiJXuLXAe8xYLdnT0B5nYKvVCzz1EvWJQ_7w3chXa5IvQqLKtUPPo3aALcU/s320/matematico.jpg" width="320" /></a></div>
<br />
Esse problema é um exemplo de <a href="http://en.wikipedia.org/wiki/Verbal_arithmetic">Criptoaritmética</a>. A solução é <b>trocar as letras por números</b>, de tal maneira que a cada letra corresponda um único número, e de modo que a adição resultante seja verdadeira. No caso descrito acima, só existe uma solução válida, que é M=1, Y=2, E=5, N=6, D=7, R=8, S=9 e O=0.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/npy7yxr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/npy7yxr.png" /></a>
<a href="http://mathurl.com/owun75r.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/owun75r.png" /></a></div>
<br />
Você pode achar essa solução usando apenas deduções lógicas. Por exemplo, M precisa ser 1 porque o carry (vai-um) da primeira coluna nunca pode ser maior que um. Com raciocínios similares você descobre os números restantes.<br />
<br />
Resolver problemas assim é bastante divertido! Meu primeiro contato com Criptoaritmética foi na revista <a href="http://www.datacassete.com.br/revistas/detail/micro-sistemas-n-46/57.html">Micro Sistemas #46</a>, de julho de 85, onde eles apresentam um gerador de criptogramas, para que você possa resolvê-los na mão. Na época eu era viciado, gostava tanto que até cheguei a <a href="https://github.com/ricbit/Oldies/blob/master/1993-07-cryptarithm/cripto.pas">portar esse gerador para Pascal</a> já na época dos PCs.<br />
<br />
Já na revista <a href="http://www.datacassete.com.br/revistas/detail/msx-micro-n-15/57.html">MSX Micro #15</a>, de maio de 88, um desafio mais difícil foi proposto pelo <a href="http://www.ricbit.com/2008/06/um-cientista-em-minha-vida.html">Nabor Rosenthal</a>: escrever um programa de computador que <b>resolva sozinho</b> os criptogramas!<br />
<br />
Como fazer um programa assim? Existem três abordagens imediatas. A primeira é usar <b>força bruta</b> para enumerar todas as combinações entre letras e números. Cada uma das 8 letras pode assumir 10 valores distintos, então o tamanho do espaço de estados é 8**10 = 1073741824 combinações.<br />
<br />
A segunda abordagem é notar que duas letras não podem ter o mesmo número, então você não precisa visitar todos os estados, só aqueles que contém <b>permutações</b> dos oito dígitos escolhidos. Assim, o espaço fica apenas com binomial(10, 8)*fatorial(8) = 1814400 combinações, quase mil vezes menor que o anterior.<br />
<br />
A terceira é usar <b>backtracking</b> para eliminar da árvore de busca as combinações inválidas. Por exemplo, se você já determinou que <b>D</b>=7 e <b>E</b>=5, então <b>Y</b> precisa necessariamente ser 2 porque é a única possibilidade que faz a última coluna ficar correta. Essa opção é mais complicada de implementar, mas é ainda mais rápida que a anterior.<br />
<br />
Nesse ano eu fiz um curso de <a href="https://www.coursera.org/course/optimization">Discrete Optimization</a> no Coursera, e aprendi um método novo para resolver criptogramas. Porém, antes de explicar esse método novo, vale a pena fazer uma pausa para estudar um problema mais simples.<br />
<br />
<h3 style="text-align: left;">
O problema da dieta</h3>
<br />
Enquanto o dinheiro do pai não chegava, o estudante estava dormindo de hóspede numa fazenda da região. Embora ele não pagasse nada para dormir no celeiro, ele ainda tinha que comprar comida. E só tinham três comidas disponíveis para comprar: espigas de milho, pães e copos de leite. Dado que ele precisa de pelo menos 2000 calorias por dia, e 5000 unidades de vitamina A, qual é o menor custo diário que ele pode ter para conseguir todos os nutrientes que precisa?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoiVX5M98LKlRKIKy9FE9qCXh3OgFEa57MOqMHtBYqnpYTGHvALWbYKG92xONJXQmOpoWNwc5W3ZGHdakEPB_BJZqJO-AaDTCKsHRoJyssaltzdjaalsYIaHRMnXXda9FgjmIsH_8nBXA/s1600/mercearia+(1).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="209" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoiVX5M98LKlRKIKy9FE9qCXh3OgFEa57MOqMHtBYqnpYTGHvALWbYKG92xONJXQmOpoWNwc5W3ZGHdakEPB_BJZqJO-AaDTCKsHRoJyssaltzdjaalsYIaHRMnXXda9FgjmIsH_8nBXA/s320/mercearia+(1).jpg" width="320" /></a></div>
<br />
<center>
<table border="1">
<tbody>
<tr><td><b>Porção</b></td><td><b>Calorias</b> (kcal)</td><td><b>Vitamina A </b>(IU)</td><td><b>Preço</b></td></tr>
<tr><td>Espiga de milho</td><td>72</td><td>107</td><td>$ 0.08</td></tr>
<tr><td>Copo de leite</td><td>121</td><td>500</td><td>$ 0.23</td></tr>
<tr><td>Pão</td><td>65</td><td>0</td><td>$ 0.05</td></tr>
</tbody></table>
</center>
<br />
Esse problema pode ser expresso através de uma série de inequações:<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both; text-align: left;">
<a href="http://mathurl.com/kmerxv2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/kmerxv2.png" /></a></div>
<br />
Na literatura, problemas com esse formato são estudados em <a href="http://en.wikipedia.org/wiki/Linear_programming">Programação Linear</a>, e existem vários algoritmos eficientes para resolvê-los, como os <a href="http://en.wikipedia.org/wiki/Interior_point_method">métodos de Ponto Interior</a>, que podem ser implementados de forma a garantir uma solução em tempo polinomial. Na prática, usa-se mais o <a href="http://en.wikipedia.org/wiki/Simplex_algorithm">Simplex</a>, que tem um pior caso ruim, mas também é polinomial no caso médio.<br />
<br />
Usando essas técnicas para resolver o problema, temos que a melhor solução é comprar <b>17.13 espigas</b> e <b>6.33 copos de leite</b>, gastando um total de <b>$2.82</b>.<br />
<br />
Mas agora tem um problema: eles não vendem 17.13 espigas, a quantidade precisa ser um inteiro. Ou você compra 17 espigas, ou compra 18. Fácil de resolver, é só arredondar para cima né? Leva-se <b>18 espigas</b> e <b>7 copos de leite</b>, gastando <b>$3.05</b>, e garantindo todos os nutrientes que você precisa.<br />
<br />
Porém, se você fizer isso, você vai perder dinheiro! Pode conferir: se você comprar <b>10 espigas</b>, <b>8 copos de leite</b>, e <b>5 pães</b>, vai gastar só <b>$2.89</b>, e continuar garantindo o mínimo de nutrientes diários. Os métodos de programação linear <b>não funcionam</b> quando os valores precisam ser inteiros!<br />
<br />
Uma maneira de chegar a esse resultado é quebrando o problema em dois. Se a sua solução tem um valor fracionário, como 17.13, então você roda o Simplex mais duas vezes: na primeira adicionando a inequação M≤17, e na segunda, M≥18. Se o resultado tiver só variáveis inteiras, beleza, está resolvido. Senão, você pega a variável fracionária e quebra em dois de novo. Esse método na literatura é a <a href="http://en.wikipedia.org/wiki/Integer_programming">Programaçao Inteira Mista</a> (ou apenas MIP).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH5nicyS9dQBxfdHJRwZRyk4vhM5XrELRewEUPFR7p5F2Psdyk-fHvl-xf9SjPbsx3534V8f_ATOVT-trqALSwAL-TZBj503gFEmJYgdeUQWjfYsejZpsapgLnkUa6TTvfv7hr7Zmeks4/s1600/diet.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgH5nicyS9dQBxfdHJRwZRyk4vhM5XrELRewEUPFR7p5F2Psdyk-fHvl-xf9SjPbsx3534V8f_ATOVT-trqALSwAL-TZBj503gFEmJYgdeUQWjfYsejZpsapgLnkUa6TTvfv7hr7Zmeks4/s200/diet.png" width="200" /></a></div>
<br />
Opa! Peraí! Os métodos de programação linear eram <b>polinomiais</b>, e abrindo uma árvore em cada variável nós transformamos o método em <b>exponencial</b>. É uma perda de performance inaceitável!<br />
<br />
Pois é, mas não tem jeito. Embora a programação linear seja polinomial, a programação inteira é <b>NP-hard</b>. Se você achar um método polinomial para resolver esse problema, então você também provou que <b>P=NP</b>.<br />
<br />
Nem tudo está perdido, entretanto. Existem vários métodos para otimizar a busca, de modo que você não precise visitar todos os nós da árvore. Por exemplo, você pode usar <a href="http://en.wikipedia.org/wiki/Branch_and_bound">branch and bound</a>, <a href="http://en.wikipedia.org/wiki/Branch_and_cut">branch and cut</a>, <a href="http://en.wikipedia.org/wiki/Cutting-plane_method">Gomory cuts</a>, e mais inúmeros truques que deixam o número de nós baixos o suficiente para o método ser viável na prática.<br />
<br />
Implementar esses métodos pode ser bem difícil. A boa notícia é que você não precisa fazê-lo! Existem inúmeras bibliotecas prontas que implementam tudo isso para você, em quase todas as linguagens do mercado. A minha preferida é a <a href="http://scip.zib.de/">SCIP</a>, que é open source e bastante competitiva com as soluções comerciais. O único problema dela é que o público-alvo são os pesquisadores da área, então a API é bem complexa e intimidadora para quem está começando agora. Se você quer começar agora a brincar com MIP, eu tenho uma alternativa melhor.<br />
<br />
<h3 style="text-align: left;">
Usando EasySCIP</h3>
<br />
Já que a SCIP é boa, mas é difícil de usar, meu primeiro instinto foi escrever uma abstração sobre ela que torne a programação mais simples. E assim surgiu o <a href="https://github.com/ricbit/easyscip">EasySCIP</a>, um binding em C++ que é fácil de usar. Para mostrar como é simples, vamos implementar o problema da dieta em EasySCIP.<br />
<br />
Primeiro você cria uma instância do EasySCIP:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">MIPSolver solver;
</pre>
</div>
<br />
Depois, cria as variáveis, uma para cada tipo de alimento. Na criação da variável, você especifica os limites onde ela pode variar (digamos, de 0 a 1000). Além disso, você passa o coeficiente dessa variável na função que vai ser minimizada (no nosso caso, é o preço de cada item):<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">Variable corn <span style="color: #333333;">=</span> solver.integer_variable(<span style="color: #0000dd; font-weight: bold;">0</span>, <span style="color: #0000dd; font-weight: bold;">1000</span>, <span style="color: #6600ee; font-weight: bold;">0.08</span>);
Variable milk <span style="color: #333333;">=</span> solver.integer_variable(<span style="color: #0000dd; font-weight: bold;">0</span>, <span style="color: #0000dd; font-weight: bold;">1000</span>, <span style="color: #6600ee; font-weight: bold;">0.23</span>);
Variable bread <span style="color: #333333;">=</span> solver.integer_variable(<span style="color: #0000dd; font-weight: bold;">0</span>, <span style="color: #0000dd; font-weight: bold;">1000</span>, <span style="color: #6600ee; font-weight: bold;">0.05</span>);
</pre>
</div>
<br />
Agora você adiciona as inequações (constraints). Para cada inequação, você passa o coeficiente da variável, e os limites inferiores e superiores. No nosso caso, só nos importam os limites inferiores, então eu chutei um valor alto para o superior. A primeira inequação fica assim:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">Constraint calories <span style="color: #333333;">=</span> solver.constraint();
calories.add_variable(corn, <span style="color: #0000dd; font-weight: bold;">72</span>);
calories.add_variable(milk, <span style="color: #0000dd; font-weight: bold;">121</span>);
calories.add_variable(bread, <span style="color: #0000dd; font-weight: bold;">65</span>);
calories.commit(<span style="color: #0000dd; font-weight: bold;">2000</span>, <span style="color: #0000dd; font-weight: bold;">200000</span>);
</pre>
</div>
<br />
Na segunda inequação você nem precisa adicionar a variável do pão, porque ele não tem nenhuma vitamina A.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">Constraint vitamin_a <span style="color: #333333;">=</span> solver.constraint();
vitamin_a.add_variable(corn, <span style="color: #0000dd; font-weight: bold;">107</span>);
vitamin_a.add_variable(milk, <span style="color: #0000dd; font-weight: bold;">500</span>);
vitamin_a.commit(<span style="color: #0000dd; font-weight: bold;">5000</span>, <span style="color: #0000dd; font-weight: bold;">500000</span>);
</pre>
</div>
<br />
Agora é só resolver e ler os valores finais:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">Solution sol <span style="color: #333333;">=</span> solver.solve();
cout <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"Corn: "</span> <span style="color: #333333;"><<</span> sol.value(corn) <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"</span><span style="background-color: #fff0f0; color: #666666; font-weight: bold;">\n</span><span style="background-color: #fff0f0;">"</span>;
cout <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"Milk: "</span> <span style="color: #333333;"><<</span> sol.value(milk) <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"</span><span style="background-color: #fff0f0; color: #666666; font-weight: bold;">\n</span><span style="background-color: #fff0f0;">"</span>;
cout <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"Bread: "</span> <span style="color: #333333;"><<</span> sol.value(bread) <span style="color: #333333;"><<</span> <span style="background-color: #fff0f0;">"</span><span style="background-color: #fff0f0; color: #666666; font-weight: bold;">\n</span><span style="background-color: #fff0f0;">"</span>;
</pre>
</div>
<br />
O programa completo você pode <a href="https://github.com/ricbit/easyscip/blob/master/diet.cc">ver no github</a>. Rodando o programa, ele retorna a solução que esperávamos:<br />
<br />
<pre>Corn: 10
Milk: 8
Bread: 5
</pre>
<div>
<br /></div>
<div>
Agora já temos as ferramentas que precisávamos para resolver o problema original!<br />
<br />
<h3 style="text-align: left;">
Criptoaritmética com MIP</h3>
<div>
<br /></div>
MIP é uma ferramenta excelente para resolver puzzles. Tudo que você precisa fazer é arrumar uma maneira de transformar seu puzzle em um sistema de inequações; feito isso, o framework faz todo o serviço sujo para você.<br />
<br />
O problema então deixa de ser programação e passa a ser modelagem. Qual o melhor modelo que representa seu puzzle? Um truque que funciona na maioria das vezes é introduzir <b>variáveis binárias</b> que codificam as informações do seu puzzle.<br />
<br />
Por exemplo, a letra <b>S</b> vai ser associada a um dos dígitos de 0 a 9, mas ainda não sabemos qual. Vamos então introduzir dez variáveis binárias: <b>b<sub>S0</sub></b>, <b>b<sub>S1</sub></b>, .., <b>b<sub>S9</sub></b>. Cada uma deles pode assumir o valor 0 ou 1. Por exemplo, se na solução final descobrirmos que <b>S</b>=9, então teremos <b>b<sub>S9</sub></b>=1, e as outras nove variáveis são zero.<br />
<br />
Mas sabemos que a letra S precisa estar associada a um dígito, e não mais que um. Isso pode ser expresso como a nossa primeira equação:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/o493wln.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/o493wln.png" /></a></div>
<br /></div>
Confira: para a equação ficar correta, se uma das variáveis for 1, então todas as outras precisam ser 0. (Os mais atentos podem reclamar que eu usei uma <i>equação</i> ao invés de uma <i>inequação</i>, mas não tem problema; toda equação da forma <b>A</b>=<b>k</b> pode ser escrita como duas inequações encadeadas: <b>k</b> ≤ <b>A</b> ≤ <b>k</b>).<br />
<br />
Repetindo uma equação dessas para cada letra, nós garantimos que a cada letra corresponde um dígito. Mas isso não é suficiente, você também precisa garantir o oposto: a cada dígito precisa corresponder uma letra. Sem essa condição adicional, você poderia ter uma solução do tipo <b>S</b>=9 e <b>E</b>=9 (mas não podemos ter o mesmo dígito em duas letras distintas).<br />
<br />
A solução é parecida com a anterior. Para o dígito 9, por exemplo, teremos:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/kskwdnc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/kskwdnc.png" /></a></div>
<br />
Note que temos apenas oito letras distintas para dez dígitos, então pelo menos dois dígitos vão sobrar. Como não temos como garantir qual dígito será utilizado, usamos uma inequação ao invés de uma equação.<br />
<br />
Outra regra que precisa ser modelada é que nenhum número pode começar com zero. Isso é o mesmo que dizer que as letras S e M não podem ser zero. Mais duas equações resolvem isso:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/mjdq8hb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/mjdq8hb.png" /></a></div>
<br />
Agora falta só modelar a soma em si. Antes de começar, vale notar que o problema tem quatro variáveis binárias escondidas que você precisa levar em conta: os <b>carrys</b> de cada coluna.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/jvjfofp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/jvjfofp.png" /></a></div>
<br />
Agora basta escrever uma equação por coluna. Para a coluna mais à direita, por exemplo, teríamos algo do tipo <b>D</b>+<b>E</b>=<b>Y</b>+10<b>c<sub>0</sub></b> em decimal. Usando as nossas variáveis binárias, a equação completa fica:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/o3xw9lr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/o3xw9lr.png" /></a></div>
<br />
Parece assustador, mas é só uma equação linear simples. Fazendo mais uma dessas para cada coluna do nosso puzzle, o modelo está quase completo: falta só uma função objetivo. Se vamos usar MIP, precisamos minimizar alguma coisa. Mas o que minimizar? Nosso modelo já está completo sem a necessidade de minimizar nada, então podemos fornecer uma função constante para ele minimizar. Na linguagem da área, nós estamos buscando <b>feasibility</b>, e não <b>optimality</b>.<br />
<br />
Com todas as inequações prontas, podemos rodar o modelo no EasySCIP. O código completo <a href="https://github.com/ricbit/easyscip/blob/master/sendmoremoney_binary.cc">está no github</a>, e, após rodá-lo em meu computador, tive a resposta em <b>0.116s</b>, o que é bastante respeitável! Mas ainda dá para melhorar :)<br />
<br />
<h3 style="text-align: left;">
Criptoaritmética com MIP, segundo modelo</h3>
<br />
Eu sei que gostar de resolver criptoaritmética parece coisa da maluco, mas eu não sou o único maluco nesse mundo, tem o Knuth também! No volume 4A do <a href="http://en.wikipedia.org/wiki/The_Art_of_Computer_Programming">Art of Computer Programming</a>, ele dá uma dica muito boa para resolver criptogramas: a análise das <b>assinaturas</b>. Vamos isolar a letra <b>E</b> no puzzle original, trocando os lugares onde ela aparece por 1, e onde não aparece por 0:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/npy7yxr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://mathurl.com/npy7yxr.png" /></a> <a href="http://mathurl.com/lz5scez.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://mathurl.com/lz5scez.png" /></a></div>
<br />
Após a substituição, nós obtemos a assinatura da letra <b>E</b>, que vale 100+1-10, ou seja, <b>91</b>. A sacada do Knuth é que, na solução final, a soma das assinaturas precisa necessariamente valer zero:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/o9ftp4a.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/o9ftp4a.png" /></a></div>
<br />
Usando variáveis inteiras ao invés de variáveis binárias, poderíamos colocar essa equação diretamente no modelo, e aí não precisaríamos das equações de cada coluna! Mas podemos fazer ainda melhor: o MIP sempre quer uma função para minimizar, então podemos mandar ele minimizar a soma das assinaturas. Assim, os métodos de branch and bound implementados pelo SCIP podem jogar fora mais nós da árvore durante o processamento, o que acelera a busca.<br />
<br />
O único problema é ele resolver minimizar tanto a soma das assinaturas, que ela acabe ficando negativa. Para evitar isso, é só adicionar essa restrição manualmente. Por exemplo, ele pode minimizar a variável <b>X</b>, onde <b>X</b> satisfaz:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/ozfaoz7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/ozfaoz7.png" /></a></div>
<br />
Ainda precisamos adicionar as regras de que a cada letra corresponde um único dígito e vice-versa. Vamos manter as variáveis binárias do modelo anterior, e ensinar o modelo a converter entre variáveis binárias e variáveis inteiras. Por exemplo, a letra D ficaria:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/peftn9y.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/peftn9y.png" /></a></div>
<br />
Com isso o modelo está completo. O código <a href="https://github.com/ricbit/easyscip/blob/master/sendmoremoney_integer.cc">está no github</a>, e rodando no EasySCIP, ele chega no mesmo resultado que o modelo anterior, porém usando apenas <b>0.052s</b>, quase metade do tempo!<br />
<br />
<h3 style="text-align: left;">
Conclusão</h3>
<br />
Quando eu devo usar MIP ao invés de outros métodos, como backtracking? O backtracking vai criar uma divisão na árvore de busca cada vez que definir o valor de uma variável, mas o MIP roda uma iteração de programação linear em cada nó, o que potencialmente pode definir várias variáveis ao mesmo tempo. A minha experiência é que o MIP ganha sempre que você conseguir criar um modelo que permita essa otimização global.<br />
<br />
Como comparação, lembre que a força bruta examinava <b>1073741824</b> nós, e a análise de permutações visitava <b>1814400</b> nós. Olhando no log do EasySCIP, eu vejo que o primeiro modelo com variáveis binárias visitou apenas <b>9</b> nós, e o segundo modelo com assinaturas resolveu tudo em <b>um único</b> nó! A programação linear achou a solução inteira sem nenhum branch! Pelos números, é bem claro que vale a pena aprender a escrever modelos MIP :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com0tag:blogger.com,1999:blog-6306509703738480474.post-59196785150026263712013-01-13T16:45:00.000-03:002013-01-15T20:58:42.346-03:00O Jogo do Pi<div dir="ltr" style="text-align: left;" trbidi="on">
No último post eu falava de <a href="http://blog.ricbit.com/2013/01/estimule-sua-criatividade-2.html">variações em jogos</a>, aí lembrei de uma variação divertida em um jogo bem conhecido. Quem assistia <a href="http://pt.wikipedia.org/wiki/Topa_Tudo_por_Dinheiro">Topa Tudo Por Dinheiro</a> certamente deve se lembrar do <a href="http://www.youtube.com/watch?feature=player_detailpage&v=SlY6MkdpAFU#t=26s">Jogo do Pim</a>. O objetivo é enumerar os naturais pelo maior tempo possível, trocando os múltiplos de quatro pela palavra "<i>pim</i>":<br />
<br />
<div style="text-align: center;">
1,2,3,<i>pim</i>, 5,6,7,<i>pim</i>, 9,10,11,<i>pim</i>...</div>
<div style="text-align: center;">
<br /></div>
Parece simples, mas na prática muita gente se confundia e errava antes mesmo de chegar ao quarenta!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaGZp_SCoAl1-XKvIlwla2EvFm0QyPNOcIX39Cp9FlbFTvPy9MBx8Bx1IQeCRy7UItlLGN2ex0Ciwnf4PAvrGGDdfQKmyUPsimJ-B5rRfwFOEbuU2SZE_k5MTdLZ9CF23nAOwPM5jRHf8/s1600/jogo_pi.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaGZp_SCoAl1-XKvIlwla2EvFm0QyPNOcIX39Cp9FlbFTvPy9MBx8Bx1IQeCRy7UItlLGN2ex0Ciwnf4PAvrGGDdfQKmyUPsimJ-B5rRfwFOEbuU2SZE_k5MTdLZ9CF23nAOwPM5jRHf8/s400/jogo_pi.jpg" width="400" /></a></div>
<br />
Uma variação mais difícil desse jogo foi inventada pelo <a href="http://jucablues.blogspot.com.br/">Juca</a> uns anos atrás: o <b>Jogo do Pi</b>. Dessa vez você ainda precisa enumerar os naturais, mas precisa falar "π" toda vez que passar um múltiplo inteiro de pi. Os primeiros múltiplos inteiros são aproximadamente 3.14, 6.28 e 9.42, então a sequência começa assim:<br />
<br />
<div style="text-align: center;">
1,2,3,<span style="text-align: left;">π</span>, 4,5,6,<span style="text-align: left;">π</span>, 7,8,9,<span style="text-align: left;">π</span>...</div>
<br />
Parece o mesmo jogo né? Você conta três números, fala π, conta mais três, fala π, e assim por diante. Mas se você seguir essa estratégia, vai perder! Olha o que acontece se você continuar:<br />
<br />
<div style="text-align: center;">
..., 16,17,18,π, 19,20,21,π, 22,23,24,25,π,...</div>
<br />
A estratégia de contar de três em três falha! Entre 7π e 8π tem quatro naturais ao invés de só três.<br />
<br />
Bem, será que não dá pra melhorar a estratégia? Podemos contar quantos números tem entre os múltiplos de pi. Com sorte, o padrão é 3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>3<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;">‑</span>4 e depois repete. Nesse caso, o padrão do Jogo do Pi é oito vezes maior que o padrão do Jogo do Pim. Infelizmente, se você fizer as contas, esse padrão também é quebrado após 112<span style="text-align: center;">π.</span><br />
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">Será que devemos procurar um <b>padrão de padrões</b> então? Nessa altura não já compensa fazer as contas na mão, melhor deixar o python achar o período pra gente:</span><br />
<span style="text-align: center;"><br /></span>
<span style="text-align: center;"><a href="https://github.com/ricbit/Oldies/blob/master/2013-01-pigame/pigame.py">Script em python para tentar achar um período</a></span><br />
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">Más notícias: o script acha padrões maiores e maiores, sem parar. Não tem como concluir se existe ou não um padrão só analisando a saída do programinha.</span><br />
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">O jeito de resolver essa dúvida, então, é usando matemática! Se você tem medo de teoria dos números, pule o quadro azul:</span><br />
<span style="text-align: center;"><br /></span>
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Antes de mais nada, vamos colocar nosso problema na notação correta. Nós queremos descobrir se a quantidade de números entre múltiplos inteiros consecutivos de pi formam uma sequência periódica. Vamos primeiro escrever a sequência explicitamente:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/avgd8y4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/avgd8y4.png" /></a></div>
<br />
Isso gera a sequência que queremos. Agora vamos supor que essa sequência tem um período <b>p</b>. Das duas uma: ou a gente acha o valor de <b>p</b>, ou batemos em uma contradição pelo caminho.<br />
<br />
Se nós somarmos todos os primeiros <b>n</b> termos dessa sequência, temos uma soma telescópica e um resultado curto:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/akdhddt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/akdhddt.png" /></a></div>
<br />
Isso vale para todo <b>n</b>, inclusive no caso onde <b>n</b> é um múltiplo inteiro do período <b>p</b>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/c68wdeu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/c68wdeu.png" /></a></div>
<br />
Mas, nesse caso, podemos fazer a somatória de outro jeito. Ao invés de somar direto de <b>0</b> a <b>kp</b>, podemos somar <b>k</b> vezes o período <b>p. </b>Aí, como a sequência é periódica, um dos termos some:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/a67xkhn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/a67xkhn.png" /></a></div>
<br />
O termo mais interno da somatória não varia mais com <b>i</b>, então essa somatória está somando <b>k</b> vezes a mesma coisa:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/b6omosx.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/b6omosx.png" /></a></div>
<br />
A conclusão é que, quando <b>p</b> é o período da sequência, podemos jogar um <b>k</b> inteiro de dentro para fora do piso. Podemos agora abrir a definição de piso para <b>kp</b><span style="text-align: center;"><b>π</b>:</span><br />
<span style="text-align: center;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/a46so38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/a46so38.png" /></a></div>
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">Dividindo todo mundo por <b>kp</b>, temos:</span><br />
<span style="text-align: center;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/a54f3nn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/a54f3nn.png" /></a></div>
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">Agora, para todo real </span><span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;"><span lang="el" xml:lang="el"><b>ε </b></span></span><span style="text-align: center;"><b>> 0</b>, podemos escolher um <b>k</b> grande o suficiente tal que </span><span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19.200000762939453px;"><span lang="el" xml:lang="el"><b>ε</b> </span></span><span style="text-align: center;">seja maior que <b>1/kp</b>. Logo:</span><br />
<span style="text-align: center;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/c8xftns.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/c8xftns.png" /></a></div>
<span style="text-align: center;"><br /></span>
<span style="text-align: center;">Pelo <a href="http://en.wikipedia.org/wiki/Squeeze_theorem">teorema do sanduíche</a>, concluímos que:</span><br />
<span style="text-align: center;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/d3qe6ky.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/d3qe6ky.png" /></a></div>
<div style="text-align: left;">
<span style="text-align: center;"><br /></span></div>
<div style="text-align: left;">
Note que o numerador dessa função é um piso, logo é um inteiro. Sabemos também que <b>p</b> é um inteiro, logo a fração é um racional. Mas <a href="http://en.wikipedia.org/wiki/Proof_that_%CF%80_is_irrational">pi é irracional</a>, portanto chegamos a uma contradição, e a sequência não tem período.</div>
</div>
<br />
Ou seja, concluímos matematicamente que o Jogo do Pi não tem período. Outra maneira de dizer a mesma coisa é que o período do Jogo do Pi é infinito, e portanto o Jogo do Pi é <b>infinitamente mais díficil</b> que o Jogo do Pim!<br />
<br />
<i>Agradecimentos ao povo do Math Stack Exchange pela ajuda na demonstração</i></div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com4tag:blogger.com,1999:blog-6306509703738480474.post-52301448492848147632013-01-06T23:59:00.000-03:002013-01-07T16:00:39.277-03:00Estimule sua Criatividade #2<div dir="ltr" style="text-align: left;" trbidi="on">
Nesse segundo post sobre criatividade, vou contar outra história do tempo do colégio técnico. As aulas regulares do curso de Eletrônica começavam à tarde, mas duas vezes por semana tinha aula de Educação Física às 7am. Entre essas aulas tinha um intervalo de várias horas, onde muita gente voltava para casa para almoçar.<br />
<br />
Eu ficava lá pelo colégio mesmo. Nesses intervalos sem aula eu adquiri vários hábitos, como ler ficção científica da <a href="http://www.editoraaleph.com.br/site/autores/isaac-asimov">Editora Aleph</a>, gibis do Conan e dos X-Men, Diários de Bordo da <a href="http://www.youtube.com/watch?v=q9txaAlXqlQ">Frota Estelar Brasileira</a>, <a href="http://pt.wikipedia.org/wiki/Aventuras_Fant%C3%A1sticas">livros-jogos</a> do Steve Jackson, jogar RPG (desde <a href="http://pt.wikipedia.org/wiki/Tagmar">Tagmar</a> até AD&D), ir ao fliperama da esquina jogar Street Fighter 2, enfim, aprendi tudo que não presta.<br />
<br />
E quando não tinha mais nada para fazer, eu jogava Jogo da Velha.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1u6eqt-PMZJQxO80QLV17Ts2v7sB1vi1yXm1bB_TgZw0IbpPjEm3Uf6yRFIrJ4f2aKSZ02wtfF8TA4ioe6BEVk_6si_y9hOeljQhvDxRKXVBkGHbS5NzRhtuAcOM0WGMXHke0CIqGRHs/s1600/jogodavelha.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1u6eqt-PMZJQxO80QLV17Ts2v7sB1vi1yXm1bB_TgZw0IbpPjEm3Uf6yRFIrJ4f2aKSZ02wtfF8TA4ioe6BEVk_6si_y9hOeljQhvDxRKXVBkGHbS5NzRhtuAcOM0WGMXHke0CIqGRHs/s320/jogodavelha.jpg" width="320" /></a></div>
<br />
<h3 style="text-align: left;">
O Jogo da Velha</h3>
<br />
Qualquer um que tenha se dedicado um pouquinho ao Jogo da Velha sabe que é um jogo chato. Se o primeiro jogador começar no centro, ele pode forçar um empate. Mas nós éramos sobreviventes do Atari, então conhecíamos o jogo <a href="http://en.wikipedia.org/wiki/3-D_Tic-Tac-Toe">3-D Tic-Tac-Toe</a>, criado pela <a href="http://en.wikipedia.org/wiki/Carol_Shaw_(video_game_designer)">Carol Shaw</a> (a programadora mulher do sexo feminino que também foi responsável pelo clássico <a href="http://en.wikipedia.org/wiki/River_Raid">River Raid</a>).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.mobygames.com/images/shots/l/72084-3-d-tic-tac-toe-atari-2600-screenshot-try-to-get-four-x-s-or.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.mobygames.com/images/shots/l/72084-3-d-tic-tac-toe-atari-2600-screenshot-try-to-get-four-x-s-or.gif" /></a></div>
<div style="text-align: center;">
<i>3-D Tic-Tac-Toe no Atari 2600</i></div>
<br />
O Jogo da Velha 3D é como o tradicional, mas agora você joga em um tabuleiro cúbico. O objetivo ainda é completar uma linha antes do seu oponente. Note que o jogo do Atari era 4x4x4, e não 3x3x3. No jogo 3x3x3, o primeiro jogador sempre pode forçar a vitória, então é uma variação sem graça.<br />
<br />
Depois de algum tempo jogando o Jogo da Velha 3D, tivemos uma idéia: se a versão 3D é mais divertida que a 2D, será que o <b>Jogo da Velha 4D</b> não seria mais legal ainda? Nós desenhamos um tabuleiro 4D no papel e, realmente, era divertido mesmo! Nós desenhávamos o tabuleiro a caneta e jogávamos com lápis, assim era só apagar com borracha para jogar de novo. No fim, ficamos tão viciados que os papéis rasgaram de tanto apagar!<br />
<br />
Hora da segunda idéia: os alunos de Eletrônica tinham acesso à sala dos computadores, cheio de PC-XTs rodando DOS. Eu aproveitei que tinha acabado de aprender Pascal, e fiz um tabuleiro eletrônico para jogar direto no computador, sem ter que ficar apagando o tabuleiro a cada jogo. (Você pode pegar esse source no github: <a href="https://github.com/ricbit/Oldies/blob/master/1993-08-tictactoe4d/4dvelha.pas">4dvelha.pas</a>)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-VyKUvIch8R9PC3kQCDqOrtv25vBDn8yaKC7knLfLsLmDExlQ9351E-7JH6K6CYNwKK6KgYD-tL2xy0Bw-HFFRHw-efF3iXqatMbvQetXhB_qrQgBOfbALestLudYXfTteVB2V_D1cUU/s1600/jogodavelha.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-VyKUvIch8R9PC3kQCDqOrtv25vBDn8yaKC7knLfLsLmDExlQ9351E-7JH6K6CYNwKK6KgYD-tL2xy0Bw-HFFRHw-efF3iXqatMbvQetXhB_qrQgBOfbALestLudYXfTteVB2V_D1cUU/s400/jogodavelha.png" width="400" /></a></div>
<div style="text-align: center;">
<i>Jogo da Velha 4D, rodando no PC-XT</i></div>
<br />
Essa versão nos divertiu um monte. Eventualmente tivemos outra idéia: por que não jogar o Jogo da Velha 5D? Essa idéia não foi para frente, depois de algumas partidas nós percebemos que o 5D tinha o mesmo problema do 3D, o primeiro jogador conseguia forçar a vitória.<br />
<br />
E o 6D? Um dos amigos usou o plotter do pai para desenhar um monstruoso tabuleiro de Jogo da Velha 6D, que só coube em um papel A2. Mas esse também não tinha graça: o jogo era muito esparso, sempre acabava antes que conseguíssemos utilizar todas as seis dimensões.<br />
<br />
No fim, o Jogo da Velha 4D era o ponto ótimo, e jogamos essa variação até o fim do colégio. Mas quando eu entrei na faculdade, topei com um problema diferente.<br />
<br />
<h3 style="text-align: left;">
O Jogo da Velha Minimax</h3>
<br />
A biblioteca da Poli era enorme e cheia de cheia de títulos legais, como o livro de <a href="http://www.amazon.com/Artificial-Intelligence-Modern-Approach-Edition/dp/0136042597/ref=sr_1_1?ie=UTF8&qid=1357523416&sr=8-1&keywords=norvig">Inteligência Artificial do Peter Norvig</a>. Ali eu conheci o <a href="http://en.wikipedia.org/wiki/Minimax">algoritmo minimax</a> para jogos competitivos, e naturalmente bateu a vontade de implementá-lo no meu Jogo da Velha 4D. (A versão com minimax também está no github: <a href="https://github.com/ricbit/Oldies/blob/master/1993-08-tictactoe4d/4dvelha2.pas">4dvelha2.pas</a>)<br />
<br />
Infelizmente, o <a href="http://en.wikipedia.org/wiki/Branching_factor">branching factor</a> do Jogo da Velha 4D é muito alto: começa em 256, quase a mesma dificuldade do jogo de Go. Nos computadores da época (isso foi por volta de 1994), eu conseguia no máximo descer um único nível da árvore. Eu fiquei bastante frustrado com isso. Será que eu conseguiria bolar alguma variação do Jogo da Velha que tivesse um branching factor menor?<br />
<br />
Alguns anos depois eu consegui uma solução: ao invés de mudar o número de dimensões, eu mudei a geometria do tabuleiro! Se você pegar um Jogo da Velha 3D, esticar as pontas, e depois torcer o tabuleiro sobre si mesmo, o resultado é um Jogo da Velha 3D não-Euclideano.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiTJm7AZnYtH-wjyC5-8-0UWP4PHjBH-gN0cdcfU7LyVxw0br22Zz8ObB3XvdyMxvrw2MaKRgcv8pP8TaKeZyi9HmbTzr7hsaQJmQb0_qJjg6eZyY5dBddPvZmpc0ZCudJGBCQP3CHafuw/s1600/cubos+(2).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiTJm7AZnYtH-wjyC5-8-0UWP4PHjBH-gN0cdcfU7LyVxw0br22Zz8ObB3XvdyMxvrw2MaKRgcv8pP8TaKeZyi9HmbTzr7hsaQJmQb0_qJjg6eZyY5dBddPvZmpc0ZCudJGBCQP3CHafuw/s400/cubos+(2).jpg" width="400" /></a></div>
<br />
Essa versão eu implementei como um applet Java, então você pode <a href="http://www.ricbit.com/mundobizarro/velha.php">jogar online</a>, ou pegar o <a href="https://github.com/ricbit/Oldies/tree/master/2000-01-3dtictactoe">source no github</a>. Com o branching factor menor, deu para colocar três níveis de dificuldade, desde o Easy (abre um nível da árvore), até o Hard (abre três níveis da árvore).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizyA3YpWA7xvPSQlnUSL493dc0lDjV1awK56LPC1CE8LmUOm649PdyqHNYbVMHL2U6Oowx6bPc3lUsThMVQzfq44uyZTUwcSh-AJpEaoRxpS5A9jyrQFU_A2DBf8sotcbQc2kzyzwwgOY/s1600/rot3d.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="302" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizyA3YpWA7xvPSQlnUSL493dc0lDjV1awK56LPC1CE8LmUOm649PdyqHNYbVMHL2U6Oowx6bPc3lUsThMVQzfq44uyZTUwcSh-AJpEaoRxpS5A9jyrQFU_A2DBf8sotcbQc2kzyzwwgOY/s400/rot3d.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<i>Jogo da Velha não-Euclideano</i></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Ganhar do computador no Hard não é impossível, mas você vai precisar de muito treino para conseguir :)<br />
<br />
<h3 style="text-align: left;">
A Criatividade</h3>
<br />
Dessa vez quem cantou a moral da história foi o Douglas Hofstadter. Em <a href="http://www.amazon.com/Metamagical-Themas-Questing-Essence-Pattern/dp/0465045669">um dos seus livros</a> ele conclui que "variações sobre um tema são o ponto crucial da Criatividade". Pode parecer anti-intuitivo para a maioria: ser criativo não é o mesmo que ser original? Então como pode a criatividade estar baseada na variação?<br />
<br />
O problema é que quem está de fora só vê uma pessoa girando um botãozinho: esse Jogo da Velha está na posição 2D, eu giro o botão aqui e tenho 3D, giro mais e tenho 4D. Mas criatividade não é girar o botão, criatividade é achar um botão que não estava lá! Quando eu notei que tinha um botão que girava a geometria, aí sim eu estava sendo criativo.<br />
<br />
Em resumo, para expandir a sua criatividade você precisa<b> achar variáveis onde antes só haviam constantes</b>. E isso vale tanto para arte quanto para computação :)</div>
<div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com8tag:blogger.com,1999:blog-6306509703738480474.post-33680986889945529822012-06-26T01:35:00.000-03:002012-06-26T15:44:44.373-03:00Estimule sua Criatividade #1<div dir="ltr" style="text-align: left;" trbidi="on">
Um tempo atrás me perguntaram se existe algum <b>método para ser mais criativo</b>. Isso é bem difícil: para testar se um método funciona ou não, você precisaria medir a criatividade antes e depois de usar o método, e ninguém sabe como mensurar criatividade.<br />
<br />
Dito isso, eu conheço dois truques que parecem funcionar bem. Neste post eu vou falar do primeiro método, e pra isso eu vou contar um história que aconteceu comigo lá nos antigos tempos de <b>colégio técnico</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja-QaPk6wvCgGE4lvP-NKcsFY92w7eMAJrYWT1nj2a7G2hXh9wPYMYdstwTmdSNL2nus7O78IRyU6QL7sfSliAFWhvWDMr4EPdZjK8AsMDHST51R0pkYoWfS9HItbb5JQziHcH7GyUaxs/s1600/prancheta.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEja-QaPk6wvCgGE4lvP-NKcsFY92w7eMAJrYWT1nj2a7G2hXh9wPYMYdstwTmdSNL2nus7O78IRyU6QL7sfSliAFWhvWDMr4EPdZjK8AsMDHST51R0pkYoWfS9HItbb5JQziHcH7GyUaxs/s400/prancheta.jpg" width="400" /></a></div>
<br />
<h3 style="text-align: left;">
A Federal</h3>
<br />
Em 1991 eu comecei o curso técnico em eletrônica na <a href="http://www.etfsp.com/">ETFSP</a>, popularmente conhecida como a <b>Federal</b> (e que hoje em dia é <a href="http://www.ifsp.edu.br/">IFSP</a>). A primeira coisa que nos passaram foi a lista de material para comprar. Além dos básicos livros e cadernos, a lista também incluía itens mais específicos, como caneta nanquim, <a href="http://www.oprojetista.com.br/busca/subsubcat_5_13_14_Materiais-para-Desenho-Reguas-Normografos.html">normógrafo</a> e calculadora científica.<br />
<br />
A calculadora foi um problema. Naquela época, calculadoras científicas eram caras (ou melhor, <a href="http://www.smbc-comics.com/index.php?db=comics&id=2582#comic">até hoje em dia elas são caras</a>). Como eu não tinha condições de comprar uma, resolvi encarar o desafio de fazer o curso usando só uma <b>calculadora de quatro operações</b>.<br />
<br />
O primeiro ano foi fácil: no início do curso, o único componente usado era o <b>resistor</b>, e contas com resistores você consegue fazer só com as quatro operações. No segundo ano apareceram os <b>capacitores</b>, e aí a<span style="background-color: white;"> coisa complicou: agora as contas envolviam exponenciais e funções trigonométricas, que a minha calculadora simples não fazia.</span><br />
<br />
O que fazer então? Dado que eu não conhecia ninguém para me emprestar uma calculadora científica, nem tinha dinheiro para comprar uma, o jeito foi apelar para a <b>herança do meu avô</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuOcyiklN3ugu2ghiYrBhcAiFrl4X7Xz6iRxAf06YXbbuu2JHXCHbuQR8MwPHVWi-aqVsDxcbt4YvEX5barzlAeG6Sb6srJwFxR9k_LKKBy2pvWvIJ8PsEeQM4ymNbpeT7kAC8cQMBLcc/s1600/estante_gigante.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuOcyiklN3ugu2ghiYrBhcAiFrl4X7Xz6iRxAf06YXbbuu2JHXCHbuQR8MwPHVWi-aqVsDxcbt4YvEX5barzlAeG6Sb6srJwFxR9k_LKKBy2pvWvIJ8PsEeQM4ymNbpeT7kAC8cQMBLcc/s400/estante_gigante.jpg" width="400" /></a></div>
<br />
A herança do meu avô não era dinheiro. Melhor que isso, era <b>conhecimento</b>! Mais especificamente, era uma estante enorme que ia de parede a parede, com um monte de livros sobre todo tipo de assunto. Os livros eram antigos, da década de 60, e iam desde o <a href="http://www.hfleming.com/tesouro.html">Tesouro da Juventude</a> até a <a href="http://produto.mercadolivre.com.br/MLB-426551159-estudo-pratico-da-lingua-portuguesa-jnio-quadros-6-vols-_JM">Gramática do Jânio Quadros</a>.<br />
<br />
O livro que me salvou foi uma curiosa coleção de <b>Matemática para Seminaristas</b>. Eu não sei exatamente por que um padre precisa saber matemática, mas o fato é que o livro era bem completo, começava com produtos notáveis e avançava até o Cálculo. E no meio do caminho, tinha o truque que eu precisava para usar minha calculadora!<br />
<br />
<h3 style="text-align: left;">
A Exponencial</h3>
<br />
Os problemas que caíam na prova eram mais ou menos assim: dado o circuito <a href="http://en.wikipedia.org/wiki/RC_circuit">RC-série</a> abaixo, calcule a tensão no capacitor após 2 segundos.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZEThlFuOXRx2ZOwklam05C0qvua40lMrgLgBrkbaAeH9Cstbn8WuafF0iDlOb7XzJAFVMHQ-cQayrvUFOha0QEROC7Z7_e3Qh4Tfyc_bGE27QziJzWKd8s3fNU1Fez60pFv8aUotQdDM/s1600/circuito_rc+(1).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZEThlFuOXRx2ZOwklam05C0qvua40lMrgLgBrkbaAeH9Cstbn8WuafF0iDlOb7XzJAFVMHQ-cQayrvUFOha0QEROC7Z7_e3Qh4Tfyc_bGE27QziJzWKd8s3fNU1Fez60pFv8aUotQdDM/s1600/circuito_rc+(1).jpg" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
No colégio técnico você não precisava resolver a equação diferencial. Ao invés disso, eles davam a fórmula final; você só precisava decorar e aplicar:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7bomo6w.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7bomo6w.png" /></a></div>
<br />
O meu problema era a exponencial, que só tem em calculadoras científicas. Mas, usando o livro dos seminaristas, eu descobri a fórmula que resolveu meu problema: a expansão em <a href="http://en.wikipedia.org/wiki/Taylor_series">série de Taylor</a>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7vvzf4k.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7vvzf4k.png" /></a></div>
<br />
Parece complicado, mas é simples calcular essa fórmula numa calculadora de quatro operações, desde que ela tenha os botões de memória (M+, M-, MC e MR). No exemplo dado, em que eu preciso calcular <b>exp(-0.2)</b>, a sequência de botões é curta:<br />
<br />
<div style="line-height: 35px;">
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">C</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">MC</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">1</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">M+</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">.</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">2</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">M-</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">×</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">.</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">2</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">÷</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">2</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">=</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">M+</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">×</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">.</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">2</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">÷</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">3</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">=</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">M-</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">×</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">.</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">2</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">÷</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">4</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">=</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">M+</span>
<span style="background-color: lightgrey; border: 1px solid black; margin-right: 4px; padding: 4px;">MR</span>
</div>
<br />
Com 29 toques eu tenho o valor de 0.818, correto com três casas decimais (e o professor só pedia duas). Se você tiver os dedos ágeis, praticamente não leva tempo!<br />
<br />
Esse método também é bom para calcular <b>senos</b> e <b>cossenos</b>, que aparecem no cálculo de <a href="http://en.wikipedia.org/wiki/Phasor">ângulos de fasores</a> e no cálculo do <a href="http://en.wikipedia.org/wiki/Power_factor">fator de potência</a>. Mas tem um caso onde o método não funciona...<br />
<br />
<h3 style="text-align: left;">
O Logaritmo</h3>
<br />
Às vezes, o que caía na prova era o <b>problema inverso</b>: dado aquele mesmo circuito RC-série, quanto tempo leva pro capacitor ficar com metade da tensão da fonte? Nesse caso, a conta depende de <b>log(0.5)</b>, e não tem logaritmo na calculadora de quatro operações.<br />
<br />
Pior, nesse caso o livro também não ajudava. A única fórmula que tinha no livro era a seguinte:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/86ymku7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/86ymku7.png" /></a></div>
<br />
Se você tentar usar essa fórmula na calculadora, logo vai perceber que ela <b>não é prática</b>: são necessários muitos termos para conseguir a precisão de duas casas decimais desejada. Olhando as fórmulas, a velocidade de convergência é clara: os coeficientes da exponencial caem com a velocidade do fatorial, enquanto que os coeficientes do logaritmo caem com a velocidade da <a href="http://en.wikipedia.org/wiki/Harmonic_series_(mathematics)">série harmônica</a>, que é muito mais lenta.<br />
<br />
Hoje em dia, eu provavelmente usaria o <a href="http://en.wikipedia.org/wiki/Newton's_method">método de Newton-Raphson</a> para calcular o logaritmo. Mas eu não entendia de cálculo numérico, então nem sabia que esse método existia. Por isso, eu tive que <b>apelar</b>. Ao invés de decorar uma fórmula, eu decorei quatro números:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7uyrw4p.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7uyrw4p.png" /></a></div>
<br />
É fácil decorar quatro números de 7 dígitos né? Certamente todo mundo sabe pelo menos quatro números de telefones de cabeça, e a dificuldade é a mesma. Com esses quatro números, eu conseguia calcular tudo que queria! Precisa de <b>log(0.5)</b>?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7fjsy3l.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7fjsy3l.png" /></a></div>
<br />
Precisa de <b>log(4.2)</b>? Beleza:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6vxq7uq.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6vxq7uq.png" /></a></div>
<br />
<br />
E se for <b>log(2.6)</b>? Ops, aí não dá, 26 tem um fator primo 13 que não está na minha lista. Mas eu posso aproximar:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7ojev3s.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7ojev3s.png" /></a></div>
<br />
O resultado não é exato, mas tem precisão de duas casas. Quem precisa de calculadora científica, afinal?<br />
<br />
Isso foi sorte, ou sempre é possível aproximar? Na época eu tinha uma intuição que sempre dava, mas hoje em dia eu consigo demonstrar que isso é verdade! A demonstração está na caixa azul, pule se você não sabe <a href="http://en.wikipedia.org/wiki/Number_theory">teoria dos números</a>:<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
A demonstração usa diretamente o <a href="http://en.wikipedia.org/wiki/Equidistribution_theorem">teorema da equidistribuição de Weyl</a>: o conjunto das partes fracionárias dos múltiplos inteiros de um número irracional qualquer é <a href="http://en.wikipedia.org/wiki/Dense_set">denso</a> em [0,1). Ou seja, para qualquer irracional
<b>α</b> e real <b>q</b>, onde <b>0 ≤ q < 1</b>, e para qualquer <b>ε</b> positivo dado, sempre existe um inteiro <b>n</b> tal que a fórmula abaixo é verdadeira:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/ccwdju8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/ccwdju8.png" /></a></div>
<br />
Com um pouquinho de álgebra é fácil estender o domínio de [0,1) para todos os reais. Imagine que escolhemos um real <b>x</b> qualquer, tal que <b>x=p+q</b>, onde <b>p</b> é a parte inteira e <b>q</b> é a parte fracionária. Então podemos partir da equação anterior:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/82qta3g.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/82qta3g.png" /></a></div>
<br />
Note que o <a href="http://en.wikipedia.org/wiki/Floor_and_ceiling_functions">piso</a> de <b>nα</b> é um inteiro, e <b>p</b> é um inteiro. Vamos chamar de <b>m</b> a diferença dos dois:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6sxbf2x.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6sxbf2x.png" /></a></div>
<br />
Ou seja, eu posso escolher qualquer irracional <b>α</b> e qualquer real <b>x</b>, que sempre vai existir um par de inteiros <b>m</b> e <b>n</b> que satisfazem a inequação. Já que eu posso escolher qualquer número, então escolho <b>α </b>e<b> x</b> como abaixo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/87j47nq.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/87j47nq.png" /></a></div>
<br />
Note que <b>α</b> é irracional, portanto podemos usar nossa inequação:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/bvqrjzq.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/bvqrjzq.png" /></a></div>
<br />
Ou seja, sempre podemos aproximar o log de um real <b>y</b> qualquer como a soma de múltiplos inteiros de <b>log(2)</b> e <b>log(3)</b>, nem precisava ter decorado o <b>log(5)</b> e <b>log(7)</b>!<br />
<br />
Infelizmente, essa demonstração é um exemplo de <a href="http://en.wikipedia.org/wiki/Constructive_proof">prova não-construtiva</a>: ela garante a existência dos inteiros <b>m</b> e <b>n</b>, mas não fala como achá-los! No fim, você precisa descobrí-los pela intuição, exatamente como eu fazia :)</div>
<br />
<h3 style="text-align: left;">
A Criatividade</h3>
<br />
Eu poderia terminar o post com uma moral do tipo "se a vida te deu limões, então arranje uns bastões de cobre e faça uma bateria", mas tem uma lição mais importante! A <a href="http://en.wikipedia.org/wiki/Marissa_Mayer">Marissa Mayer</a> cantou a bola em <a href="http://www.businessweek.com/magazine/content/06_07/b3971144.htm">uma entrevista de 2006</a>: <b>a criatividade adora restrições</b>, especialmente se acompanhada de um desprezo saudável pelo impossível.<br />
<br />
Tente se lembrar de alguma coisa que você viu e achou muito criativo. Que tal a artista que fazia <a href="http://thedesigninspiration.com/articles/30-cassette-tapes-artworks-by-erika-iris-simmons/">retratos usando fita cassete</a>? O cara que faz <a href="http://www.desicolours.com/creative-art-with-fruits-n-veggies/03/07/2008">arte usando frutas</a>? O doido que <a href="http://www.uoguelph.ca/~pjf/Pi/pi.mnemonic.extrordinaire.html">reescreveu The Raven</a>, do Poe, de modo que o número de letras do poema batesse com os dígitos de pi?<br />
<br />
Todos eles são exemplos onde o autor se auto-impôs uma restrição (de forma, de conteúdo, de matéria-prima). Se você quer expandir sua criatividade, <b>impor limites</b> costuma ser mais efetivo que retirá-los. Eu mesmo sempre tive problemas quando me pediam uma redação "com tema livre", era muito mais fácil quando o tema tinha alguma restrição.<br />
<br />
E isso não vale apenas para arte, vale para computação também. Tente fazer aquele seu programa usar menos memória, menos CPU, tente programar a mesma coisa em menos tempo: tudo isso vai te forçar a usar soluções mais criativas.<br />
<br />
Eu ainda tenho outro truque para estimular a criatividade, mas esse fica para o post seguinte :)<br />
<br />
<i>(Agradecimentos ao povo do Math Stack Exchange pela ajuda na demonstração)</i></div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com30tag:blogger.com,1999:blog-6306509703738480474.post-21597325564083873232012-06-13T10:31:00.000-03:002012-06-13T20:34:32.887-03:00O Bug mais Difícil<div dir="ltr" style="text-align: left;" trbidi="on">
Essa é uma pergunta clássica em entrevistas: <b>qual foi o bug mais difícil que você já consertou?</b> No meu caso, o bug mais difícil que resolvi deu um trabalhão para encontrar, e por um motivo bastante curioso: o bug não era no programa, mas sim na <b>pecinha entre a cadeira e o teclado</b>!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmDg5QaOunnAj6Bws_mfJBczMzwBfTXmHuPFdbPb72GcfEHahosk6bJzjj3blXW9Mwi-LD2GxXVmV5iYtnCVs0KCTJauZs34F9UTy2GqEMT82vITGrpUkG0Q1m4GLXU94DsT1CiVoplPY/s1600/cadeira_teclado.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmDg5QaOunnAj6Bws_mfJBczMzwBfTXmHuPFdbPb72GcfEHahosk6bJzjj3blXW9Mwi-LD2GxXVmV5iYtnCVs0KCTJauZs34F9UTy2GqEMT82vITGrpUkG0Q1m4GLXU94DsT1CiVoplPY/s320/cadeira_teclado.jpg" width="320" /></a></div>
<br />
<h3 style="text-align: left;">
A história</h3>
<br />
Essa história se passa em agosto de 1998. Nessa época, eu estava escrevendo um <a href="http://en.wikipedia.org/wiki/BrMSX">emulador de MSX</a>, e tomando dois cuidados na implementação: o emulador tinha que ser rápido o suficiente para rodar em full speed nas máquinas da época, e tinha que ser preciso o suficiente para ser indistinguível de um MSX real.<br />
<br />
O primeiro objetivo eu atingi escrevendo o emulador inteiramente em Assembly, o segundo através de uma metodologia de testes sólida. Na época eu ainda não fazia <a href="http://en.wikipedia.org/wiki/Unit_testing">unit testing</a>, mas, para cada feature adicionada, eu sempre fazia um programa de teste e comparava a execução dele no emulador e na máquina real.<br />
<br />
As primeiras versões do emulador eram mudas, eu emulava só a CPU e o chip de vídeo. Quando esses dois ficaram estáveis, eu comecei a escrever a emulação do chip de audio: o <a href="http://en.wikipedia.org/wiki/General_Instrument_AY-3-8910">AY-3-8912</a> da General Instrument, também conhecido como PSG. Esse chip tinha capacidade de produzir três canais de som e um canal de ruído, sendo que o ruído era usado para fazer efeitos sonoros (como tiros e explosões), e também para fazer sons de percursão (como baterias).<br />
<br />
Para testar se a minha implementação do canal de ruído estava boa, eu fiz o seguinte programinha em MSX BASIC:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #0000d0; font-weight: bold;">10</span> <span style="color: green; font-weight: bold;">SOUND</span> <span style="color: #0000d0; font-weight: bold;">7</span>,<span style="color: #0000d0; font-weight: bold;">183</span>
<span style="color: #0000d0; font-weight: bold;">20</span> <span style="color: green; font-weight: bold;">SOUND</span> <span style="color: #0000d0; font-weight: bold;">8</span>,<span style="color: #0000d0; font-weight: bold;">15</span>
<span style="color: #0000d0; font-weight: bold;">30</span> <span style="color: green; font-weight: bold;">FOR</span> I<span style="color: #303030;">=</span><span style="color: #0000d0; font-weight: bold;">8</span> <span style="color: green; font-weight: bold;">TO</span> <span style="color: #0000d0; font-weight: bold;">1</span> <span style="color: green; font-weight: bold;">STEP</span> <span style="color: #303030;">-</span><span style="color: #0000d0; font-weight: bold;">1</span>
<span style="color: #0000d0; font-weight: bold;">40</span> <span style="color: green; font-weight: bold;">SOUND</span> <span style="color: #0000d0; font-weight: bold;">6</span>,I
<span style="color: #0000d0; font-weight: bold;">50</span> <span style="color: green; font-weight: bold;">PRINT</span> I
<span style="color: #0000d0; font-weight: bold;">60</span> <span style="color: green; font-weight: bold;">FOR</span> J<span style="color: #303030;">=</span><span style="color: #0000d0; font-weight: bold;">0</span> <span style="color: green; font-weight: bold;">TO</span> <span style="color: #0000d0; font-weight: bold;">300</span>: <span style="color: green; font-weight: bold;">NEXT</span> J,I
<span style="color: #0000d0; font-weight: bold;">70</span> <span style="color: green; font-weight: bold;">BEEP</span>
</pre>
</div>
<br />
Os números mágicos no código assustam, mas o funcionamento é simples: o registro 7 é o <b>mixer</b>, com o valor de 183 eu ligo o gerador de ruído no canal A e desligo todo o resto. O registro 8 é o <b>volume do canal A</b>, que eu setei para 15, ou seja, volume máximo. Por fim, eu faço um loop alterando o valor do registro 6, que é a <b>frequência do ruído</b>.<br />
<br />
Portanto, o que se espera desse programinha é que ele toque um barulhinho de volume constante, começando grave e ficando cada vez mais agudo. Isso na teoria. Na prática, aconteceu outra coisa!<br />
<br />
<h3 style="text-align: left;">
O bug</h3>
<br />
Eu resolvi repetir o experimento da época e filmar. No video abaixo você pode ouvir esse programinha rodando em um MSX real, e depois rodando no meu emulador (eu usei o <a href="http://www.dosbox.com/">dosbox</a> para compilar a versão de 1998, antes do bug ser corrigido):<br />
<br />
<br />
<center><iframe allowfullscreen="" frameborder="0" height="315" src="http://www.youtube.com/embed/8k3idryrk48" width="420"></iframe></center>
<br />
<br />
Olha que curioso, embora eu tenha programado o volume para ficar <b>constante</b>, no MSX real o volume <b>abaixa</b> sensivelmente no final. E pior, no emulador isso não acontece!<br />
<br />
A diferença é sutil, será que eu estava viajando? Um jeito de confirmar é capturando os dois sinais e jogando no <a href="http://www.gnu.org/software/octave/">Octave</a> para comparar (quer dizer, na época ainda não tinha o Octave, então eu usava o Matlab mesmo):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWfczklzaZ-KFghiM_FXAMFGllZO8HEwBtE-5g11swGFZp76oprame1VzGq2ItOWEjCZGejf5wrzMij-c0BBAIPsMRqMwaUwHUMSWCeu-jjwjdNLm2vIB2pYjcKMmm_htGMwpFpD4FbdM/s1600/figure1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWfczklzaZ-KFghiM_FXAMFGllZO8HEwBtE-5g11swGFZp76oprame1VzGq2ItOWEjCZGejf5wrzMij-c0BBAIPsMRqMwaUwHUMSWCeu-jjwjdNLm2vIB2pYjcKMmm_htGMwpFpD4FbdM/s1600/figure1.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
De olho parece realmente que o turboR vai diminuindo, mas é difícil comparar. Um jeito mais eficiente é notando que a grandeza que perceptualmente nós sentimos como <b>volume</b>, fisicamente é causada pela <b>potência</b> do sinal. E potência a gente sabe calcular, é a integral do quadrado da forma de onda. Jogando isso no Octave:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFgBWGSkaRiP0AP5itno82k9Hhu4zq2LuQjiW-29UNI09G9wq8mazqvWNvCnikD0UGQW5FOv9FzLqA9pE9h2Cdp8IJ2HALQoPyGN0zUBG6leF8qD9xl0GJeBD3nZVCYGB9pspJbzaZG0c/s1600/figure2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFgBWGSkaRiP0AP5itno82k9Hhu4zq2LuQjiW-29UNI09G9wq8mazqvWNvCnikD0UGQW5FOv9FzLqA9pE9h2Cdp8IJ2HALQoPyGN0zUBG6leF8qD9xl0GJeBD3nZVCYGB9pspJbzaZG0c/s1600/figure2.png" /></a></div>
<br />
Olha lá! Não era viagem não, nos dois últimos passos realmente o volume do turboR fica menor que o do emulador! Mas por que isso acontece? Talvez eu tivesse alguma pista analisando o hardware com cuidado.<br />
<br />
<h3 style="text-align: left;">
O circuito</h3>
<br />
Para entender o funcionamento do PSG, não adianta usar o <a href="http://www.msxpro.com/esquemas.html">esquemático do MSX</a> e nem pegar o <a href="http://www.ym2149.com/ay8910.pdf">datasheet do AY-3-8912</a>, porque nenhum dos dois explica o que acontece <b>dentro</b> do chip. Em teoria daria para abrir o chip e tentar ler a pastilha com um microscópio eletrônico, mas como eu não tinha acesso a um, o jeito foi apelar para engenharia reversa mesmo.<br />
<br />
Os canais de som do PSG foram fáceis de modelar. Ele tem três geradores de onda quadrada, e cada um funciona da seguinte forma:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMoqTFqUe6cE7tuWiMCTkZHazf7HisVy8YyFDzlEvaaV7c0_NfzavmMOEx0NMBD_U9YrFVlNk3UJZYovsY7-Ozp3hjasOlUkv4-qPxnhS8MirzBF3DvXIJPVY6u-tCPtC4p_qrdoEuPjI/s1600/grafico_bug.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="183" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMoqTFqUe6cE7tuWiMCTkZHazf7HisVy8YyFDzlEvaaV7c0_NfzavmMOEx0NMBD_U9YrFVlNk3UJZYovsY7-Ozp3hjasOlUkv4-qPxnhS8MirzBF3DvXIJPVY6u-tCPtC4p_qrdoEuPjI/s320/grafico_bug.jpg" width="320" /></a></div>
<br />
O clock de entrada do contador é o clock do MSX dividido por 16, ou seja, 223506Hz. A cada pulso, o contador incrementa de um, e o resultado é comparado com o valor do registro que determina a frequência. Quando o valor é igual, o contador reseta e um <a href="http://en.wikipedia.org/wiki/Flip-flop_(electronics)">flip-flop tipo T</a> inverte de estado, gerando assim a onda quadrada desejada.<br />
<br />
Mas e o gerador de ruído? Na época ninguém sabia como funcionava, uns chutavam que era <a href="http://en.wikipedia.org/wiki/Johnson%E2%80%93Nyquist_noise">ruído térmico</a> de resistor, outros que era <a href="http://en.wikipedia.org/wiki/Shot_noise">shot noise</a> na junção de algum semicondutor.<br />
<br />
Mas a verdade era muito mais simples, o PSG tinha um gerador pseudo-aleatório! Para ser mais exato, tinha um gerador de onda quadrada igual ao descrito acima, e a cada transição na saída ele fazia uma iteração em um <a href="http://en.wikipedia.org/wiki/Linear_feedback_shift_register">Linear Feedback Shift Register</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVmeu6Of5FpqrFtuDqmB7CovR94gri27slJfFCC6IHrQ9h1vS6b_cDto6_GFRQrr48Y8yvvMj217L31BbOMlFFET0lgIYhlFE5yCq5WFpoa8o2lgIpg19Uh_VsUhIOfwlUlM9-QAZV_r4/s1600/grafico_bug2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVmeu6Of5FpqrFtuDqmB7CovR94gri27slJfFCC6IHrQ9h1vS6b_cDto6_GFRQrr48Y8yvvMj217L31BbOMlFFET0lgIYhlFE5yCq5WFpoa8o2lgIpg19Uh_VsUhIOfwlUlM9-QAZV_r4/s320/grafico_bug2.jpg" width="320" /></a></div>
<br />
Nós sabemos que todo LFSR gera um sinal periódico, então, capturando um período inteiro do gerador, dá para deduzir qual é o polinômio característico dele. Fazendo o experimento, o polinômio era o maximal de tamanho 17, ou seja, <b>x<sup>17</sup>+x<sup>14</sup>+1</b>.<br />
<br />
Eu conhecia a forma de onda gerada, e conhecia até o exato LFSR que era usado. Ainda assim, isso não explicava por que o volume abaixava. Será que eu tinha implementado errado?<br />
<br />
<h3 style="text-align: left;">
A implementação</h3>
<br />
Um dos meus requisitos era rodar o emulador a toda velocidade nas máquinas da época. Em 1998, isso significava emular esse circuito inteiro em um <a href="http://en.wikipedia.org/wiki/P5_(microarchitecture)">Pentium 1</a>, e ainda sobrar tempo para emular a CPU e o VDP. A coisa era tão apertada que simplesmente escrever em Assembly não era suficiente, eu tinha que garantir que o inner loop rodasse inteiramente em <b>registros</b>.<br />
<br />
O problema é que os meros 7 registros de uso geral do x86 não eram suficientes. Minha primeira solução foi apelar feio: durante a emulação do PSG, eu desligava as interrupções e usava o <b>stack pointer</b> como registro! Na minha casa isso funcionava bem, mas em casa eu usava DOS. A maioria dos meus usuários à época usava Windows 95, e aí essa abordagem de mexer no esp fazia a máquina travar.<br />
<br />
Vamos para a solução dois então. Tinha um monte de variáveis que mudavam a cada nota tocada pelo PSG, mas eram constantes durante o inner loop. Sendo assim, era uma boa oportunidade para usar <b>código auto-modificável</b>: eu "recompilava" o inner loop cada vez que alguém mudasse um registro do PSG. Essa solução funcionava até no Windows e era rápida o suficiente.<br />
<br />
Porém, ela era rápida, mas bastante complexa. Você pode ver <a href="https://github.com/ricbit/Oldies/blob/master/1997-08-brmsx/psg.asm">esse código no github</a>, não é a coisa mais legível do mundo, nem a mais fácil de entender. Então, seria de se esperar que o volume não estivesse abaixando por conta de algum erro de implementação.<br />
<br />
Mas após pensar um pouco, eu concluí que a minha implementação deveria estar correta. Se ela estivesse errada, então esse erro deveria aparecer só no meu emulador. Mas na época exisitiam outros emuladores, e em <b>todos os outros</b> esse mesmo problema aparecia.<br />
<br />
Se várias implementações diferentes tinham o mesmo problema, então o erro não era no código, tinha que ser conceitual. E se é conceitual, então talvez eu conseguisse alguma pista analisando matematicamente o sinal.<br />
<br />
<h3 style="text-align: left;">
A transformada</h3>
<br />
Hora de fazer a modelagem do sinal então! Vamos considerar que os bits de saída do LFSR formam uma sequência discreta <b>{d<sub>i</sub>}</b>, e que a cada bit 1 na entrada nós mandamos para saída uma forma de onda <b>s(t)</b>. Dessa maneira, se a largura do pulso for <b>T</b>, então o ruído do PSG pode ser computado como:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/cmh8e8t.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/cmh8e8t.png" /></a></div>
<br />
Da teoria de <a href="http://en.wikipedia.org/wiki/Stochastic_process">processos estocásticos</a>, nós sabemos que a <a href="http://en.wikipedia.org/wiki/Spectral_density">densidade espectral de potência</a> do sinal, nesse caso, vale:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/d8uf8bp.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/d8uf8bp.png" /></a></div>
<br />
...onde <b>G<sub>d</sub>(f)</b> é a densidade espectral de potência da sequência <b>{d<sub>i</sub>}</b>, e <b>S(f)</b> é a <a href="http://en.wikipedia.org/wiki/Fourier_transform">transformada de Fourier</a> de <b>s(t)</b>. Se admitirmos que o ruído em questão seja <a href="http://en.wikipedia.org/wiki/White_noise">ruído branco</a>, então o <b>G<sub>d</sub>(f)</b> é uma constante. Já a forma de onda <b>s(t)</b> é um <a href="http://en.wikipedia.org/wiki/Rectangular_function">pulso retangular</a>, cuja transformada é a <a href="http://en.wikipedia.org/wiki/Sinc_function">função sinc</a>. Portanto, a d.e.p. do sinal tem a forma de um <b>sinc ao quadrado. </b><br />
<br />
O caso que dá problema no emulador é quando o registro 6 vale 1, ou seja, quando <b>T</b>=8.98<span style="background-color: white; font-family: sans-serif; font-size: 13px; line-height: 19px; text-align: -webkit-auto;">µ</span>s. Assim, a densidade espectral de potência da saída é a abaixo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEkN_5FupJUgq6In1UB3UnUZ-DhCxPh3QyPG2XwUaBr6mP7vwKrohDL_KvAgNwkyvy3mu_bQbguw00NNEQImOP6T92z813xKHxlz1BSzf1QR6vXg2qbKHAtWk7TClAIAW5ldtcuWMZf4s/s1600/grafico_hz.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEkN_5FupJUgq6In1UB3UnUZ-DhCxPh3QyPG2XwUaBr6mP7vwKrohDL_KvAgNwkyvy3mu_bQbguw00NNEQImOP6T92z813xKHxlz1BSzf1QR6vXg2qbKHAtWk7TClAIAW5ldtcuWMZf4s/s1600/grafico_hz.jpg" /></a></div>
<br />
Novamente, aquilo que perceptualmente nós percebemos como <b>volume</b> é a <b>potência</b> do sinal, e, pelo <a href="http://en.wikipedia.org/wiki/Parseval%27s_theorem">teorema de Parseval</a>, a potência é a área abaixo do gráfico acima.<br />
<br />
Depois de olhar um tempo para esse gráfico, finalmente caiu a ficha!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji491DF-Q77qY_VN2rYLuJ44OhkdcKWGdXRdbI3dgJwt7eBwFMpN6kdq-YLGwrUAQorxaX65lqYX45wldfTlYY_K-yBr5apJzBFF9UQK2ietr48UTtIKgm5N-WptZorYRdncFWC9XDq1I/s1600/plim.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji491DF-Q77qY_VN2rYLuJ44OhkdcKWGdXRdbI3dgJwt7eBwFMpN6kdq-YLGwrUAQorxaX65lqYX45wldfTlYY_K-yBr5apJzBFF9UQK2ietr48UTtIKgm5N-WptZorYRdncFWC9XDq1I/s320/plim.jpg" width="320" /></a></div>
<br />
<h3 style="text-align: left;">
A solução</h3>
<br />
No fim, o problema todo estava entre a cadeira e o teclado! Mais especificamente, no fato de que tinha um <b>ser humano</b> entre a cadeira e o teclado! O sistema auditivo humano só consegue ouvir freqüências até <b>20kHz</b>, e esse sinal estava espalhando potência para faixas inaudíveis! De fato, um humano só consegue perceber como volume essa parcela da potência:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDKm-HCxiU04ikNDXXP-6mlUgIiyyfmRlP8bed9B2JKQtgRk6ZMzbWJPvm1bskA-fI7g53ulAb7PU_7ECO-OU9fSBJzRD25WQHBjuk432vM-oq4PS-VA-4puXuTI5gGhc_8vAm55qH3zo/s1600/grafico_hz_2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDKm-HCxiU04ikNDXXP-6mlUgIiyyfmRlP8bed9B2JKQtgRk6ZMzbWJPvm1bskA-fI7g53ulAb7PU_7ECO-OU9fSBJzRD25WQHBjuk432vM-oq4PS-VA-4puXuTI5gGhc_8vAm55qH3zo/s1600/grafico_hz_2.jpg" /></a></div>
<br />
A integral do sinc ao quadrado não dá para resolver analiticamente, mas dá para calcular numericamente. Eu posso, por exemplo, fazer uma tabela com a <b>potência audível</b> dividida pela <b>potência total</b>, para cada um dos valores de frequência que eu usei no programinha em BASIC:<br />
<br />
<br />
<center><table border="1" cellpadding="5">
<tbody>
<tr><td><b>Registro 6</b></td><td><b>Porcentagem da potência audível</b></td></tr>
<tr><td>8</td><td>0.99</td></tr>
<tr><td>7</td><td>0.99</td></tr>
<tr><td>6</td><td>0.99</td></tr>
<tr><td>5</td><td>0.99</td></tr>
<tr><td>4</td><td>0.99</td></tr>
<tr><td>3</td><td>0.96</td></tr>
<tr><td>2</td><td>0.83</td></tr>
<tr><td>1</td><td>0.50</td></tr>
</tbody></table>
</center>
<br />
<br />
Se você comparar com o gráfico de potência do emulador versus turboR, dá pra notar que a porcentagem acima é exatamente o que falta para tornar a volume do emulador igual à máquina real! Adicionando essa atenuação no emulador, ele finalmente ficou fiel ao original como eu queria.<br />
<br />
Leitores que estejam prestando atenção, a essa altura devem estar se perguntando: "Mas peraí, se o problema é puramente sensorial, então porque o gráfico de potência medido no Octave mostra a perda de volume?". A resposta é que isso também é um artefato do sistema auditivo humano, mas indiretamente.<br />
<br />
Quando eu pluguei o turboR na entrada de microfone do meu notebook, a placa de som fez a captura do audio em qualidade de CD, ou seja, com <a href="http://en.wikipedia.org/wiki/Sampling_(signal_processing)">amostragem</a> de 44100Hz. E os projetistas da placa de som são espertos, eles sabem que antes de amostrar você precisa passar o sinal em um <a href="http://en.wikipedia.org/wiki/Low-pass_filter">filtro passa-baixas</a> com corte na <a href="http://en.wikipedia.org/wiki/Nyquist_frequency">frequência de Nyquist</a>, para evitar o <a href="http://en.wikipedia.org/wiki/Aliasing">aliasing</a>. E essa frequência foi calculada exatamente para jogar fora o que os humanos não ouvem, gerando o mesmo problema.<br />
<br />
Se você quiser refazer o experimento, coloquei no github os scripts em Octave que usei para gerar as imagens e tabelas do post:<br />
<br />
<i><a href="https://github.com/ricbit/Oldies/tree/master/2012-06-noise">Scripts em Octave para análise de ruído</a></i><br />
<br />
Por fim, esse bug deu um trabalhão para resolver, e no final desconfio que a diferença era tão sutil que ninguém além de mim notou a diferença :)</div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com21tag:blogger.com,1999:blog-6306509703738480474.post-57475621273475001092012-06-02T23:15:00.000-03:002012-06-03T18:21:47.781-03:00A Fórmula de Bhaskara<div dir="ltr" style="text-align: left;" trbidi="on">
Um tempo atrás o <a href="http://hacktoon.com/">Karlisson</a> teve uma idéia muito boa: criar uma coletânea com os <a href="http://labs.hacktoon.com/src/1001/">1001 algoritmos que você deve implementar antes de morrer</a>. O objetivo é fazer uma implementação de referência, em python, para todos os algoritmos clássicos, desde os simples como o <a href="http://en.wikipedia.org/wiki/Bubble_sort">Bubblesort</a> até os complexos como o <a href="http://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm">Edmonds-Karp</a>.<br />
<br />
Se você também quiser participar, é só fazer um fork do <a href="https://github.com/karlisson/1001">repositório no github</a>. É um ótimo exercício para quem é iniciante, e para os veteranos é uma boa oportunidade de praticar o hábito de fazer <a href="http://en.wikipedia.org/wiki/Code_review">code reviews</a>. Já que no post anterior eu <a href="http://blog.ricbit.com/2012/05/terra-plana.html">falava de parábolas</a>, vamos aproveitar pra revisar um dos códigos do projeto: uma função que acha as <a href="https://github.com/karlisson/1001/blob/master/algorithms/math/algebra/bhaskara.py">raízes reais da equação de segundo grau</a>:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: green; font-weight: bold;">def</span> <span style="color: #0060b0; font-weight: bold;">bhaskara</span>(a, b, c):
delta <span style="color: #303030;">=</span> b <span style="color: #303030;">**</span> <span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">-</span> <span style="color: #0000d0; font-weight: bold;">4</span> <span style="color: #303030;">*</span> a <span style="color: #303030;">*</span> c
<span style="color: green; font-weight: bold;">if</span> delta <span style="color: #303030;"><</span> <span style="color: #0000d0; font-weight: bold;">0</span>:
<span style="color: green; font-weight: bold;">return</span> <span style="color: #007020;">None</span>
<span style="color: green; font-weight: bold;">else</span>:
raizes <span style="color: #303030;">=</span> []
m1 <span style="color: #303030;">=</span> math<span style="color: #303030;">.</span>sqrt(delta)
r1 <span style="color: #303030;">=</span>(<span style="color: #303030;">-</span>b <span style="color: #303030;">+</span> m1) <span style="color: #303030;">/</span> (<span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">*</span> a)
raizes<span style="color: #303030;">.</span>append(r1)
r2 <span style="color: #303030;">=</span>(<span style="color: #303030;">-</span>b <span style="color: #303030;">-</span> m1) <span style="color: #303030;">/</span> (<span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">*</span> a)
raizes<span style="color: #303030;">.</span>append(r2)
<span style="color: green; font-weight: bold;">return</span> raizes
</pre>
</div>
<br />
A função é uma implementação direta da fórmula:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/dev6ld.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/dev6ld.png" /></a></div>
<br />
Conferindo com a fórmula, o código parece correto. Porém, olhando com cuidado, ele tem três problemas!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI6gJzf1DvquT61ozzNar4e90V9VUP4azlq1YkK-XorCLdwjtvawLi7LSQQjwB86n2hPAflsaoCJDX4FSXwoVZ2ai5ffNbMuqeEpJD1SHCFO0JleCetRZDoe_duM21Xk3Gj9YmhXG-xV8/s1600/mostrando_bug.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI6gJzf1DvquT61ozzNar4e90V9VUP4azlq1YkK-XorCLdwjtvawLi7LSQQjwB86n2hPAflsaoCJDX4FSXwoVZ2ai5ffNbMuqeEpJD1SHCFO0JleCetRZDoe_duM21Xk3Gj9YmhXG-xV8/s320/mostrando_bug.jpg" width="320" /></a></div>
<br />
<br />
<b>Primeiro problema: API inconsistente</b><br />
<br />
O problema mais imediato do código é que ele faz uma divisão por zero quando <b>a</b> é igual a zero. Mas isso é um bug de verdade? A intenção do código era resolver equações do segundo grau, mas, se <b>a</b> é nulo, então a equação não é de segundo grau, é de primeiro!<br />
<br />
Eu interpreto isso como um problema na API. Se a idéia do código é resolver somente equações de grau 2, então, antes de sair fazendo conta, ele deveria verificar se as entradas são válidas (ou seja, se <b>a</b> é não-nulo). Por outro lado, se a intenção é resolver equações de grau 2 ou inferior, então ele poderia retornar <b>[-c/b]</b> quando <b>a</b> for zero. Nos dois casos, faltou sinalizar exatamente qual o propósito da API.<br />
<br />
A API ainda tem uma segunda falha, que é o valor de retorno. O que exatamente ele vai retornar? Eu poderia, por exemplo, dizer que a função sempre retorna uma lista com as soluções existentes. Nesse caso, quando o <b>delta</b> é negativo, o correto seria retornar uma lista vazia, ao invés de retornar <b>None</b>.<br />
<br />
Além disso, o que devemos retornar quando o <b>delta</b> é exatamente zero? As duas possibilidades são retornar uma lista com uma única raiz, <b>[-b/2a]</b>, ou então retornar uma lista com duas raízes iguais, ou seja, <b>[-b/2a,-b/2a]</b>. Qual resposta faz mais sentido?<br />
<br />
A escolha correta vem do <a href="http://en.wikipedia.org/wiki/Fundamental_theorem_of_algebra">Teorema Fundamental da Álgebra</a>, que diz que o número de raízes complexas de uma equação polinomial é igual ao grau da equação. O motivo de considerarmos que algumas equações tem múltiplas raízes iguais é pra fazer esse teorema ser válido em todas as situações.<br />
<br />
Mas o teorema só vale para raízes complexas! Como estamos trabalhando com raízes reais, então eu acho que o mais correto é retornar uma lista com um único elemento mesmo.<br />
<br />
Você também poderia argumentar que o nome da função não é apropriado, porque está descrevendo a implementação (fórmula de Bhaskara) ao invés de descrever a interface (solução da equação quadrática). Mas aí nós chegamos no segundo problema...<br />
<br />
<b>Segundo problema: Bhaskara quem?</b><br />
<br />
Certamente você ouviu na escola que a solução da equação quadrática chama-se fórmula de <a href="http://en.wikipedia.org/wiki/Bh%C4%81skara_II">Bhaskara</a>. Mas, curiosamente, é só no Brasil que a fórmula tem esse nome!<br />
<br />
Pode conferir: a <a href="http://en.wikipedia.org/wiki/Quadratic_equation">wikipedia em inglês</a> nem cita o Bhaskara. Talvez na <a href="http://hi.wikipedia.org/wiki/%E0%A4%B5%E0%A4%B0%E0%A5%8D%E0%A4%97_%E0%A4%B8%E0%A4%AE%E0%A5%80%E0%A4%95%E0%A4%B0%E0%A4%A3">wikipedia em hindi</a>? Nada de Bhaskara lá também, na Índia eles chamam a equação de <a href="http://www.youtube.com/watch?v=N_Z_VYf1WP8">fórmula de Sridhar Acharya</a>. Nos outros países a fórmula nem tem nome, é simplesmente "equação quadrática" ou "fórmula a-b-c".<br />
<br />
Na verdade, os babilônicos já sabiam resolver alguns tipos de equações quadráticas há pelo menos 4000 anos. Os gregos conheciam soluções geométricas. E a fórmula em si, é do Bhaskara mesmo?<br />
<br />
Bem, uma maneira de resolver a dúvida é procurando exatamente o que ele escreveu. O texto mais importante do Bhaskara foi o livro que dedicou à filha, <a href="http://en.wikipedia.org/wiki/Lilavati">Lilavati</a>. O original era em sânscrito, mas tem uma <a href="http://www.archive.org/stream/algebrawitharith00brahuoft#page/28/mode/2up">tradução em inglês</a> online para quem quiser ler. Eu procurei pela solução da quadrática, e o mais próximo que achei foi a proposição 63:<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
Uma quantidade, incrementada ou decrementada de sua raiz quadrada multiplicada por algum número, é dada. Então adicione o quadrado de metade do multiplicador da raiz ao número dado, e extraia a raiz quadrada da soma. Adicione metade do multiplicador, se a diferença foi dada; ou subtraia, se a soma for dada. O quadrado do resultado é a quantidade procurada.</div>
<br />
Em notação moderna, o que o Bhaskara escreveu foi:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6of9lw3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6of9lw3.png" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
Eu não acho que seja correto creditar ao Bhaskara a solução da equação quadrática. A fórmula acima é bem parecida com a que usamos, mas note o detalhe: ela só fornece uma raiz! Para considerar a solução como correta, eu acho que ele deveria ter explicitado que algumas equações vão ter duas raízes.<br />
<br />
Pois bem, podemos então mudar o nome da função, de <i>bhaskara()</i> para algo mais apropriado, como <i>quadratic_roots()</i>. Mas ainda resta o último problema...<br />
<br />
<b>Terceiro problema: Instabilidade numérica</b><br />
<br />
Vamos testar o código original com a seguinte equação: <b>x<sup>2-</sup>2000000000x+4=0</b>. As raízes são aproxidamadamente <b>2e9</b> e <b>2e-9</b>. Porém, olhe só o resultado:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #303030;">>>></span> bhaskara<span style="color: #303030;">.</span>bhaskara(<span style="color: #0000d0; font-weight: bold;">1</span>, <span style="color: #303030;">-</span><span style="color: #6000e0; font-weight: bold;">2e9</span>, <span style="color: #0000d0; font-weight: bold;">4</span>)
[<span style="color: #6000e0; font-weight: bold;">2000000000.0</span>, <span style="color: #6000e0; font-weight: bold;">0.0</span>]
</pre>
</div>
<br />
Como assim zero? Cadê a solução <b>2e-9</b>? Essa raiz foi vítima de um problema que nem todo mundo presta atenção: <b><i>muito cuidado com a perda de precisão em ponto flutuante</i></b>. Em especial, quando você subtrai dois números de magnitude similar, a resposta pode não ter precisão suficiente para dar o resultado correto. Para a equação dada, o código original tem os valores <b>b</b> e <b>m1</b> muito próximos, e a subtração perde a menor raiz.<br />
<br />
A solução é evitar a subtração. Você inspeciona o sinal de <b>b</b>, e escolhe <b>m1</b> com o mesmo sinal, de modo que os módulos são sempre somados, achando assim a primeira raiz <b>x1</b>. Tendo <b>x1</b>, você pode achar <b>x2</b> usando a <a href="http://en.wikipedia.org/wiki/Vieta's_formulas">fórmula de Viète</a> para o produto de raízes:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/89ytdjv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/89ytdjv.png" /></a></div>
<br />
Dessa maneira a subtração problemática some, e chegamos na versão final do programa corrigido:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: green; font-weight: bold;">def</span> <span style="color: #0060b0; font-weight: bold;">quadratic_roots</span>(a, b, c):
<span style="color: #d04020;">"""Find all real roots of a quadratic equation.</span>
<span style="color: #d04020;"> Args:</span>
<span style="color: #d04020;"> a, b, c: Coefficients of the quadratic, a*x*x+b*x+c=0</span>
<span style="color: #d04020;"> Returns:</span>
<span style="color: #d04020;"> A list with all real roots.</span>
<span style="color: #d04020;"> Raises:</span>
<span style="color: #d04020;"> ValueError if a==0 (the equation is not quadratic).</span>
<span style="color: #d04020;"> """</span>
<span style="color: green; font-weight: bold;">if</span> <span style="color: black; font-weight: bold;">not</span> a:
<span style="color: green; font-weight: bold;">raise</span> <span style="color: #f00000; font-weight: bold;">ValueError</span>
delta <span style="color: #303030;">=</span> b <span style="color: #303030;">*</span> b <span style="color: #303030;">-</span> <span style="color: #0000d0; font-weight: bold;">4</span> <span style="color: #303030;">*</span> a <span style="color: #303030;">*</span> c
<span style="color: green; font-weight: bold;">if</span> delta <span style="color: #303030;"><</span> <span style="color: #0000d0; font-weight: bold;">0</span>:
<span style="color: green; font-weight: bold;">return</span> []
<span style="color: green; font-weight: bold;">if</span> <span style="color: black; font-weight: bold;">not</span> delta:
<span style="color: green; font-weight: bold;">return</span> [<span style="color: #303030;">-</span>b <span style="color: #303030;">/</span> (<span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">*</span> a)]
<span style="color: green; font-weight: bold;">if</span> b <span style="color: #303030;"><</span> <span style="color: #0000d0; font-weight: bold;">0</span>:
x1 <span style="color: #303030;">=</span> (<span style="color: #303030;">-</span>b <span style="color: #303030;">+</span> delta <span style="color: #303030;">**</span> <span style="color: #6000e0; font-weight: bold;">0.5</span>) <span style="color: #303030;">/</span> (<span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">*</span> a)
<span style="color: green; font-weight: bold;">else</span>:
x1 <span style="color: #303030;">=</span> (<span style="color: #303030;">-</span>b <span style="color: #303030;">-</span> delta <span style="color: #303030;">**</span> <span style="color: #6000e0; font-weight: bold;">0.5</span>) <span style="color: #303030;">/</span> (<span style="color: #0000d0; font-weight: bold;">2</span> <span style="color: #303030;">*</span> a)
x2 <span style="color: #303030;">=</span> c <span style="color: #303030;">/</span> (a <span style="color: #303030;">*</span> x1)
<span style="color: green; font-weight: bold;">return</span> [x1, x2]
</pre>
</div>
<br />
Conferindo:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #303030;">>>></span> roots<span style="color: #303030;">.</span>quadratic_roots(<span style="color: #0000d0; font-weight: bold;">1</span>, <span style="color: #303030;">-</span><span style="color: #6000e0; font-weight: bold;">2e9</span>, <span style="color: #0000d0; font-weight: bold;">4</span>)
[<span style="color: #6000e0; font-weight: bold;">2000000000.0</span>, <span style="color: #6000e0; font-weight: bold;">2.0000000000000001e-09</span>]
</pre>
</div>
<br />
Nice!<br />
<br />
Você pode se perguntar se esse exemplo que eu dei não é artificial demais, e se coisas assim acontecem na prática. Mas acontecem sim! Um exemplo comum onde esse erro acontece é na implementação da <a href="http://en.wikipedia.org/wiki/Binary_search_algorithm">busca binária</a>.<br />
<br />
Por exemplo, suponha que você quer resolver a equação <b>ln(x)+sqrt(x)=5</b>. A função <i>ln()</i> é monotônica, o <i>sqrt()</i> também, então dá pra achar a raiz por busca binária:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: green; font-weight: bold;">def</span> <span style="color: #0060b0; font-weight: bold;">search</span>(epsilon):
inf, sup <span style="color: #303030;">=</span> <span style="color: #6000e0; font-weight: bold;">0.0</span>, <span style="color: #6000e0; font-weight: bold;">1e10</span>
<span style="color: green; font-weight: bold;">while</span> sup <span style="color: #303030;">-</span> inf <span style="color: #303030;">></span> epsilon:
med <span style="color: #303030;">=</span> (sup <span style="color: #303030;">+</span> inf) <span style="color: #303030;">/</span> <span style="color: #0000d0; font-weight: bold;">2</span>
x <span style="color: #303030;">=</span> math<span style="color: #303030;">.</span>log(med) <span style="color: #303030;">+</span> math<span style="color: #303030;">.</span>sqrt(med)
<span style="color: green; font-weight: bold;">if</span> x <span style="color: #303030;">></span> <span style="color: #6000e0; font-weight: bold;">5.0</span>:
sup <span style="color: #303030;">=</span> med
<span style="color: green; font-weight: bold;">else</span>:
inf <span style="color: #303030;">=</span> med
<span style="color: green; font-weight: bold;">return</span> inf
</pre>
</div>
<br />
Vamos testar essa rotina para várias precisões:
<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>search(<span style="color: #6000e0; font-weight: bold;">1e-4</span>)
<span style="color: #6000e0; font-weight: bold;">8.3093709690729156</span>
<span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>search(<span style="color: #6000e0; font-weight: bold;">1e-10</span>)
<span style="color: #6000e0; font-weight: bold;">8.3094326941933723</span>
<span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>search(<span style="color: #6000e0; font-weight: bold;">1e-15</span>)</pre>
</div>
<br />
Oops! Quando <b>epsilon</b> vale <b>1e-15,</b> a rotina trava! De novo, o problema é perda de precisão. Nesse caso, as variáveis <b>sup</b> e <b>inf</b> tem valores muito próximos, então a média <b>med</b> não sai do lugar, ficando presa no valor do <b>inf</b> para sempre.<br />
<br />
A solução, novamente, é sumir com a subtração. Ao invés de controlar os extremos do intervalo, você usa uma variável para o valor inicial e outra para o tamanho do intervalo:<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: green; font-weight: bold;">def</span> <span style="color: #0060b0; font-weight: bold;">smart_search</span>(epsilon):
inf, delta <span style="color: #303030;">=</span> <span style="color: #6000e0; font-weight: bold;">0.0</span>, <span style="color: #6000e0; font-weight: bold;">1e10</span>
<span style="color: green; font-weight: bold;">while</span> delta <span style="color: #303030;">></span> epsilon:
delta <span style="color: #303030;">/=</span> <span style="color: #0000d0; font-weight: bold;">2</span>
med <span style="color: #303030;">=</span> inf <span style="color: #303030;">+</span> delta
x <span style="color: #303030;">=</span> math<span style="color: #303030;">.</span>log(med) <span style="color: #303030;">+</span> math<span style="color: #303030;">.</span>sqrt(med)
<span style="color: green; font-weight: bold;">if</span> x <span style="color: #303030;"><</span> <span style="color: #6000e0; font-weight: bold;">5.0</span>:
inf <span style="color: #303030;">+=</span> delta
<span style="color: green; font-weight: bold;">return</span> inf
</pre>
</div>
<br />
Conferindo:
<br />
<br />
<div style="background: #f0f0f0; color: black; overflow: auto; padding: .2em .6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>smart_search(<span style="color: #6000e0; font-weight: bold;">1e-4</span>)
<span style="color: #6000e0; font-weight: bold;">8.3093709690729156</span>
<span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>smart_search(<span style="color: #6000e0; font-weight: bold;">1e-10</span>)
<span style="color: #6000e0; font-weight: bold;">8.309432694193374</span>
<span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>smart_search(<span style="color: #6000e0; font-weight: bold;">1e-15</span>)
<span style="color: #6000e0; font-weight: bold;">8.3094326942315693</span>
<span style="color: #303030;">>>></span> binary<span style="color: #303030;">.</span>smart_search(<span style="color: #6000e0; font-weight: bold;">1e-30</span>)
<span style="color: #6000e0; font-weight: bold;">8.3094326942315693</span>
</pre>
</div>
<br />
Agora sim!<br />
<br />
No fim das contas, é importante você ter a matemática sempre afiada, mas também é igualmente importante conhecer os limites impostos pela computação do mundo real.<br />
<br />
<i>(Agradecimentos ao Karlisson, ao Henrique, ao Wladimir, ao Chester e ao povo da lista eng-misc pela ajuda na pesquisa!)</i></div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com13tag:blogger.com,1999:blog-6306509703738480474.post-80930084921749886072012-05-22T00:57:00.000-03:002012-05-22T22:24:40.175-03:00A Terra Plana<div dir="ltr" style="text-align: left;" trbidi="on">
Todo mundo já ouviu a história de que foi Colombo que confirmou que a Terra era redonda, e que antes dele as pessoas achavam que a Terra era plana. Porém, isso é um mito! Desde a época dos gregos já se sabia que a Terra é redonda.<br />
<br />
Mesmo para os povos antigos as evidências eram fáceis de serem encontradas. Por indução, se o Sol é redondo e a Lua é redonda, por que a Terra não seria? Mais diretamente, durante um eclipse lunar, é possível ver claramente que a sombra projetada pela Terra é redonda.<br />
<br />
Nem mesmo a Igreja disputava essa interpretação. Santo Agostinho, por exemplo, <a href="http://www.ccel.org/ccel/schaff/npnf102.iv.XVI.9.html">sabia que a Terra era esférica</a>, embora não acreditasse que houvesse gente morando do outro lado do globo. São Tomás de Aquino começa a sua <a href="http://www.ccel.org/a/aquinas/summa/FP/FP001.html">Summa Theologica</a> citando duas diferentes demonstrações de que a Terra é redonda.<br />
<br />
"Mas e aquela história de que os marinheiros da época tinham medo de cair pela borda da Terra?" Bem, para quem não entende como a gravidade funciona, é possível ter esse medo mesmo com a Terra sendo redonda! Se a gravidade puxa sempre para baixo, então você cai quando chega no Equador:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyvPZeEd3rkkUNEqUFST0CRzhmE70GVc6Xdd9ln-XFgT9VJlTBDD47phiRJUSb1dOTjTgJUS7gsJFIs0kt7nIP12HqWxfMEp8LT8hVtAknYW9Dffj1I3Y4odtzjVdW1Oc-Vr6RX8O_WsM/s1600/terra_plana_01+(1).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyvPZeEd3rkkUNEqUFST0CRzhmE70GVc6Xdd9ln-XFgT9VJlTBDD47phiRJUSb1dOTjTgJUS7gsJFIs0kt7nIP12HqWxfMEp8LT8hVtAknYW9Dffj1I3Y4odtzjVdW1Oc-Vr6RX8O_WsM/s320/terra_plana_01+(1).jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Aparentemente, todo esse mito começou com a publicação de "<a href="http://en.wikipedia.org/wiki/A_History_of_the_Life_and_Voyages_of_Christopher_Columbus">As Vida e as Viagens de Cristóvão Colombo</a>", que você pode <a href="http://books.google.com.br/books?id=uIADAAAAQAAJ&printsec=frontcover&hl=pt-BR&source=gbs_ge_summary_r&cad=0#v=onepage&q&f=false">ler integralmente no Google Books</a>. O livro realmente cita que o senso comum era de que a Terra era plana. O problema é que todo mundo achou que esse era um livro de história, quando na verdade era ficção! (Concluí-se, portanto, que o autor desse livro foi um precursor do <a href="http://www.theonion.com/">The Onion</a>).<br />
<br />
Hoje em dia ainda há quem acredite que a Terra é plana, desde os ingênuos que nunca tiveram acesso à educação, até os malucos que formam seitas como a <a href="http://theflatearthsociety.org/cms/">Sociedade da Terra Plana</a>. Talvez você conheça alguém assim. Na verdade, é bem possível que <b>você</b> acredite na Terra Plana. Sim, você que está lendo esse texto!<br />
<br />
Se você acha que não acredita na Terra plana, então tente responder o meu teste: Suponha que você está usando um estilingue para arremessar passarinhos em porquinhos. Na ausência de atrito com o ar e outras formas de perdas dissipativas, qual a curva que o passarinho descreve em seu trajeto?<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXPp3h4sDI6P6WvkmfOH8YPvPNIUHVWhQwJKDwWh4LGYw8xxo0QA2-STubsOcsFkAh0beOF-JXndbD6u8IMvX4VaCqdkvhRKATbYp91dRrkOv2AtaX-ZUOLGvOtDuujNE1GQeDHJw6Q-I/s1600/terra_plana_02.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="275" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXPp3h4sDI6P6WvkmfOH8YPvPNIUHVWhQwJKDwWh4LGYw8xxo0QA2-STubsOcsFkAh0beOF-JXndbD6u8IMvX4VaCqdkvhRKATbYp91dRrkOv2AtaX-ZUOLGvOtDuujNE1GQeDHJw6Q-I/s400/terra_plana_02.jpg" width="400" /></a></div>
<br />
Se você respondeu que a curva é uma <b>parábola</b>, surpresa! Você acabou de assumir que a Terra é plana!<br />
<br />
"Mas peraí! Como assim?! Foi isso que meu professor ensinou!". Pois é, sempre é bom ter uma dose de ceticismo quando te ensinam alguma coisa. Na verdade, a prova de que a trajetória é uma parábola requer a existência de uma força gravitacional do tipo <b>F=mg</b>, onde <b>g</b> é uma constante de aproxidamente 9.81 m/s<sup>2</sup>.<br />
<br />
A pegadinha é que esse <b>g</b> não é uma constante! Ele varia com a altura, e, levando isso em conta, a curva não é mais uma parábola. Porém, se você assumir que a Terra é um plano infinito, aí sim a curva é uma parábola, porque nesse caso <b>g</b> é uma constante independente da altura.<br />
<br />
E como provar que o <b>g</b> é realmente constante para uma Terra plana? Existem várias maneiras: se você souber um pouco de cálculo, pode usar o <b>método do guerreiro</b>, que envolve várias páginas de lutas contra integrais duplas. Se você for um mestre do cálculo, pode tentar o <b>método do mago</b>, que resolve com poucas contas, mas requer o uso de teoremas mágicos. Agora, se você não souber cálculo, o jeito é apelar para <b>método do clérigo</b>: acredite em mim e pule o quadro azul abaixo :)<br />
<br />
<div style="background-color: #f0f0ff; border: 1px dashed black; clear: both; padding: 10px;">
<b>O Método do Guerreiro</b><br />
<br />
Antes de começar, convém lembrar a lei da gravidade de Newton, que é proporcional às massas e inversamente quadrática com a distância entre elas:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/c8mmfrc.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/c8mmfrc.png" /></a></div>
<br />
Como a lei tem simetria radial, as contas ficam mais fáceis se explorarmos essa simetria. Vamos calcular qual é a atração gravitacional causada por um disco de raio <b>R</b> e espessura infinitesimal, composto de materia uniforme com densidade <b>σ</b>, sobre um passarinho de massa <b>m<sub>P</sub></b> que esta a uma altura <b>h</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYLOcIHNTURnCSVbhJJ64eUWzLn3efzHg7kFri3kxnaqFXI_CZgz65UtjPwN0aaZ9CAMJJr4Q0E5tTsqtYcof4ZGm-Oy4p0WiNHvhIEBpogl-et7_TbF5oxJatnl_GmZEN_mxJt_r_AVYE/s1600/terra_plana_03+(1).png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYLOcIHNTURnCSVbhJJ64eUWzLn3efzHg7kFri3kxnaqFXI_CZgz65UtjPwN0aaZ9CAMJJr4Q0E5tTsqtYcof4ZGm-Oy4p0WiNHvhIEBpogl-et7_TbF5oxJatnl_GmZEN_mxJt_r_AVYE/s320/terra_plana_03+(1).png" width="269" /></a></div>
<br />
Nós vamos picotar o disco em pedacinhos de massa <b>dm</b> e integrar sobre todos os pedacinhos. Como as dimensões de cada pedacinho são infinitesimais, então eles podem ser considerados pequenos retângulos, onde um dos lados vale <b>dr</b>, e o outro vale um pequeno comprimento de arco. Um comprimento de arco é igual ao raio vezes o ângulo, logo esse lado vale <b>r.dϕ</b> . A massa do pedacinho, então, é a densidade vezes a área: <b>σ.r.dr.</b><b>dϕ.</b><br />
<br />
Além disso, se eu tenho o raio <b>r</b> e a altura <b>h</b>, por Pitágoras eu sei quanto vale <b>d<sup>2</sup></b>. A força total sobre o pedacinho é:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/bsh8swq.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/bsh8swq.png" /></a></div>
<br />
Antes de continuar, vamos decompor a força total em componentes radial (<b>F<sub>r</sub></b>) e vertical (<b>F<sub>h</sub></b>). Agora a simetria do problema se mostra útil, basta notar que para cada <b>dm</b>, tem um outro <b>dm</b> numa posição simétrica, do outro lado do disco; e nesse <b>dm</b> espelhado, o <b>F<sub>r</sub></b> vai ter o mesmo módulo e direção, mas sinal oposto. Então todos os <b>F<sub>r</sub></b> vão cancelar, e a força total sobre o passarinho vai ser apenas a soma dos <b>F<sub>h</sub></b> de cada <b>dm</b>.<br />
<br />
E quanto vale o <b>F<sub>h</sub></b>? É a força total <b>F</b> vezes o <b>cos θ</b>, que por sua vez é o lado adjacente pela hipotenusa. Logo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/cxlukle.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/cxlukle.png" /></a></div>
<br />
Para calcular o <b>F<sub>h</sub></b> total, é só integrar para o <b>ϕ</b> variando de <b>0</b> a <b>2π</b>, e para o <b>r</b> variando de <b>0</b> a <b>R</b>:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/7axxz5e.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/7axxz5e.png" /></a></div>
<br />
Todo mundo é constante em relação a <b>ϕ</b>, então dá pra jogar pra fora, e a integral em <b>dϕ</b> fica simples:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/c5a8yke.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/c5a8yke.png" /></a></div>
<br />
Para continuar, uma mudança de variável:<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/bw8ea47.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/bw8ea47.png" /></a></div>
<br />
O meio joga pra fora e cancela com o dois, o resto é um monômio simples, para integrar você soma um no expoente e passa dividindo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6wt3mm4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6wt3mm4.png" /></a></div>
<br />
A ultima expressão é a fórmula final da força gerada por um disco uniforme. Por fim, basta notar que o plano é o limite do disco quando o raio vai para o infinito:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6wp4cng.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6wp4cng.png" /></a></div>
<br />
Se o <b>R</b> vai para o infinito, então a primeira fração vai para zero, logo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/c38pu62.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/c38pu62.png" /></a></div>
<br />
Nós estamos interessados no caso em que <b>h</b> é positivo, logo a fração simplifica e chegamos na forma final da força devida a um plano:<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/cjybzk5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/cjybzk5.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Como esperado, a força <b>F</b> não depende da altura <b>h</b>!</div>
<br />
<br />
<div class="separator" style="clear: both; text-align: left;">
<b>O Método do Mago</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<div class="separator" style="clear: both;">
Ao invés de usar a lei da gravidade de Newton, podemos usar a <a href="http://en.wikipedia.org/wiki/Gauss's_law_for_gravity">lei da gravidade de Gauss</a>, que diz que a integral do campo gravitacional sobre uma superfície fechada é proporcional a massa contida dentro dela:</div>
<div class="separator" style="clear: both;">
<span class="Apple-tab-span" style="white-space: pre;"> </span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/6tq4nka.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/6tq4nka.png" /></a></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Essas duas leis são equivalentes, pode-se usar uma para provar a outra. Mas em alguns casos a lei de Gauss é mais prática, especialmente se você puder bolar uma superfície que explore as simetrias do problema. No nosso caso, a superfície mágica é um cilindro centrado no plano, com as bases paralelas a ele:</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ3cC3KWLL7oFqPOqAOVqkQvqmWvwfhrwOF3dXCa_yrYXG8Zob1AEcvyIB9CkkqNzdmC279ZcD2Nf_EIsJXGGQnPI50zBxCEZ6JWSR7rr1x1f8gGBiFp_CE1np7X-P4hocUJ8o1aUEKGk/s1600/terra_plana_04.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ3cC3KWLL7oFqPOqAOVqkQvqmWvwfhrwOF3dXCa_yrYXG8Zob1AEcvyIB9CkkqNzdmC279ZcD2Nf_EIsJXGGQnPI50zBxCEZ6JWSR7rr1x1f8gGBiFp_CE1np7X-P4hocUJ8o1aUEKGk/s200/terra_plana_04.png" width="179" /></a></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Antes de mais nada precisamos da massa dentro da superfície. Vamos chamar a área da base do cilindro de <b>A</b>. Como o plano tem densidade <b>σ</b>, entao a massa dentro da superfície fechada é <b>M = A.σ</b>.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Nós já vimos antes que, pela simetria radial, todas as forças resultantes são perpendiculares ao plano. Na lateral do cilindro, todos os elementos de área <b>dA</b> são normais à lateral, logo são perpendiculares às forças, e portanto o produto escalar deles dá zero.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Além disso, também há uma simetria em relação à translações no plano, por isso todas as forças atuando a uma mesma altura são iguais entre si. Dessa maneira, a integral de superfície em cada base do cilindro vale esse valor constante (chamemos de <b>F</b>) vezes a área <b>A</b>.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Juntando tudo, temos:</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://mathurl.com/79dujy7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://mathurl.com/79dujy7.png" /></a></div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
Esse é o mesmo resultado do método anterior, mas com bem menos contas!</div>
</div>
<br />
Mas se na Terra redonda a curva não é uma parábola, então que curva ela é? Bem, se você assumir que a Terra é uma esfera perfeita, homogênea, sem campos magnéticos, e assumir que não há atrito com ar e que ninguém está girando, então a curva correta é uma <b>elipse</b>.<br />
<br />
Para quem <a href="http://blog.ricbit.com/2011/04/as-tres-leis-de-newton.html">leu o Principia</a>, este é um resultado intuitivo. Na Proposição 71 do Livro 1, o Newton demonstra que a força resultante da atração por uma esfera homogênea é a mesma força de quando você concentra toda a massa da esfera num ponto material em seu centro. Daí o problema se reduz à atração entre dois pontos materiais, que sabemos ser uma elipse.<br />
<br />
Esse raciocínio também serve para responder a uma dúvida comum da criançada: se a gravidade da Terra puxa tudo para o centro, por que os satélites entram em órbita ao invés de cair? A resposta mais imediata é que na verdade <b>tudo </b>que você joga pra cima entra em órbita, mas algumas órbitas são tão excêntricas que o objeto bate de volta na superfície da Terra.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzzueJi-A68IlTydGNM2_UsPYWSmN41mGdlmAXH9zMYoNGf5gt0jHLGrZ38W8exaptKkskAe_9XQ_ozMfumH4pD_IFKS-mqITHqJ8dnMGlbZaY4GrPBpX3IGh-ktHLkcfiQCKCXcYRcbUb/s1600/terra_plana_05+(1).jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzzueJi-A68IlTydGNM2_UsPYWSmN41mGdlmAXH9zMYoNGf5gt0jHLGrZ38W8exaptKkskAe_9XQ_ozMfumH4pD_IFKS-mqITHqJ8dnMGlbZaY4GrPBpX3IGh-ktHLkcfiQCKCXcYRcbUb/s320/terra_plana_05+(1).jpg" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Eu não gosto muito da maneira que ensinam Física no Brasil (o Feynman <a href="http://physicsact.wordpress.com/2009/03/27/richard-feynman-e-o-ensino-da-fisica-no-brasil/">também não</a>). Felizmente, hoje em dia você sempre pode remediar a sua formação usando os recursos online, como a <a href="http://en.wikipedia.org/wiki/Main_Page">Wikipedia</a> e a <a href="http://www.khanacademy.org/science/physics">Khan Academy</a>!</div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com6tag:blogger.com,1999:blog-6306509703738480474.post-52764644403641101242011-07-31T23:14:00.001-03:002011-07-31T23:20:11.383-03:00Metaprogramming com constexpr<div dir="ltr" style="text-align: left;" trbidi="on">Até ano passado, quando eu queria fazer algum projetinho onde eu precisava da maior velocidade possível, acabava escolhendo escrevê-lo em C++. Mas a minha vida mudou quando eu conheci o <a href="http://en.wikipedia.org/wiki/C%2B%2B0x">C++0x</a>. Ele é muito superior ao C++ tradicional, adicionando features que faziam muita falta, como <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions">lambdas</a>, <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Type_inference">variáveis auto</a> e <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Range-based_for-loop">range for</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdRNV3cyouy28Ec7RockZ8e3HiWSvh8UyFUN9HZV_z-qsocyJ799HlzzJ9CKalVyXh921YqV3mUFVqAFz6Pu3bVnW3uFRBrS2xm9C2fsBcaZBUC0VSY7Hm_hup8mfU-Ixyrzfci6KDw4w/s1600/flash+%25281%2529.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgdRNV3cyouy28Ec7RockZ8e3HiWSvh8UyFUN9HZV_z-qsocyJ799HlzzJ9CKalVyXh921YqV3mUFVqAFz6Pu3bVnW3uFRBrS2xm9C2fsBcaZBUC0VSY7Hm_hup8mfU-Ixyrzfci6KDw4w/s400/flash+%25281%2529.jpg" width="400" /></a></div><br />
Uma das features novas que mais gosto é o <a href="http://en.wikipedia.org/wiki/C%2B%2B0x#Generalized_constant_expressions">constexpr</a>, uma maneira de avisar ao compilador que o resultado de função pode ser determinado em tempo de compilação. Combinando isso com funções recursivas, nós temos uma maneira nova de fazer metaprogramming em C++, bem melhor que o tradicional <a href="http://en.wikipedia.org/wiki/Template_metaprogramming">template metaprogramming</a>.<br />
<br />
Para mostrar a superioridade do constexpr, resolvi escrever de três maneiras diferentes uma função bem simples: o <a href="http://en.wikipedia.org/wiki/Euler's_totient_function">totiente de Euler</a>, cuja definição é a quantidade de inteiros positivos menores que <b>n</b>, e que não possuem fatores em comum com <b>n</b>. Escrito em C++ tradicional, essa função fica assim:<br />
<br />
<pre style="background: #f0f0f0; color: black; padding: 5px;"><span style="color: maroon; font-weight: bold;">int</span> gcd<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> a<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> b<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>b <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">return</span> a<span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">else</span>
<span style="color: maroon; font-weight: bold;">return</span> gcd<span style="color: #808030;">(</span>b<span style="color: #808030;">,</span> a <span style="color: #808030;">%</span> b<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">int</span> totient<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">int</span> sum <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">for</span> <span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> i <span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: purple;">;</span> i <span style="color: #808030;"><</span> x<span style="color: purple;">;</span> i<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">if</span> <span style="color: #808030;">(</span>gcd<span style="color: #808030;">(</span>x<span style="color: #808030;">,</span> i<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span> sum<span style="color: #808030;">+</span><span style="color: #808030;">+</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">return</span> sum<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: #603000;">cout</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> totient<span style="color: #808030;">(</span>1000<span style="color: #808030;">)</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> endl<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre><br />
O código em si é bem simples de entender e compila em qualquer lugar. Eu calculei o <a href="http://en.wikipedia.org/wiki/Greatest_common_divisor">GCD</a> usando o <a href="http://en.wikipedia.org/wiki/Euclidean_algorithm">algoritmo de Euclides</a>, que é O(log n), então essa implementação do totiente é O(n log n).<br />
<br />
Compare agora com o mesmo algoritmo escrito em template metaprogramming:<br />
<br />
<pre style="background: #f0f0f0; color: black; padding: 5px;"><span style="color: maroon; font-weight: bold;">template</span><span style="color: purple;"><</span><span style="color: maroon; font-weight: bold;">int</span> a<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> b<span style="color: purple;">></span>
<span style="color: maroon; font-weight: bold;">struct</span> GCD <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span> GCD<span style="color: #808030;"><</span>b<span style="color: #808030;">,</span> a <span style="color: #808030;">%</span> b<span style="color: #808030;">></span><span style="color: purple;">::</span>result<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">template</span><span style="color: purple;"><</span><span style="color: maroon; font-weight: bold;">int</span> a<span style="color: purple;">></span>
<span style="color: maroon; font-weight: bold;">struct</span> GCD<span style="color: purple;"><</span>a<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: purple;">></span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span> a<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">template</span><span style="color: purple;"><</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> i<span style="color: purple;">></span>
<span style="color: maroon; font-weight: bold;">struct</span> TotientHelper <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span>
<span style="color: #808030;">(</span>GCD<span style="color: purple;"><</span>x<span style="color: #808030;">,</span> i<span style="color: purple;">></span><span style="color: purple;">::</span>result <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: #808030;">+</span>
TotientHelper<span style="color: #808030;"><</span>x<span style="color: #808030;">,</span> i <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">></span><span style="color: purple;">::</span>result<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">template</span><span style="color: purple;"><</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: purple;">></span>
<span style="color: maroon; font-weight: bold;">struct</span> TotientHelper<span style="color: purple;"><</span>x<span style="color: #808030;">,</span> <span style="color: #008c00;">0</span><span style="color: purple;">></span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span> <span style="color: #008c00;">0</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">template</span><span style="color: purple;"><</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: purple;">></span>
<span style="color: maroon; font-weight: bold;">struct</span> Totient <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">static</span> <span style="color: maroon; font-weight: bold;">int</span> result <span style="color: #808030;">=</span>
TotientHelper<span style="color: #808030;"><</span>x<span style="color: #808030;">,</span> x <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">></span><span style="color: purple;">::</span>result<span style="color: purple;">;</span>
<span style="color: purple;">}</span><span style="color: purple;">;</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">const</span> <span style="color: maroon; font-weight: bold;">int</span> x <span style="color: #808030;">=</span> Totient<span style="color: purple;"><</span>1000<span style="color: purple;">></span><span style="color: purple;">::</span>result<span style="color: purple;">;</span>
<span style="color: #603000;">cout</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> x <span style="color: #808030;"><</span><span style="color: #808030;"><</span> endl<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre><br />
Essa versão é bem mais difícil de entender e é cheia de boilerplate. A vantagem dela é que o cálculo do totiente é feito em tempo de compilação; então, em runtime, o resultado é calculado em O(1).<br />
<br />
Por fim, a versão em c++0x usando constexpr:<br />
<br />
<pre style="background: #f0f0f0; color: black; padding: 5px;">constexpr <span style="color: maroon; font-weight: bold;">int</span> gcd<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> a<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> b<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">return</span> b <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span> <span style="color: purple;">?</span> a <span style="color: purple;">:</span> gcd<span style="color: #808030;">(</span>b<span style="color: #808030;">,</span> a <span style="color: #808030;">%</span> b<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
constexpr <span style="color: maroon; font-weight: bold;">int</span> totient<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: #808030;">,</span> <span style="color: maroon; font-weight: bold;">int</span> i<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">return</span> i <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">0</span> <span style="color: purple;">?</span> <span style="color: #008c00;">0</span> <span style="color: purple;">:</span> <span style="color: #808030;">(</span>gcd<span style="color: #808030;">(</span>x<span style="color: #808030;">,</span> i<span style="color: #808030;">)</span> <span style="color: #808030;">=</span><span style="color: #808030;">=</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span> <span style="color: #808030;">+</span> totient<span style="color: #808030;">(</span>x<span style="color: #808030;">,</span> i <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
constexpr <span style="color: maroon; font-weight: bold;">int</span> totient<span style="color: #808030;">(</span><span style="color: maroon; font-weight: bold;">int</span> x<span style="color: #808030;">)</span> <span style="color: purple;">{</span>
<span style="color: maroon; font-weight: bold;">return</span> totient<span style="color: #808030;">(</span>x<span style="color: #808030;">,</span> x <span style="color: #808030;">-</span> <span style="color: #008c00;">1</span><span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: purple;">}</span>
<span style="color: maroon; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #808030;">(</span><span style="color: #808030;">)</span> <span style="color: purple;">{</span>
constexpr <span style="color: maroon; font-weight: bold;">auto</span> x <span style="color: #808030;">=</span> totient<span style="color: #808030;">(</span>NUMBER<span style="color: #808030;">)</span><span style="color: purple;">;</span>
<span style="color: #603000;">cout</span> <span style="color: #808030;"><</span><span style="color: #808030;"><</span> x <span style="color: #808030;"><</span><span style="color: #808030;"><</span> endl<span style="color: purple;">;</span>
<span style="color: purple;">}</span>
</pre><br />
Essa versão não é tão simples quanto a que calcula em runtime, mas também não é excessivamente complexa como a versão com templates.<br />
<br />
Para fazer metaprogramming com constexpr, você só precisa seguir algumas regras simples:<br />
<ul style="text-align: left;"><li>O corpo da função precisa ter um único return.</li>
<li>Você não pode usar if. Se você precisar de condicionais, tem que usar o operador ternário.</li>
<li>Você não pode usar for. Se você precisar de loops, tem que usar recursão.</li>
<li>Você não pode fazer atribuições a variáveis.</li>
<li><div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;">Uma pegadinha: ele só faz o cálculo em compile time se você atribuir o resultado a uma variável constexpr. Se você tentar fazer diretamente cout << totient(100), ele vai calcular em run-time.</div></li>
</ul>Além disso, obviamente, você precisa de um compilador que suporte constexpr. Até onde eu sei, o único que aceita constexpr é o gcc 4.7 ou superior.<br />
<br />
Mas a grande vantagem do constexpr sobre os templates não é só a facilidade de programação: é o tempo de compilação. Um programa com constexpr compila muito mais rápido que uma versão com templates. Para comparar o tempo de compilação, eu fiz um script que mede o tempo para vários valores do totiente:<br />
<br />
<a href="http://www.ricbit.com/code/totient_runtime.cc">Script em C++ para calcular o totiente em run-time</a><br />
<a href="http://www.ricbit.com/code/totient_template.cc">Script em C++ para calcular o totiente com templates</a><br />
<a href="http://www.ricbit.com/code/totient_constexpr.cc">Script em C++0x para calcular o totiente com constexpr</a><br />
<a href="http://www.ricbit.com/code/totient.py">Script em Python para medir o tempo dos programas acima</a><br />
<br />
Vamos primeiro comparar o tempo de compilação da versão em runtime com a versão em template:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4pKY4a12a842FoodHTUgNRmTDrUUPU4wbd_oSTmJBi4x_p4OQXuiFhHedB4daeo7kYDhG7BgtCgdqot7RWYJm3d6aJXrzd7OABTQEpODw9EC_21yGpqcHYskt3sbuUGtkgpyvhM0_3cgM/s1600/chart_2+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4pKY4a12a842FoodHTUgNRmTDrUUPU4wbd_oSTmJBi4x_p4OQXuiFhHedB4daeo7kYDhG7BgtCgdqot7RWYJm3d6aJXrzd7OABTQEpODw9EC_21yGpqcHYskt3sbuUGtkgpyvhM0_3cgM/s1600/chart_2+%25281%2529.png" /></a></div><br />
<br />
Como esperado, o tempo de compilação da versão em runtime é constante e bem pequeno. Já a versão em template é bem ruinzinha. Eu fiz uma <a href="http://en.wikipedia.org/wiki/Regression_analysis">regressão</a> para determinar que curva é essa, e o resultado é que é uma parábola (com <a href="http://en.wikipedia.org/wiki/Coefficient_of_determination">coeficiente de determinação</a> R<sup>2</sup>=0.999, ou seja, com bastante certeza).<br />
<br />
Agora a fraqueza do método com templates é evidente: o algoritmo era O(n log n) em runtime, virou O(n<sup>2</sup>) em compile-time. Veja como isso não acontece com a versão com constexpr:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixJuNUX5cvL1z2R9sPBiAdd1GKxSYiVWTG5iGogYuBNXPVSEz6YVETSvAdP3t7o16QxrfK9RwlqN13WdKfihS-npcf5MoInyFRG2JVU5BGJqDBCjIorpflGgJFNRWH4DQeIQ4Ws6uUlJAx/s1600/g_runtime_x_constexpr+%25281%2529.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixJuNUX5cvL1z2R9sPBiAdd1GKxSYiVWTG5iGogYuBNXPVSEz6YVETSvAdP3t7o16QxrfK9RwlqN13WdKfihS-npcf5MoInyFRG2JVU5BGJqDBCjIorpflGgJFNRWH4DQeIQ4Ws6uUlJAx/s1600/g_runtime_x_constexpr+%25281%2529.png" /></a></div><br />
<br />
Ah, agora sim! A versão com constexpr continua sendo O(n log n), só trocou o tempo de run-time pelo tempo de compilação. Comparado o template nem tem graça: para <b>n</b>=30000, a versão com constexpr compila em 1 segundo, e versão em template compila em 2 minutos.<br />
<br />
A essa altura já estamos convencidos de que o constexpr é o melhor jeito de implementar metaprogramming, mas ainda falta a pergunta crucial: na prática, pra que isso serve?<br />
<br />
Para mim, a utilidade mais imediata é pra conseguir primeiro lugar nos problemas do <a href="http://www.spoj.pl/">spoj</a>! Como o spoj só leva em conta o tempo de run-time para ordenar as soluções, se você jogar um pouco do tempo do run-time para a compilação, consegue subir algumas colocações no ranking :)</div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com7tag:blogger.com,1999:blog-6306509703738480474.post-20425236347271139022011-05-16T00:36:00.000-03:002011-05-16T00:36:50.362-03:00Torto Reverso<div dir="ltr" style="text-align: left;" trbidi="on">No fim do ano passado o meu Nintendo DS quebrou, após anos de bons serviços prestados. Eu pensei em consertar, mas dado que o <a href="http://www.nintendo.com/3ds">Nintendo 3DS</a> ia sair em março, achei que valia a pena simplesmente esperar. Porém, o que fazer nesse meio tempo?<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsJMXj7YGQcnqdp8HHojMKb_eq9JcuKoiCMlLDbn5SZ5gf4ESONR1Uilh0FYsDQOLW2_3bqnrwMmAwX2fH9aWX39MGv2e1vZaQT71x9L9pXbNKqwSE8Jf2BVSpMwBcNOMKMdKLnfcoNvS7/s1600/ricbit_ds.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsJMXj7YGQcnqdp8HHojMKb_eq9JcuKoiCMlLDbn5SZ5gf4ESONR1Uilh0FYsDQOLW2_3bqnrwMmAwX2fH9aWX39MGv2e1vZaQT71x9L9pXbNKqwSE8Jf2BVSpMwBcNOMKMdKLnfcoNvS7/s400/ricbit_ds.jpg" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><i><br />
</i></div>A minha solução foi ir à banca e comprar um punhado de cruzadinhas da Coquetel. No começo foi divertido, mas depois de algum tempo perdeu a graça: para quem está acostumado com a <a href="http://www.croco-puzzle.com/Shop/artikel.php?id=131">Denksel</a>, os puzzles da Coquetel são fáceis demais.<br />
<br />
Para aumentar a dificuldade dos puzzles, eu resolvi mudar as regras de alguns deles. A minha modificação predileta eu batizei de <b>Torto Reverso</b>. Antes de mostrar como funciona a minha variação, vamos relembrar as regras do Torto tradicional, tais como estão na revista. É dado um grid 3x6 de letras e a seguinte descrição:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz6sE2zGCjfScgTKml1iY6wrcR0h6dHyW38KPYE43cgo3V7x9VIPUIM2okgfW325mPzWbIGrIdYMbdSrK4N8JznL4aAjZOEO_isM2MjnBqG3600dOLzhEgeSbYezvRk0xrLwr1l6reixT9/s1600/torto_original.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz6sE2zGCjfScgTKml1iY6wrcR0h6dHyW38KPYE43cgo3V7x9VIPUIM2okgfW325mPzWbIGrIdYMbdSrK4N8JznL4aAjZOEO_isM2MjnBqG3600dOLzhEgeSbYezvRk0xrLwr1l6reixT9/s1600/torto_original.JPG" /></a></div>"Deve-se formar as palavras seguindo em todas as direções, sempre ligando as letras em seqüência direta, sem cruzar, sem pular e sem repetir a mesma letra (para que uma palavra tenha a mesma letra repetida, é necessário que esta letra também esteja duplicada no diagrama). Damos como exemplo uma palavra encontrada. Só valem palavras de cinco letras ou mais. Na solução, constam 30 palavras formadas com este diagrama."<br />
<br />
<div style="clear: both;">Se você tiver um bom vocabulário, esse puzzle não tem segredo. Muito mais difícil é o Torto Reverso: e se, ao invés de procurar palavras no grid, eu te der as palavras e pedir para achar um grid válido que contenha todas elas?</div><br />
Depois de fazer alguns desses na mão, eu fiquei com vontade de implementar um solver. Eu poderia ter feito um solver em C++ ou Python, mas pensei aqui comigo: "eis uma boa oportunidade para aprender <a href="http://en.wikipedia.org/wiki/Prolog">Prolog</a>!"<br />
<br />
Se você também não conhece Prolog, então acompanhe um pequeno tutorial antes de ver minha implementação do solver:<br />
<br />
<div dir="ltr" style="text-align: left;" trbidi="on"><b>Introdução ao Prolog</b><br />
<br />
Em linguagens de programação tradicionais, a metáfora que você usa para explicar é a da receita de bolo: programar é fazer uma seqüência de tarefas, como adicionar 200g de farinha e mexer até que a massa esteja consistente. Prolog usa um paradigma diferente, então a melhor metáfora é pensar que ele é um provador de teoremas: você entra com os axiomas e ele deriva os teoremas a partir deles.<br />
<br />
Por exemplo, podemos usar o Prolog para definir os <a href="http://en.wikipedia.org/wiki/Peano_axioms">axiomas de Peano</a>. Começamos com a definição de números naturais: zero é um número natural, e o sucessor de um natural também é um natural.<br />
<br />
<code>natural(0).<br />
natural(s(X)) :- natural(X).</code><br />
<br />
Com essas definições já podemos perguntar ao Prolog: será que 2, ou seja, o sucessor do sucessor do zero, é um número natural?<br />
<br />
<code>?- natural(s(s(0))).<br />
true.</code><br />
<br />
<div>Bacana! E o <a href="http://www.ricbit.com/mundobizarro/jiraiya.php">sucessor de Togakure</a>, é um número natural?</div><br />
<code>?- natural(s(togakure)).<br />
false.</code><br />
<br />
<div>Está certo, afinal, se Togakure não é um número natural então o sucessor também não pode ser. Vamos agora definir a adição: zero é o elemento neutro, e se você somar qualquer coisa com o sucessor de outra coisa, o resultado é o sucessor da soma das duas coisas.</div><br />
<code>add(A, 0, A).<br />
add(A, s(B), s(C)) :- add(A, B, C).</code><br />
<br />
<div>Podemos perguntar agora: será que 1+1=2? Será que 1+1=3?</div><br />
<code>?- add(s(0), s(0), s(s(0))).<br />
true .<br />
?- add(s(0), s(0), s(s(s(0)))).<br />
false.</code><br />
<br />
<div>Até agora só usamos o Prolog para testar a veracidade de sentenças, mas a diversão começa quando usamos o Prolog para completar sentenças. Sempre que você deixar uma variável livre numa sentença, o Prolog tenta completar a sentença para torná-la verdadeira. Por exemplo, quanto é 2+2?</div><br />
<code>?- add(s(s(0)), s(s(0)), X).<br />
X = s(s(s(s(0)))) .</code><br />
<br />
<div>Talvez a parte mais bacana do Prolog seja o fato da linguagem ser reversível. O mesmo programa que avalia a soma também é capaz de resolver uma equação. Por exemplo, qual é o número que somado com 2 dá 5?</div><br />
<code>?- add(X, s(s(0)), s(s(s(s(s(0)))))).<br />
X = s(s(s(0))) .</code><br />
<br />
<div>E qual é o número que somado com 1 dá 0?</div><br />
<code>?- add(X, s(0), 0).<br />
false.</code><br />
<br />
<div>O Prolog respondeu <i>false</i>, ou seja, no conjunto dos naturais não tem nenhum número que somado com 1 dá 0. Outra coisa bacana é que você pode deixar mais de uma variável livre. Por exemplo, quais são os pares de números cuja soma é 2?</div><br />
<code>?- add(A, B, s(s(0))).<br />
A = s(s(0)),<br />
B = 0 ;<br />
A = s(0),<br />
B = s(0) ;<br />
A = 0,<br />
B = s(s(0)) ;</code><br />
<br />
<div>Ou seja, as soluções possíveis são 2+0, 1+1 e 0+2.</div><br />
<b>Resolvendo o Torto Reverso</b><br />
<br />
Agora que sabemos o básico do Prolog, já temos o material necessário para resolver o puzzle! Note que eu usei como interpretador o <a href="http://www.swi-prolog.org/">SWI-Prolog</a>, porque ele tem a melhor biblioteca de built-ins dos Prologs que eu pesquisei. Tudo que estiver em verde são palavras reservadas do SWI-Prolog.<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFkGvJhTxT_dwHg-EMyrjgfhKOgZzY8Jwd2wLvrX4vWBSQ-fvLm1RlRt-03t2HKfQNG4mGbmBfU8_OIGFvoiQuR8HE0xKNGjwC4-QCt5-7z2VeUi9PD2trODU9HVtAIX3o9ZXmMVOTqgLv/s1600/contorcionismo.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFkGvJhTxT_dwHg-EMyrjgfhKOgZzY8Jwd2wLvrX4vWBSQ-fvLm1RlRt-03t2HKfQNG4mGbmBfU8_OIGFvoiQuR8HE0xKNGjwC4-QCt5-7z2VeUi9PD2trODU9HVtAIX3o9ZXmMVOTqgLv/s320/contorcionismo.jpg" width="320" /></a></div><div class="separator" style="clear: both; text-align: center;"><i><br />
</i></div>Em linguagens tradicionais você precisa descrever, passo a passo, um método para encontrar a solução do seu problema. Em Prolog é o contrário, você só precisa falar quais são as propriedades da sua solução, e o interpretador se vira para achar um método de solucionar.<br />
<br />
Por exemplo, nós podemos criar um predicado que é verdadeiro se o nosso grid é uma lista com 18 elementos. Mas como Prolog é reversível, se você deixar a variável do grid livre, ele vai criar um grid para você!<br />
<br />
<pre>validgrid(Grid) :-
<span style="color: #408040;">length</span>(Grid, 18).
?- validgrid(X).
X = [_G282, _G285, _G288, _G291, _G294,|...].</pre><br />
O Prolog não tinha informação suficiente para deduzir quem eram os elementos do grid, então colocou placeholders no lugar (_G282, _G285, etc).<br />
<br />
Note também que o Prolog não tem arrays bidimensionais, só listas. Por isso, vamos precisar de um predicado para checar se os índices do grid são válidos, e um para indexar o grid a partir dos índices:<br />
<br />
<pre>valid(I,J) :-
<span style="color: #408040;">between</span>(0, 2, I),
<span style="color: #408040;">between</span>(0, 5, J).
validletter(Grid, Letter, X, Y) :-
valid(X, Y),
Pos <span style="color: #408040;">is</span> X + Y * 3,
<span style="color: #408040;">nth0</span>(Pos, Grid, Letter).</pre><br />
Nesse trecho o importante é entender o comando <i>is</i>. Para o Prolog, 1+1*3 é diferente de 4, porque ele não faz a avaliação aritmética por default. Usando o comando <i>is</i>, você o força a avaliar a expressão.<br />
<br />
Agora vamos definir a conectividade do grid. Dada uma posição válida qualquer, nós podemos nos mover para qualquer das oito direções, desde que você não saia do grid. Para isso, podemos definir as oito possibilidades para um predicado <i>move</i>:<br />
<br />
<pre>move(I, J, left, X, J) :-
X <span style="color: #408040;">is</span> I - 1,
valid(X, J).
(...)
move(I, J, down_right, X, Y) :-
X <span style="color: #408040;">is</span> I + 1,
Y <span style="color: #408040;">is</span> J + 1,
valid(X, Y).</pre><br />
Assim, já podemos perguntar ao Prolog: saindo da posição 0,0, quais são as posições atingíveis?<br />
<br />
<pre>?- move(0, 0, Direction, X, Y).
Direction = right,
X = 1,
Y = 0 ;
Direction = down,
X = 0,
Y = 1 ;
Direction = down_right,
X = 1,
Y = 1.</pre><br />
Pronto, já temos as ferramentas básicas. Vamos descrever a nossa solução então. Nós queremos colocar uma palavra no grid, então quais as propriedades dessa palavra? Se a palavra tiver uma letra só, ela pode ser encaixada em qualquer posição do grid:<br />
<br />
<pre>word(Grid, [Letter], X, Y) :-
validletter(Grid, Letter, X, Y).</pre><br />
Agora, se ela tem várias letras, então você encaixa a primeira letra e faz a recursão para o resto da palavra, notando que o resto começa em um ponto que é um movimento válido a partir da primeira letra:<br />
<br />
<pre>word(Grid, [Letter | Tail], X, Y) :-
validletter(Grid, Letter, X, Y),
move(X, Y, _, I, J),
word(Grid, Tail, I, J).</pre><br />
Aqui temos mais dois operadores do Prolog: o operador pipe separa uma lista em head e tail, e o operador underscore significa "don't care", nós não ligamos para o que o Prolog vai encaixar ali.<br />
<br />
Isso já é o suficiente para encaixar uma palavra no grid, por exemplo, a partir da posição 0,0:<br />
<br />
<pre>solve(Word) :-
word(Grid, Word, 0, 0),
writegrid(Grid).
?- solve("torto").
tor
.ot
...
...
...
...</pre><br />
Eu pedi para imprimir só a primeira solução, mas nós poderíamos ter pedido para o Prolog imprimir todas as soluções. (Eu pulei a definição do writegrid, porque não é tão interessante.)<br />
<br />
Antes de encaixar o resto das palavras, precisamos adicionar o restante das regras. Uma regra que não lidamos até agora é que uma palavra não pode repetir uma posição do grid durante o trajeto. <br />
<br />
Para implementar essa restrição, podemos criar uma lista de posições já visitadas, e só permitir a inserção de uma letra se a posição dela ainda não estiver na lista. Vamos modificar nosso predicado <i>word</i> então:<br />
<br />
<pre>word(Grid, [Letter], Visited, X, Y) :-
validletter(Grid, Letter, X, Y),
\+ <span style="color: #408040;">member</span>(pos(X, Y), Visited).
word(Grid, [Letter | Tail], Visited, X, Y) :-
validletter(Grid, Letter, X, Y),
move(X, Y, _, I, J),
\+ <span style="color: #408040;">member</span>(pos(X, Y), Visited),
word(Grid, Tail, [pos(X, Y) | Visited], I, J).</pre><br />
A novidade aqui é o operador \+, que é simplesmente um "not". Ou seja, insira a nova letra somente se a posição dela não estiver na lista <i>Visited</i>.<br />
<br />
Mas ainda tem outra regra: a palavra não pode cruzar o próprio caminho. Na horizontal e na vertical isso não tem como acontecer, porque para isso ele teria que repetir uma posição. Mas pode acontecer na diagonal, como na figura abaixo:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXEdclkqBFm5dva-qDlhrjw9H2oRNfgiU5nAyA2EJihOvtpbuMBW8aVERWmfF67DokZrCujRwDNCfUnbu07EtbvmnLuA62CwK81SojegVnjWJQe3JparemZPbPBgqb-qEZMuodbtKIz3R/s1600/etna.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvXEdclkqBFm5dva-qDlhrjw9H2oRNfgiU5nAyA2EJihOvtpbuMBW8aVERWmfF67DokZrCujRwDNCfUnbu07EtbvmnLuA62CwK81SojegVnjWJQe3JparemZPbPBgqb-qEZMuodbtKIz3R/s1600/etna.JPG" /></a></div><div style="text-align: center;"><i>Etna não é um vulcão válido.</i></div><br />
Para evitar esse caso, podemos fazer uma nova lista. Sempre que passarmos na diagonal por um bloco delimitado por letras, adicionamos esse bloco na lista.<br />
<br />
Agora, para checar se andamos na diagonal, poderíamos verificar a direção que o Prolog deduziu no predicado <i>move.</i> Ao invés disso, vamos fazer de um jeito diferente:<br />
<br />
<pre>validBlock(X, _, X, _, Block, Block) :- !.
validBlock(_, Y, _, Y, Block, Block) :- !.
validBlock(X, Y, I, J, OldBlock, NewBlock) :-
Bx <span style="color: #408040;">is min</span>(X, I),
By <span style="color: #408040;">is min</span>(Y, J),
\+ <span style="color: #408040;">member</span>(pos(Bx, By), OldBlock),
NewBlock = [pos(Bx, By) | OldBlock].</pre><br />
Se um movimento repetir o X, ou se repetir o Y, então não é uma diagonal, e não precisamos adicionar nada na lista. Para isso, usamos o operador exclamação, que implementa um <a href="http://en.wikipedia.org/wiki/Cut_(logic_programming)">corte</a>.<br />
<br />
A idéia é simples: um predicado do tipo validBlock(0, 0, 0, 0, A, B) iria fazer match nas três instâncias declaradas, e por isso o Prolog iria deduzir três soluções para esse predicado. Mas, com o corte, ele desiste de procurar mais soluções após encontrar a primeira.<br />
<br />
Note também que, para definir unicamente um bloco, independente da direção da diagonal, basta pegar o mínimo das posições X e Y.<br />
<br />
Podemos agora incorporar esse predicado para chegar na versão final do <i>word</i>:<br />
<br />
<pre>word(Grid, [Letter], Visited, _, X, Y) :-
validletter(Grid, Letter, X, Y),
\+ <span style="color: #408040;">member</span>(pos(X, Y), Visited).
word(Grid, [Letter | Tail], Visited, Block, X, Y) :-
validletter(Grid, Letter, X, Y),
move(X, Y, _, I, J),
\+ <span style="color: #408040;">member</span>(pos(X, Y), Visited),
validBlock(X, Y, I, J, Block, NewBlock),
word(Grid,Tail,[pos(X, Y) | Visited],NewBlock,I,J).</pre><br />
Isso já encaixa qualquer palavra seguindo todas as regras! Agora é só falar para inserir uma lista de palavras, ao invés de uma palavra só, e terminamos nosso solver:<br />
<br />
<pre>wordlist(_, []).
wordlist(Grid, [Word | WordList]) :-
word(Grid, Word, [], [], _, _),
wordlist(Grid, WordList).
solve(WordList) :-
validgrid(Grid),
wordlist(Grid, WordList),
writegrid(Grid).
?- solve(["otica","dedal","tedio","cardeal","aldeia"]).
oti
dac
eda
tel
idr
oac</pre><br />
O solver em Prolog ficou curto, né? Note que nós descrevemos só a solução, e não o método de solução. Em outras linguagens você precisaria se preocupar com <a href="http://en.wikipedia.org/wiki/Backtracking">Backtracking</a>, <a href="http://en.wikipedia.org/wiki/A*_search_algorithm">A*</a>, <a href="http://en.wikipedia.org/wiki/Exact_cover">Exact Cover</a> e o escambau, enquanto que no Prolog isso é feito por baixo dos panos para você.<br />
<br />
Se você quiser rodar o solver em casa, aqui está o fonte completo:<br />
<br />
<a href="http://www.ricbit.com/code/torto.pro">Solução do Torto Reverso em Prolog</a><br />
<br />
A desvantagem de escrever em Prolog é que fica um pouco mais lento que as linguagens tradicionais. Com essa implementação mostrada, temos 30 palavras, onde cada uma tem uma posição inicial definida num grid de 18 posições, e no mínimo 5 letras, onde cada letra pode estar em 8 posições possíveis a partir da anterior.<br />
<br />
Ou seja, o espaço de busca é de (18*8<sup>4</sup>)<sup>30</sup>, o que dá um total de <span style="word-wrap: break-word;">106898987586074109109365096428568366376512477689679588996632532409238880450162648732507836110780231551768603122161241934850249808547434035647873024</span> grids possiveis. Daí, usar esse solver em Prolog para um grid com 30 palavras pode demorar um bocadinho.<br />
<br />
<b>Ricbit Jam #3</b><br />
<br />
Você acha que consegue fazer melhor que o Prolog usando sua linguagem predileta? Então participe do Ricbit Jam! Quem conseguir fazer o solver mais rápido do Torto Reverso vai ganhar de brinde um Boliche de Dedo oficial do Google:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Tq-rsAxSg1xG9uHmmwKy-gHn1p5tuqabLyXiXiq8XzFlkM1p1l3WAFpYmtFnSdOlG_ccEY0rwrEo5gxCOCiGseSb4dHRPxegZ8XQzCAZqkBMMolQKQ7JJX01jvBQ4TxnbLCH6X1okQ0c/s1600/boliche_dedo_google.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4Tq-rsAxSg1xG9uHmmwKy-gHn1p5tuqabLyXiXiq8XzFlkM1p1l3WAFpYmtFnSdOlG_ccEY0rwrEo5gxCOCiGseSb4dHRPxegZ8XQzCAZqkBMMolQKQ7JJX01jvBQ4TxnbLCH6X1okQ0c/s320/boliche_dedo_google.jpg" width="238" /></a></div><div class="separator" style="clear: both; text-align: center;"><i>Boliche de dedo do Google</i></div><br />
Para participar, é só ler as regras abaixo:<br />
<br />
<a href="http://www.ricbit.com/ricbitjam/ricbitjam3.html">Regras do Ricbit Jam #3</a><br />
<br />
Divirta-se, e boa sorte!</div></div><div class="blogger-post-footer"><script type="text/javascript">
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"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4036480-1");
pageTracker._initData();
pageTracker._trackPageview();
</script></div>Ricardo Bittencourthttp://www.blogger.com/profile/17393980440854756685noreply@blogger.com18