<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="pt-br"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://natamleao.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://natamleao.github.io/" rel="alternate" type="text/html" hreflang="pt-br" /><updated>2026-04-13T09:31:43+00:00</updated><id>https://natamleao.github.io/feed.xml</id><subtitle>© 2026 Natam Leão Ferreira</subtitle><author><name>Natam L. Ferreria</name></author><entry><title type="html">Perceptron: onde redes neurais começam (e param)</title><link href="https://natamleao.github.io/perceptron-neural-network/" rel="alternate" type="text/html" title="Perceptron: onde redes neurais começam (e param)" /><published>2026-04-12T00:00:00+00:00</published><updated>2026-04-12T00:00:00+00:00</updated><id>https://natamleao.github.io/perceptron-neural-network</id><content type="html" xml:base="https://natamleao.github.io/perceptron-neural-network/"><![CDATA[<h2 id="o-ponto-de-partida">O ponto de partida</h2>

<p>O perceptron é um daqueles modelos que parecem simples demais à primeira vista.</p>

<p>Mas tem um detalhe importante:</p>

<blockquote>
  <p>ele já contém a ideia essencial de aprendizado</p>
</blockquote>

<p>Mesmo que de forma limitada.</p>

<hr />

<h2 id="mais-do-que-um-exercício">Mais do que um exercício</h2>

<p>A implementação em si não tem mistério.</p>

<p>Um somatório, uma função de ativação, ajuste de pesos.</p>

<p>Mas a intenção aqui não foi só implementar.</p>

<p>Foi entender o comportamento:</p>

<ul>
  <li>o que ele aprende</li>
  <li>como ele aprende</li>
  <li>onde ele simplesmente não consegue ir</li>
</ul>

<hr />

<h2 id="o-modelo">O modelo</h2>

<p>A decisão do perceptron é baseada em uma combinação linear das entradas:</p>

\[y = \begin{cases}
\;\;\,1, &amp; \text{se } \sum v_i x_i + b \ge 0 \\
-1, &amp; \text{caso contrário}
\end{cases}\]

<p>Isso define uma fronteira de decisão — uma linha (ou hiperplano, em dimensões maiores).</p>

<p>É só isso.</p>

<p>Esse modelo deriva diretamente da ideia de neurônio artificial, que descrevi <a href="https://natamleao.github.io/artificial-neuron">aqui</a>.</p>

<p>A função de ativação utilizada é um degrau bipolar. Também escrevi uma nota sobre isso <a href="https://natamleao.github.io/activation-functions">aqui</a>.</p>

<p>Durante o treinamento, os pesos são ajustados sempre que o modelo erra:</p>

\[\begin{cases}
v_i^{\text{novo}} = v_i^{\text{antigo}} + \alpha (t^{(k)} - y^{(k)}) x^{(k)} \\
b^{\text{novo}} = b^{\text{antigo}} + \alpha (t^{(k)} - y^{(k)})
\end{cases}\]

<p>Nada sofisticado — mas suficiente para capturar padrões simples.</p>

<hr />

<h2 id="onde-ele-funciona">Onde ele funciona</h2>

<p>Quando os dados podem ser separados por uma linha, ele converge.</p>

<p>Casos simples deixam isso claro:</p>

<ul>
  <li>AND</li>
  <li>OR</li>
</ul>

<p>Aqui, o modelo aprende rápido e se ajusta sem dificuldade.</p>

<hr />

<h2 id="onde-ele-quebra">Onde ele quebra</h2>

<p>O problema aparece quando a separação deixa de ser linear.</p>

<p>O exemplo clássico é o XOR.</p>

<p>Não importa o quanto você treine:</p>

<blockquote>
  <p>o modelo não converge</p>
</blockquote>

<p>E isso não é falha da implementação — é limitação do próprio modelo.</p>

<hr />

<h2 id="o-que-isso-mostra">O que isso mostra</h2>

<p>Esse ponto é mais interessante do que parece.</p>

<p>Porque ele revela algo fundamental:</p>

<blockquote>
  <p>o modelo não aprende “qualquer coisa” — ele aprende o que a sua forma permite</p>
</blockquote>

<p>No caso do perceptron, essa forma é linear.</p>

<p>Se o problema exige algo além disso, ele simplesmente não consegue representar.</p>

<hr />

<h2 id="visualização">Visualização</h2>

<p>A parte mais interessante é ver a fronteira de decisão se ajustando ao longo do treino.</p>

<div style="width: 100%;">
  <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
    <iframe src="https://www.youtube.com/embed/Pk5vEqu2-FY" frameborder="0" allowfullscreen="" style="position: absolute; top:0; left:0; width:100%; height:100%;">
    </iframe>
  </div>
</div>

<p>Ver isso acontecendo deixa o processo menos abstrato.</p>

<p>O modelo ajusta os pesos, move a fronteira e tenta corrigir erros — mas sempre dentro do que ele consegue representar.</p>

<hr />

<h2 id="por-que-esse-projeto">Por que esse projeto</h2>

<p>A ideia aqui foi bem direta:</p>

<ul>
  <li>sair da definição teórica</li>
  <li>implementar o aprendizado passo a passo</li>
  <li>visualizar o processo</li>
  <li>entender o limite do modelo na prática</li>
</ul>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>O perceptron é simples — e é justamente por isso que ele é útil.</p>

<p>Ele mostra o começo.</p>

<p>E também deixa claro onde esse começo deixa de ser suficiente.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Perceptron-Neural-Network">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="Python" /><category term="Machine Learning" /><category term="Neural Networks" /><summary type="html"><![CDATA[Implementação de um perceptron em Python para entender como modelos lineares aprendem — e onde falham.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/perceptron-thumbnail.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/perceptron-thumbnail.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Análise de algoritmos com perf e CPU profiling</title><link href="https://natamleao.github.io/complexity-vs-cpu-profiling/" rel="alternate" type="text/html" title="Análise de algoritmos com perf e CPU profiling" /><published>2026-04-08T00:00:00+00:00</published><updated>2026-04-08T00:00:00+00:00</updated><id>https://natamleao.github.io/complexity-vs-cpu-profiling</id><content type="html" xml:base="https://natamleao.github.io/complexity-vs-cpu-profiling/"><![CDATA[<h2 id="complexidade-teórica-vs-prática">Complexidade teórica <em>vs</em> prática</h2>

<p>A notação assintótica ($O(n)$, $O(n\log\,n)$, $O(n^2)$) descreve crescimento.</p>

<p>Mas não descreve <strong>como o código realmente se comporta na CPU</strong>.</p>

<p>Dois algoritmos com a mesma complexidade podem ter desempenhos completamente diferentes dependendo de:</p>

<ul>
  <li>padrão de acesso à memória</li>
  <li><em>branch prediction</em></li>
  <li>hierarquia de <em>cache</em> (L1/L2/L3)</li>
  <li>alocação dinâmica</li>
  <li>chamadas de função</li>
</ul>

<p>Aqui entra o <code class="language-plaintext highlighter-rouge">perf</code>.</p>

<hr />

<h2 id="o-objetivo">O objetivo</h2>

<p>A pergunta é simples:</p>

<blockquote>
  <p>Se a complexidade já descreve tudo, por que o comportamento real na CPU muda tanto?</p>
</blockquote>

<hr />

<h2 id="ferramenta-usada-perf">Ferramenta usada: <em>perf</em></h2>

<p>O <code class="language-plaintext highlighter-rouge">perf</code> permite observar o que realmente acontece no <em>hardware</em>:</p>

<ul>
  <li>ciclos de CPU</li>
  <li>instruções executadas</li>
  <li>IPC (<em>instructions per cycle</em>)</li>
  <li><em>branch misses</em></li>
  <li><em>hotspots</em> por função</li>
</ul>

<p>Isso transforma a análise de teórica para física.</p>

<hr />

<h2 id="ambiente-de-execução">Ambiente de execução</h2>

<ul>
  <li>CPU: AMD Ryzen 7 5825U (8 cores / 16 threads)</li>
  <li>RAM: 7.2 GiB</li>
  <li>Sistema: Debian GNU/Linux 12 (Bookworm)</li>
  <li>Arquitetura: x86-64</li>
  <li>Compilador: GCC com otimização <code class="language-plaintext highlighter-rouge">-O2</code></li>
  <li>Flags: <code class="language-plaintext highlighter-rouge">-O2 -Wall -Werror -g</code></li>
</ul>

<hr />

<h2 id="algoritmos-analisados">Algoritmos analisados</h2>

<ul>
  <li><em>Bubble Sort</em></li>
  <li><em>Optimized Bubble Sort</em></li>
  <li><em>Insertion Sort</em></li>
  <li><em>Selection Sort</em></li>
  <li><em>Merge Sort</em></li>
  <li><em>Quick Sort</em></li>
</ul>

<p>Todos executados sobre o mesmo conjunto de entrada.</p>

<hr />

<h2 id="métricas-analisadas">Métricas analisadas</h2>

<p>O <code class="language-plaintext highlighter-rouge">perf stat</code> foi usado com:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
cycles
instructions
branch-misses

</code></pre></div></div>

<p>E o perfil detalhado via <code class="language-plaintext highlighter-rouge">perf record + perf report</code>.</p>

<hr />

<h2 id="visão-geral-dos-resultados">Visão geral dos resultados</h2>

<h3 id="ipc-instructions-per-cycle">IPC (<em>Instructions per Cycle</em>)</h3>

<p>Algoritmos eficientes:</p>

<ul>
  <li><em>Quick Sort</em> $\to$ IPC mais baixo, mas execução rápida</li>
  <li><em>Merge Sort</em> $\to$ IPC moderado e estável</li>
</ul>

<p>Algoritmos $O(n²)$:</p>

<ul>
  <li><em>Bubble</em> / <em>Selection</em> / <em>Insertion Sort</em> $\to$ IPC variável, sem relação direta com eficiência</li>
</ul>

<p>Importante: IPC alto não significa desempenho melhor. Ele só indica eficiência de pipeline.</p>

<hr />

<h2 id="cache-e-memória"><em>Cache</em> e memória</h2>

<h3 id="merge-sort"><em>Merge Sort</em></h3>

<ul>
  <li>padrão de acesso sequencial</li>
  <li>boa localidade de <em>cache</em></li>
  <li>uso intenso de <code class="language-plaintext highlighter-rouge">memcpy/memmove</code></li>
</ul>

<p>Resultado:</p>

<ul>
  <li>bom comportamento em <em>cache</em></li>
  <li>custo relevante de memória, não de CPU pura</li>
</ul>

<hr />

<h3 id="quick-sort"><em>Quick Sort</em></h3>

<ul>
  <li>melhor desempenho geral</li>
  <li>comportamento depende da partição</li>
  <li>acesso menos previsível à memória</li>
</ul>

<p>Apesar disso:</p>

<ul>
  <li>baixa sobrecarga estrutural</li>
  <li>boa performance média em dados aleatórios</li>
</ul>

<hr />

<h3 id="bubble--selection--insertion-sort"><em>Bubble</em> / <em>Selection</em> / <em>Insertion Sort</em></h3>

<ul>
  <li>muitas comparações repetidas</li>
  <li>reuso intenso das mesmas regiões de memória</li>
  <li>pouca exploração eficiente de <em>cache</em></li>
</ul>

<p>Resultado:</p>

<ul>
  <li>desperdício de ciclos</li>
  <li>baixa eficiência global</li>
  <li>crescimento explosivo com $n$</li>
</ul>

<hr />

<h2 id="branch-prediction"><em>Branch prediction</em></h2>

<p>Algoritmos com muitos condicionais:</p>

<ul>
  <li><em>Quick Sort</em> (<code class="language-plaintext highlighter-rouge">partition</code>)</li>
  <li><em>Bubble Sort</em> (comparações constantes)</li>
</ul>

<p>sofrem com:</p>

<ul>
  <li><em>branch misprediction</em></li>
  <li><em>pipeline flush</em></li>
</ul>

<p>Isso aparece diretamente no <code class="language-plaintext highlighter-rouge">branch-misses</code>.</p>

<hr />

<h2 id="hotspots-reais-perf-report"><em>Hotspots</em> reais (<em>perf report</em>)</h2>

<p>Os principais pontos de execução:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">merge</code></li>
  <li><code class="language-plaintext highlighter-rouge">partition</code></li>
  <li><code class="language-plaintext highlighter-rouge">insertionSort</code></li>
  <li><code class="language-plaintext highlighter-rouge">bubbleSort</code></li>
</ul>

<p>Em alguns casos:</p>

<blockquote>
  <p>mais de 90% do tempo fica concentrado em uma única função</p>
</blockquote>

<p>Conclusão direta:</p>

<blockquote>
  <p>desempenho é sempre concentrado, nunca distribuído</p>
</blockquote>

<hr />

<h2 id="o-que-os-dados-mostram">O que os dados mostram</h2>

<p>Mesmo com a mesma complexidade:</p>

<ul>
  <li>algoritmos se comportam diferente no <em>hardware</em></li>
  <li>memória pode dominar o custo total</li>
  <li>chamadas internas podem ser gargalo dominante</li>
</ul>

<hr />

<h2 id="complexidade-vs-realidade">Complexidade <em>vs</em> realidade</h2>

<p>A teoria responde:</p>

<blockquote>
  <p>quanto o algoritmo cresce</p>
</blockquote>

<p>A CPU responde:</p>

<blockquote>
  <p>onde o tempo realmente está sendo gasto</p>
</blockquote>

<hr />

<h1 id="resultados-com-perf">Resultados com <em>perf</em></h1>

<hr />

<h2 id="merge-sort-1"><em>Merge Sort</em></h2>

<ul>
  <li>Tempo: ~23 ms</li>
  <li>IPC: 1.21</li>
  <li><em>Branch misses</em>: 7.80%</li>
  <li><em>Cycles</em>: 92.4M</li>
</ul>

<p><em>Hotspots</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">merge</code> → 73.69%</li>
  <li><code class="language-plaintext highlighter-rouge">memmove</code> → 9.24%</li>
  <li><code class="language-plaintext highlighter-rouge">malloc/free</code> → relevante</li>
</ul>

<p>comportamento dominado por memória</p>

<hr />

<h2 id="quick-sort-1"><em>Quick Sort</em></h2>

<ul>
  <li>Tempo: ~16 ms</li>
  <li>IPC: 0.90</li>
  <li><em>Branch misses</em>: 12.54%</li>
  <li><em>Cycles</em>: 66.5M</li>
</ul>

<p><em>Hotspots</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">partition</code> → 88.55%</li>
</ul>

<p>gargalo: previsibilidade de <em>branches</em></p>

<hr />

<h2 id="insertion-sort"><em>Insertion Sort</em></h2>

<ul>
  <li>Tempo: ~3.34 s</li>
  <li>IPC: 4.77</li>
  <li><em>Branch misses</em>: ~0%</li>
</ul>

<p><em>Hotspot</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">insertionSort</code> → 99.95%</li>
</ul>

<p>simples, mas extremamente ineficiente em escala</p>

<hr />

<h2 id="selection-sort"><em>Selection Sort</em></h2>

<ul>
  <li>Tempo: ~10.5 s</li>
  <li>IPC: 4.90</li>
  <li><em>Branch misses</em>: ~0.01%</li>
</ul>

<p><em>Hotspot</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">selectionSort</code> → 99.97%</li>
</ul>

<p>custo puramente $O(n²)$</p>

<hr />

<h2 id="bubble-sort"><em>Bubble Sort</em></h2>

<ul>
  <li>Tempo: ~99 s</li>
  <li>IPC: 0.41</li>
  <li><em>Branch misses</em>: 12.70%</li>
</ul>

<p><em>Hotspot</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">bubbleSort</code> → 99.99%</li>
</ul>

<p>pior cenário clássico confirmado na prática</p>

<hr />

<h2 id="optimized-bubble-sort"><em>Optimized Bubble Sort</em></h2>

<ul>
  <li>Tempo: ~98 s</li>
  <li>IPC: 0.44</li>
  <li><em>Branch misses</em>: 12.71%</li>
</ul>

<p><em>Hotspot</em>:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">optimizedBubbleSort</code> → 99.99%</li>
</ul>

<p>otimização local irrelevante em termos assintóticos</p>

<hr />

<h2 id="leitura-geral-dos-dados">Leitura geral dos dados</h2>

<h3 id="algoritmos-onlogn">Algoritmos $O(n\log\,n)$</h3>

<ul>
  <li>melhor desempenho absoluto</li>
  <li>melhor uso de CPU</li>
  <li>melhor escalabilidade</li>
</ul>

<h3 id="algoritmos-on">Algoritmos $O(n²)$</h3>

<ul>
  <li>crescimento dominante</li>
  <li>IPC não salva performance</li>
  <li>custo explode rapidamente</li>
</ul>

<hr />

<h2 id="o-ponto-mais-importante">O ponto mais importante</h2>

<p>A diferença real não é só a complexidade:</p>

<ul>
  <li><em>Quick Sort</em> $\to$ sensível a <em>branch prediction</em></li>
  <li><em>Merge Sort</em> $\to$ sensível a memória</li>
  <li><em>Bubble</em>/<em>Selection Sort</em> $\to$ redundância extrema</li>
</ul>

<p>Cada algoritmo falha por um motivo diferente no <em>hardware</em>.</p>

<hr />

<h2 id="conclusão">Conclusão</h2>

<p>A complexidade continua correta.</p>

<p>Mas incompleta.</p>

<p>Na prática:</p>

<ul>
  <li><em>cache</em> muda o jogo</li>
  <li>memória domina muitos algoritmos</li>
  <li><em>branch prediction</em> altera o desempenho real</li>
  <li>IPC revela eficiência interna</li>
</ul>

<hr />

<h2 id="síntese-final">Síntese final</h2>

<blockquote>
  <p>Dois algoritmos podem ter a mesma complexidade e comportamentos completamente diferentes na CPU.</p>
</blockquote>

<hr />

<h2 id="leitura-complementar">Leitura complementar</h2>

<p>Uma análise complementar explora o comportamento estatístico dos algoritmos de ordenação com múltiplas execuções, medindo variabilidade, desvio padrão e consistência dos tempos de execução. Você pode encontrá-la <a href="https://natamleao.github.io/sorting-benchmark/">aqui</a>.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Compare-Sorts">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Algorithms" /><category term="Profiling" /><summary type="html"><![CDATA[Análise de algoritmos de ordenação a nível de CPU utilizando perf, explorando IPC, cache misses, branch prediction e comportamento real de execução.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/perf-profiling.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/perf-profiling.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Comparando algoritmos de ordenação com análise estatística de desempenho</title><link href="https://natamleao.github.io/sorting-benchmark/" rel="alternate" type="text/html" title="Comparando algoritmos de ordenação com análise estatística de desempenho" /><published>2026-04-06T00:00:00+00:00</published><updated>2026-04-06T00:00:00+00:00</updated><id>https://natamleao.github.io/sorting-benchmark</id><content type="html" xml:base="https://natamleao.github.io/sorting-benchmark/"><![CDATA[<h2 id="a-comparação-é-simples-e-evidencia-a-diferença-teórica-de-complexidade">A comparação é simples e evidencia a diferença teórica de complexidade</h2>

<p><em>Bubble Sort</em>, <em>Insertion Sort</em>, <em>Selection Sort</em>, <em>Quick Sort</em> e <em>Merge Sort</em> aparecem em introdução à computação.</p>

<p>Alguns deles são simples. Outros são eficientes $O(n^2)\; \text{vs}\; O(n\log n)$.</p>

<p>Beleza. Isso quase todo mundo já viu. Mas isso normalmente fica só na teoria.</p>

<p>Aqui a ideia foi ir além:</p>

<blockquote>
  <p>colocar todos sob as mesmas condições e observar o comportamento real de execução</p>
</blockquote>

<hr />

<h2 id="como-montei-o-experimento">Como montei o experimento</h2>

<p>Nada muito “acadêmico”, mas com controle suficiente pra não distorcer os resultados:</p>

<ul>
  <li>todos os algoritmos recebem os mesmos dados</li>
  <li>cada execução é isolada</li>
  <li>medição com <code class="language-plaintext highlighter-rouge">clock_gettime</code> usando <code class="language-plaintext highlighter-rouge">CLOCK_MONOTONIC</code></li>
  <li>múltiplas execuções por entrada para reduzir ruído estatístico</li>
  <li>uso de média, desvio padrão e coeficiente de variação (CV)</li>
</ul>

<p>O coeficiente de variação (CV) — $\text{CV}=\frac{\sigma}{\mu}\times 100$ — é importante porque normaliza a dispersão em relação à média:</p>

<p>ele mostra o quanto o tempo oscila proporcionalmente ao valor médio</p>

<ul>
  <li>$\sigma \to \text{desvio padrão}$</li>
  <li>$\mu \to \text{média}$</li>
</ul>

<p>Isso evita uma armadilha comum:
desvio padrão parecer “grande” só porque o tempo médio também cresceu.</p>

<hr />

<h2 id="benchmark"><em>Benchmark</em></h2>

<p>Cada algoritmo não roda apenas uma vez.</p>

<p>O processo é:</p>

<ol>
  <li>o <em>array</em> original é gerado</li>
  <li>são criadas 100 execuções independentes</li>
  <li>cada execução ordena uma cópia dos dados</li>
  <li>calcula-se média, desvio padrão e coeficiente de variação (CV) do tempo</li>
</ol>

<p>Isso reduz ruído e melhora confiabilidade da medição.</p>

<hr />

<h2 id="geração-dos-dados">Geração dos dados</h2>

<p>Os valores são gerados aleatoriamente no intervalo: <code class="language-plaintext highlighter-rouge">[-1e6, 1e6]</code></p>

<p>E copiados para todos os algoritmos, garantindo:</p>

<ul>
  <li>mesma distribuição</li>
  <li>mesma dificuldade</li>
  <li>comparação justa</li>
</ul>

<hr />

<h2 id="ambiente-de-execução">Ambiente de execução</h2>

<ul>
  <li>CPU: AMD Ryzen 7 5825U (8 cores / 16 threads)</li>
  <li>RAM: 7.2 GiB</li>
  <li>Sistema: Debian GNU/Linux 12 (Bookworm)</li>
  <li>Arquitetura: x86-64</li>
  <li>Compilador: GCC com otimização <code class="language-plaintext highlighter-rouge">-O2</code></li>
  <li>Flags: <code class="language-plaintext highlighter-rouge">-O2 -Wall -Werror -fsanitize=address -g</code></li>
</ul>

<p>Isso é necessário deixar claro, o tempo depende e varia de máquina para máquina.</p>

<hr />

<h2 id="escalabilidade">Escalabilidade</h2>

<p>Em entradas pequenas:</p>

<ul>
  <li>tudo parece semelhante</li>
</ul>

<p>Mas quando o tamanho cresce:</p>

<blockquote>
  <p>a complexidade domina completamente o comportamento</p>
</blockquote>

<hr />

<h2 id="resultados-experimentais">Resultados experimentais</h2>

<p>Os resultados foram analisados sob três métricas principais:</p>

<ul>
  <li>média do tempo de execução</li>
  <li>desvio padrão entre execuções</li>
  <li>coeficiente de variação (CV)</li>
</ul>

<p>Essas métricas permitem avaliar não só o desempenho médio dos algoritmos, mas também a estabilidade das medições e a variabilidade relativa dos tempos.</p>

<hr />

<h2 id="gráficos-de-análise">Gráficos de análise</h2>

<p>Para os gráficos abaixo, os algoritmos $O(n^2)$ foram executados com os tamanhos de entrada: 10000, 20000, 50000, 100000 e 200000.
Já os algoritmos $O(n \log n)$ foram executados com os tamanhos: 10000, 20000, 50000, 100000, 200000, 500000 e 1000000.</p>

<h3 id="gráfico-1--tempo-médio-vs-tamanho-da-entrada"><strong>Gráfico 1 — Tempo médio vs tamanho da entrada</strong></h3>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CS/graphic - log-scale-time.svg" alt="Escala log" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Tempo médio para evidenciar crescimento assintótico
  </p>
</div>

<p>Neste gráfico, é possível observar claramente a diferença de escalabilidade entre os algoritmos.</p>

<p><em>Merge Sort</em> e <em>Quick Sort</em> apresentam crescimento significativamente mais lento, mantendo tempos baixos mesmo com o aumento do tamanho da entrada, o que está em linha com a complexidade $O(n\log\,n)$.</p>

<p>Por outro lado, <em>Bubble Sort</em> e <em>Optimized Bubble Sort</em> exibem crescimento acentuado, característico de algoritmos $O(n^2)$. A otimização aplicada ao <em>Bubble Sort</em> não resulta em ganho relevante de desempenho para entradas grandes, indicando que melhorias locais não alteram o comportamento assintótico do algoritmo.</p>

<p>Além disso, observa-se que o <em>Quick Sort</em> é consistentemente o algoritmo mais rápido entre todos os testados, enquanto os algoritmos quadráticos tornam-se rapidamente inviáveis conforme o tamanho da entrada aumenta.</p>

<hr />

<h3 id="gráfico-2--desvio-padrão-vs-tamanho-da-entrada"><strong>Gráfico 2 — Desvio padrão vs tamanho da entrada</strong></h3>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CS/graphic - stddev-vs-input.svg" alt="Desvio padrão" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Variabilidade dos tempos de execução conforme o tamanho da entrada
  </p>
</div>

<p>Neste gráfico, observa-se o comportamento da variabilidade dos tempos de execução à medida que o tamanho da entrada aumenta.</p>

<p>Algoritmos como <em>Merge Sort</em> e <em>Quick Sort</em> apresentam uma tendência de estabilização do desvio padrão, indicando maior consistência nas execuções mesmo para entradas maiores. Isso sugere que, além de eficientes, esses algoritmos são também mais previsíveis.</p>

<p>Por outro lado, algoritmos quadráticos como <em>Bubble Sort</em>, <em>Optimized Bubble Sort</em>, <em>Insertion Sort</em> e <em>Selection Sort</em> exibem maior instabilidade, com picos mais elevados de desvio padrão e crescimento mais acentuado à medida que o tamanho da entrada aumenta.</p>

<p>Esse comportamento indica que, além de mais lentos, esses algoritmos também são menos consistentes, sofrendo maior influência de fatores como distribuição dos dados, cache e variações de execução.</p>

<hr />

<h3 id="gráfico-3--coeficiente-de-variação-cv-vs-tamanho-da-entrada"><strong>Gráfico 3 — Coeficiente de variação (CV) vs tamanho da entrada</strong></h3>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CS/graphic - cv-vs-input.svg" alt="Coeficiente de variação" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Relação entre dispersão e média dos tempos de execução
  </p>
</div>

<p>Neste gráfico, observa-se a relação entre a variabilidade e o tempo médio de execução dos algoritmos.</p>

<p><em>Merge Sort</em> e <em>Quick Sort</em> apresentam valores baixos e relativamente estáveis, indicando comportamento consistente mesmo com o aumento do tamanho da entrada.</p>

<p>Por outro lado, os algoritmos quadráticos exibem maior variação relativa, especialmente em entradas maiores, evidenciando menor previsibilidade.</p>

<hr />

<h3 id="gráfico-4--comparação-empírica-com-complexidade-assintótica"><strong>Gráfico 4 — Comparação empírica com complexidade assintótica</strong></h3>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CS/graphic - asymptotic-comparison.svg" alt="Complexidade assintótica" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Comparação entre tempos reais e curvas teóricas $O(n²)$ e $O(n\log\,n)$
  </p>
</div>

<p>Neste gráfico, as curvas teóricas $O(n^2)$ e $O(n \log n)$ são ajustadas aos dados experimentais, permitindo a comparação direta entre comportamento real e crescimento assintótico.</p>

<p>Observa-se que o <em>Bubble Sort</em> acompanha de forma consistente a curva quadrática, apresentando crescimento mais acentuado à medida que o tamanho da entrada aumenta.</p>

<p>Já o <em>Quick Sort</em> segue de perto a curva $O(n\log\,n)$, mantendo crescimento significativamente mais controlado.</p>

<p>Essa correspondência evidencia que, apesar de abstrair constantes e detalhes de implementação, a análise assintótica descreve corretamente o comportamento dominante dos algoritmos na prática.</p>

<hr />

<h2 id="quando-algoritmos-deixam-de-ser-opção">Quando algoritmos deixam de ser opção</h2>

<p>Algoritmos $O(n^2)$:</p>

<ul>
  <li>crescem rapidamente</li>
  <li>tornam-se inviáveis em grandes entradas</li>
  <li>deixam de ser práticos muito cedo</li>
</ul>

<p>Já $O(n \log n)$:</p>

<ul>
  <li>escalam muito melhor</li>
  <li>continuam eficientes em grandes volumes</li>
</ul>

<hr />

<h2 id="o-que-fica-evidente">O que fica evidente</h2>

<ul>
  <li>a teoria aparece no comportamento</li>
  <li>diferenças pequenas viram enormes</li>
  <li>o crescimento domina completamente o tempo de execução</li>
</ul>

<hr />
<h2 id="síntese-dos-resultados">Síntese dos resultados</h2>

<p>Quando os gráficos são vistos em conjunto:</p>

<ul>
  <li>o tempo médio mostra o custo real</li>
  <li>o desvio padrão mostra estabilidade</li>
  <li>o CV mostra consistência relativa</li>
  <li>a escala logarítmica revela padrões de crescimento</li>
  <li>a comparação teórica valida o modelo matemático</li>
</ul>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>A teoria não é uma abstração aqui — ela aparece nos dados.</p>

<p>Quando se mede corretamente:</p>

<blockquote>
  <p>complexidade deixa de ser notação e vira comportamento observável</p>
</blockquote>

<hr />

<h2 id="leitura-complementar">Leitura complementar</h2>

<p>Uma análise complementar aprofunda o comportamento interno dos algoritmos a nível de CPU, utilizando <code class="language-plaintext highlighter-rouge">perf</code> para investigar IPC, <em>cache hierarchy</em> e <em>branch prediction</em>. Você pode encontrá-la <a href="https://natamleao.github.io/complexity-vs-cpu-profiling/">aqui</a>.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Compare-Sorts">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Algorithms" /><category term="Sorting" /><summary type="html"><![CDATA[Análise comparativa de algoritmos de ordenação em C utilizando benchmarks estatísticos com múltiplas execuções, medição de variabilidade e isolamento de dados para garantir consistência experimental.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/sorts-comparison.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/sorts-comparison.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Gerenciando tarefas e organizando estado em uma aplicação web</title><link href="https://natamleao.github.io/task-manager/" rel="alternate" type="text/html" title="Gerenciando tarefas e organizando estado em uma aplicação web" /><published>2026-04-02T00:00:00+00:00</published><updated>2026-04-02T00:00:00+00:00</updated><id>https://natamleao.github.io/task-manager</id><content type="html" xml:base="https://natamleao.github.io/task-manager/"><![CDATA[<h2 id="o-ponto-de-partida">O ponto de partida</h2>

<p>Esse projeto surgiu como uma tentativa de sair do código isolado e entrar em algo mais próximo de uma aplicação de verdade.</p>

<p>A ideia foi simples:</p>

<blockquote>
  <p>permitir que dados sejam criados, modificados e mantidos ao longo do tempo</p>
</blockquote>

<p>No caso, tarefas.</p>

<hr />

<h2 id="interface">Interface</h2>

<div style="text-align: center;">
  <img src="https://i.imgur.com/FdIszKB.png" alt="Página inicial do gerenciador" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
</div>

<p>A interface é direta, sem abstrações pesadas.</p>

<p>Isso deixa claro o fluxo completo:<br />
entrada → processamento → persistência → retorno.</p>

<hr />

<h2 id="mais-do-que-crud">Mais do que CRUD</h2>

<p>Na superfície, é um CRUD clássico:</p>

<ul>
  <li>criar</li>
  <li>visualizar</li>
  <li>atualizar</li>
  <li>remover</li>
</ul>

<p>Mas o interessante não é isso.</p>

<p>É o ciclo:</p>

<ul>
  <li>o usuário interage com a interface</li>
  <li>os dados são enviados</li>
  <li>o estado é atualizado</li>
  <li>a persistência garante que nada se perca</li>
</ul>

<p>Mesmo sendo simples, já forma um sistema completo.</p>

<hr />

<h2 id="visualização-das-tarefas">Visualização das tarefas</h2>

<div style="text-align: center;">
  <img src="https://i.imgur.com/MXCZoEG.png" alt="Página de visualização do gerenciador" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
</div>

<p>Aqui fica mais claro como os dados circulam.</p>

<p>Você cria, altera, remove — e o sistema precisa manter tudo consistente.</p>

<hr />

<h2 id="onde-as-coisas-se-conectam">Onde as coisas se conectam</h2>

<p>Diferente de programas locais, aqui existem camadas:</p>

<ul>
  <li>interface (HTML/CSS)</li>
  <li>lógica (JavaScript)</li>
  <li>servidor</li>
  <li>banco de dados</li>
</ul>

<p>Nada muito complexo isoladamente.</p>

<p>Mas o sistema depende de tudo isso funcionando junto.</p>

<hr />

<h2 id="persistência-muda-o-jogo">Persistência muda o jogo</h2>

<p>Usar um banco de dados adiciona um ponto importante:</p>

<blockquote>
  <p>os dados deixam de existir só na memória</p>
</blockquote>

<p>Eles passam a ter continuidade.</p>

<p>Isso exige:</p>

<ul>
  <li>consistência nas operações</li>
  <li>cuidado com atualização e remoção</li>
  <li>atenção ao fluxo entre cliente e servidor</li>
</ul>

<hr />

<h2 id="limitações-intencionais">Limitações (intencionais)</h2>

<p>O projeto não tenta ser um sistema completo.</p>

<ul>
  <li>sem autenticação</li>
  <li>backend simples</li>
  <li>modelo direto</li>
</ul>

<p>A ideia não era escalar — era entender o fluxo.</p>

<hr />

<h2 id="o-que-esse-projeto-representa">O que esse projeto representa</h2>

<p>Ele marca uma transição importante:</p>

<p>de programas isolados<br />
para aplicações com:</p>

<ul>
  <li>múltiplas camadas</li>
  <li>estado persistente</li>
  <li>interação contínua</li>
</ul>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>No fim, o sistema é simples.</p>

<p>Mas já exige algo além de código que “funciona”:</p>

<blockquote>
  <p>exige manter consistência ao longo do tempo</p>
</blockquote>

<p>E isso muda completamente o tipo de problema.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Task-Manager">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="JavaScript" /><category term="Web Development" /><category term="Persistence" /><summary type="html"><![CDATA[Um gerenciador de tarefas simples para explorar fluxo de dados, persistência e organização em aplicações web.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/price-comparison-c-thumbnail.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/price-comparison-c-thumbnail.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Servidor de comunicação e o problema de lidar com múltiplos clientes</title><link href="https://natamleao.github.io/server-client/" rel="alternate" type="text/html" title="Servidor de comunicação e o problema de lidar com múltiplos clientes" /><published>2026-04-01T00:00:00+00:00</published><updated>2026-04-01T00:00:00+00:00</updated><id>https://natamleao.github.io/server-client</id><content type="html" xml:base="https://natamleao.github.io/server-client/"><![CDATA[<h2 id="o-ponto-de-partida">O ponto de partida</h2>

<p>Esse projeto começou com uma ideia simples:</p>

<blockquote>
  <p>fazer dois programas conversarem</p>
</blockquote>

<p>Mas rapidamente isso evolui.</p>

<p>Porque no momento em que você sai de um cliente único e entra em múltiplos clientes, o problema muda completamente.</p>

<hr />

<h2 id="não-é-só-conexão">Não é só conexão</h2>

<p>Abrir um socket e trocar dados é direto.</p>

<p>O problema real começa quando você precisa lidar com:</p>

<ul>
  <li>múltiplas conexões ao mesmo tempo</li>
  <li>diferentes tipos de requisição</li>
  <li>respostas consistentes para cada cliente</li>
</ul>

<p>Aqui já não é mais só comunicação — é organização.</p>

<hr />

<h2 id="um-servidor-várias-responsabilidades">Um servidor, várias responsabilidades</h2>

<p>O servidor centraliza tudo.</p>

<p>Ele precisa:</p>

<ul>
  <li>aceitar conexões</li>
  <li>interpretar o que o cliente quer</li>
  <li>decidir qual funcionalidade executar</li>
  <li>responder corretamente</li>
</ul>

<p>E tudo isso sem “misturar” os clientes.</p>

<hr />

<h2 id="tipos-de-interação">Tipos de interação</h2>

<p>As funcionalidades em si são simples, mas ajudam a exercitar diferentes cenários:</p>

<ul>
  <li>responder informações (curiosidades)</li>
  <li>gerar dados em tempo real (hora do servidor)</li>
  <li>enviar arquivos</li>
  <li>listar conteúdo disponível</li>
</ul>

<p>Cada uma dessas ações exige um tipo diferente de tratamento.</p>

<hr />

<h2 id="onde-começa-a-complicar">Onde começa a complicar</h2>

<p>Quando vários clientes entram ao mesmo tempo, aparecem questões que não existem em programas locais:</p>

<ul>
  <li>concorrência</li>
  <li>ordem de execução</li>
  <li>consistência das respostas</li>
  <li>tratamento de erro em rede</li>
</ul>

<p>Nada disso aparece se você testa com um único cliente.</p>

<hr />

<h2 id="um-detalhe-importante">Um detalhe importante</h2>

<p>A comunicação aqui não é só envio de dados.</p>

<p>Existe um “protocolo implícito”:</p>

<blockquote>
  <p>o cliente precisa saber o que pode pedir
o servidor precisa entender o que foi pedido</p>
</blockquote>

<p>Mesmo que simples, isso já é uma forma de contrato.</p>

<hr />

<h2 id="o-que-esse-projeto-mostra">O que esse projeto mostra</h2>

<p>Ele não tenta ser um servidor completo.</p>

<p>Mas já mostra bem a transição de:</p>

<ul>
  <li>execução local</li>
  <li>para comunicação entre processos</li>
  <li>para sistemas com múltiplos pontos interagindo</li>
</ul>

<p>E isso muda completamente o tipo de problema.</p>

<hr />

<h2 id="limitações">Limitações</h2>

<p>Algumas coisas ficaram de fora — propositalmente:</p>

<ul>
  <li>autenticação</li>
  <li>criptografia</li>
  <li>controle mais sofisticado de concorrência</li>
</ul>

<p>A ideia aqui não foi robustez total, e sim entender o funcionamento.</p>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>No fim, o projeto começa simples — abrir conexão, trocar dados.</p>

<p>Mas rapidamente revela algo maior:</p>

<blockquote>
  <p>quando múltiplos agentes começam a interagir, organizar a comunicação passa a ser o problema central</p>
</blockquote>

<p>E isso já é o começo de pensar em sistemas distribuídos.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Client-Server">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="Python" /><category term="Networking" /><category term="Systems" /><summary type="html"><![CDATA[Um servidor em Python baseado em sockets TCP que lida com múltiplos clientes e organiza diferentes tipos de requisição.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/umbrellan.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/umbrellan.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Comparando Heap Sort e Insertion Sort em um experimento com vários tamanhos de entradas</title><link href="https://natamleao.github.io/compare-insertion-heap-sort/" rel="alternate" type="text/html" title="Comparando Heap Sort e Insertion Sort em um experimento com vários tamanhos de entradas" /><published>2026-03-31T00:00:00+00:00</published><updated>2026-03-31T00:00:00+00:00</updated><id>https://natamleao.github.io/compare-insertion-heap-sort</id><content type="html" xml:base="https://natamleao.github.io/compare-insertion-heap-sort/"><![CDATA[<h2 id="a-comparação-que-muitos-já-devem-ter-visto">A comparação que muitos já devem ter visto</h2>

<p><em>Heap Sort</em> e <em>Insertion Sort</em> aparecem em muitos cursos básicos.</p>

<p>Um é eficiente, outro é simples.
$O(nlog\,n)\; \text{vs}\; O(n²)$.</p>

<p>Beleza. Isso é o esperado.</p>

<p>Mas esse tipo de comparação costuma parar na teoria.
Aqui a ideia foi empurrar um pouco além:</p>

<blockquote>
  <p>colocar os dois sob as mesmas condições e observar o que acontece de verdade</p>
</blockquote>

<hr />

<h2 id="como-montei-o-experimento">Como montei o experimento</h2>

<p>Nada muito elaborado — mas com cuidado suficiente pra não enviesar o resultado:</p>

<ul>
  <li>os dois algoritmos recebem <strong>exatamente os mesmos dados</strong></li>
  <li>cada um roda isoladamente</li>
  <li>medição com <code class="language-plaintext highlighter-rouge">clock_gettime</code> usando <code class="language-plaintext highlighter-rouge">CLOCK_MONOTONIC</code></li>
  <li>foco direto em tempo de execução</li>
</ul>

<p>A intenção não foi fazer <em>benchmark</em> acadêmico, mas também não deixar solto.</p>

<hr />

<h2 id="estruturas">Estruturas</h2>

<p>Cada algoritmo roda na sua própria estrutura, bem direta:</p>

<ul>
  <li><em>Heap Sort</em> com <em>heap</em> em <em>array</em></li>
  <li><em>Insertion Sort</em> com array dinâmico</li>
</ul>

<p>Isso evita interferência entre implementações e mantém a comparação limpa.</p>

<hr />

<h2 id="ambiente-de-execução">Ambiente de execução</h2>

<p>Os testes foram executados no meu notebook:</p>

<ul>
  <li>CPU: AMD Ryzen 7 5825U (8 cores / 16 threads)</li>
  <li>RAM: 7.2 GiB</li>
  <li>Sistema: Debian GNU/Linux 12 (Bookworm)</li>
  <li>Arquitetura: x86-64</li>
  <li>Armazenamento: SSD</li>
  <li>Compilador: GCC</li>
  <li>Flags: <code class="language-plaintext highlighter-rouge">-O2 -Wall -Werror</code></li>
</ul>

<p>Isso é importante, dado que o tempo de execução não é absoluto, dependendo diretamente da máquina em que você roda.</p>

<hr />

<h2 id="o-que-aparece-quando-se-começa-a-escalar">O que aparece quando se começa a escalar</h2>

<p>Com entradas pequenas, os dois algoritmos convivem bem.</p>

<p>O <em>Insertion Sort</em> até se mantém competitivo — o que faz sentido, já que ele tem baixo <em>overhead</em>.</p>

<p>Mas conforme o tamanho cresce, a diferença deixa de ser sutil.</p>

<p>Ela começa a ficar estrutural.</p>

<hr />

<h2 id="gráficos-de-comparação">Gráficos de comparação</h2>

<ul>
  <li><strong>Gráfico 1</strong></li>
</ul>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CIH/graphic - heap sort.svg" alt="Gráfico Heap Sort" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Heap Sort — crescimento do tempo de execução 
  </p>
</div>

<p>O crescimento é consistente.
Nada surpreendente — mas também nada fora de controle.</p>

<hr />

<ul>
  <li><strong>Gráfico 2</strong></li>
</ul>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CIH/graphic - insertion sort.svg" alt="Gráfico Insertion Sort" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Insertion Sort — crescimento acelerado do tempo
  </p>
</div>

<p>O crescimento começa aceitável, mas rapidamente perde controle.
O comportamento quadrático aparece sem disfarce.</p>

<hr />

<ul>
  <li><strong>Gráfico 3</strong></li>
</ul>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CIH/graphic - heap sort vs insertion sort.svg" alt="Comparação Heap Sort vs Insertion Sort" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Heap Sort vs Insertion Sort — diferença de crescimento 
  </p>
</div>

<p>Colocados lado a lado, não é mais uma questão de “qual é melhor”.</p>

<p>É uma questão de <strong>qual continua sendo viável</strong>.</p>

<hr />

<h2 id="o-ponto-em-que-a-teoria-vira-limite-real">O ponto em que a teoria vira limite real</h2>

<p>Teve um momento no experimento que deixou isso bem claro.</p>

<p>Ao tentar escalar para 50 milhões de elementos, o <em>Insertion Sort</em> simplesmente deixou de ser uma opção prática.</p>

<p>Não por detalhe de implementação, mas por natureza do algoritmo.</p>

<p>$(5 \times 10^7)^2 = 2.5 \times 10^{15}$, esse seria o número total de operações a serem realizadas.</p>

<p>Mesmo com operações rápidas, isso escala para tempos absurdos.</p>

<p>É aqui que $O(n²)$ deixa de ser só uma notação e vira uma barreira concreta.</p>

<hr />

<h2 id="o-que-ficou-mais-evidente">O que ficou mais evidente</h2>

<p>Algumas coisas que já eram conhecidas ficam bem mais claras após a medição:</p>

<ul>
  <li><em>Heap Sort</em> mantém crescimento controlado</li>
  <li><em>Insertion Sort</em> degrada rápido com escala</li>
  <li>a diferença não é só teórica — é operacional</li>
  <li>viabilidade depende diretamente da complexidade</li>
</ul>

<p>E talvez o mais importante:</p>

<blockquote>
  <p>existe um ponto onde o algoritmo deixa de ser “lento” e passa a ser simplesmente inviável</p>
</blockquote>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>Esse projeto não tenta reinventar nada.</p>

<p>Ele só coloca dois algoritmos clássicos no mesmo cenário e observa o comportamento com dados reais.</p>

<p>E quando você faz isso, a diferença entre eles deixa de ser um detalhe acadêmico.</p>

<p>Vira uma decisão prática.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Compare-Insertion-Heap-Sort">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Algorithms" /><category term="Sorting" /><summary type="html"><![CDATA[Comparação prática entre Heap Sort e Insertion Sort em C com medição de tempo, mostrando o custo real de cada algoritmo.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/heap-vs-insertion.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/heap-vs-insertion.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Medindo o tempo de execução do algoritmo Insertion Sort em um experimento simples</title><link href="https://natamleao.github.io/insertion-sort/" rel="alternate" type="text/html" title="Medindo o tempo de execução do algoritmo Insertion Sort em um experimento simples" /><published>2026-03-30T00:00:00+00:00</published><updated>2026-03-30T00:00:00+00:00</updated><id>https://natamleao.github.io/insertion-sort</id><content type="html" xml:base="https://natamleao.github.io/insertion-sort/"><![CDATA[<h2 id="o-ponto-de-partida">O ponto de partida</h2>

<p><em>Insertion Sort</em> é um dos algoritmos com os quais logo se tem contato na ciência da computação ou cursos correlatos.</p>

<p>Simples, direto, quase intuitivo.
Você percorre o <em>array</em> e vai inserindo cada elemento na posição correta.</p>

<p>Então a motivação aqui não foi “entender como funciona”.</p>

<p>Foi outra:</p>

<blockquote>
  <p>sair do nível teórico e observar o comportamento quando você mede de verdade</p>
</blockquote>

<hr />

<h2 id="implementar-é-o-básico">Implementar é o básico</h2>

<p>A implementação segue o caminho esperado:</p>

<ul>
  <li><em>array</em> dinâmico para armazenar os dados</li>
  <li>percurso sequencial</li>
  <li>inserção na parte já ordenada</li>
  <li>ordenação <em>in-place</em></li>
</ul>

<p>Nada além do necessário.</p>

<p>E isso é intencional — quanto mais simples a base, mais claro fica o comportamento do algoritmo.</p>

<hr />

<h2 id="onde-começa-a-ficar-interessante">Onde começa a ficar interessante</h2>

<p>A diferença aqui foi tratar a medição como parte central.</p>

<p>Usei <code class="language-plaintext highlighter-rouge">clock_gettime</code> com <code class="language-plaintext highlighter-rouge">CLOCK_MONOTONIC</code> para capturar tempo de execução real.</p>

<p>Porque dizer que o algoritmo é $O(n^2)$ é fácil.</p>

<blockquote>
  <p>o que importa é ver o que isso significa quando você roda de fato</p>
</blockquote>

<hr />

<h2 id="o-que-acontece-na-medição">O que acontece na medição</h2>

<p>Com poucos elementos, o algoritmo é rápido.</p>

<p>Direto, eficiente, sem <em>overhead</em>.</p>

<p>Mas conforme o volume cresce, o comportamento começa a se acumular:</p>

<ul>
  <li>cada inserção passa a custar mais</li>
  <li>mais elementos precisam ser deslocados</li>
  <li>o tempo total cresce de forma agressiva</li>
</ul>

<p>Nada disso é surpresa — mas ver acontecendo é diferente.</p>

<hr />

<h2 id="gráfico">Gráfico</h2>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CIH/graphic - insertion sort.svg" alt="Gráfico Insertion Sort" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Insertion Sort — crescimento acelerado do tempo 
  </p>
</div>

<p>O gráfico deixa isso bem claro.</p>

<p>O crescimento começa controlado, mas rapidamente foge de escala.</p>

<p>É o $n^2$ aparecendo sem suavização.</p>

<hr />

<h2 id="um-detalhe-que-chama-atenção">Um detalhe que chama atenção</h2>

<p>O custo do algoritmo não está distribuído de forma uniforme.</p>

<p>Ele se acumula nas inserções.</p>

<p>Quanto mais o <em>array</em> cresce, mais caro fica manter a ordenação incremental.</p>

<p>E isso cria um efeito cascata que domina o tempo total.</p>

<hr />

<h2 id="limitações">Limitações</h2>

<p>A medição aqui não tenta ser rigorosa ao extremo:</p>

<ul>
  <li>uma execução por cenário</li>
  <li>dados gerados de forma simples</li>
  <li>sem análise estatística aprofundada</li>
</ul>

<p>Não é <em>benchmark</em> formal.</p>

<p>Mas também não precisa ser pra mostrar o comportamento.</p>

<hr />

<h2 id="por-que-esse-projeto-existe">Por que esse projeto existe</h2>

<p>Esse projeto não é sobre o algoritmo em si.</p>

<p>É sobre observar o limite dele.</p>

<p>Porque enquanto o <em>Insertion Sort</em> funciona muito bem em cenários pequenos ou parcialmente ordenados, ele tem um ponto claro onde deixa de ser viável.</p>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p><em>Insertion Sort</em> é simples — e essa simplicidade é exatamente o que o torna útil em certos contextos.</p>

<p>Mas ela também impõe um limite.</p>

<p>Quando você mede, esse limite deixa de ser teórico e vira algo concreto.</p>

<p>E isso muda completamente a forma como você enxerga o algoritmo.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Insertion-Sort">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Algorithms" /><category term="Sorting" /><summary type="html"><![CDATA[Implementação de Insertion Sort em C com medição de tempo para observar o comportamento na prática.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/insertion-sort.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/insertion-sort.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Hash adaptativa baseada nos dados</title><link href="https://natamleao.github.io/hash-table-with-digit-span-c/" rel="alternate" type="text/html" title="Hash adaptativa baseada nos dados" /><published>2026-03-30T00:00:00+00:00</published><updated>2026-03-30T00:00:00+00:00</updated><id>https://natamleao.github.io/hash-table-with-digit-span-c</id><content type="html" xml:base="https://natamleao.github.io/hash-table-with-digit-span-c/"><![CDATA[<h2 id="de-onde-veio-a-ideia">De onde veio a ideia</h2>

<p>Tabela <em>hash</em> normalmente começa com uma decisão fixa: você define uma função e trabalha em cima dela.</p>

<p>Aqui eu quis fazer um pequeno desvio desse padrão — não mudando a estrutura, mas a forma de escolher a função.</p>

<p>A motivação foi simples: antes de aplicar a função, olhar minimamente para os dados.</p>

<hr />

<h2 id="o-método">O método</h2>

<p>A abordagem usada aqui não é nova.</p>

<p>Ela segue uma linha conhecida em <em>hashing</em>: usar propriedades da distribuição das chaves para tentar escolher uma função que se comporte melhor naquele contexto.</p>

<p>No caso, a escolha foi trabalhar com <strong>análise de dígitos</strong>:</p>

<ul>
  <li>decompor os números em dígitos</li>
  <li>observar a frequência de cada dígito por posição</li>
  <li>comparar com uma distribuição ideal (uniforme)</li>
  <li>escolher a posição mais “equilibrada”</li>
</ul>

<p>Isso gera um critério simples:
usar o dígito menos enviesado como base para o índice.</p>

<p>Não é uma função universal, nem perfeita — é uma heurística baseada no conjunto de entrada.</p>

<hr />

<h2 id="por-que-isso-faz-sentido">Por que isso faz sentido</h2>

<p>Nem todo dígito carrega a mesma informação.</p>

<p>Dependendo do conjunto, alguns dígitos são praticamente inúteis (muito repetidos), enquanto outros distribuem melhor.</p>

<p>A ideia é justamente evitar escolhas ruins logo de cara.</p>

<p>Em vez de usar, por exemplo, sempre o último dígito, deixar os dados indicarem qual posição tende a espalhar melhor os valores.</p>

<hr />

<h2 id="o-que-muda-na-prática">O que muda na prática</h2>

<p>A função final continua simples:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">index</span> <span class="o">=</span> <span class="n">digit</span> <span class="o">%</span> <span class="n">capacity</span><span class="p">;</span>
</code></pre></div></div>

<p>Nada sofisticado.</p>

<p>A diferença é que o <em>digit</em> não é fixo — ele é escolhido com base em uma análise prévia.</p>

<p>Isso mantém a implementação leve, mas introduz um pequeno grau de adaptação.</p>

<hr />

<h2 id="limitações">Limitações</h2>

<p>Esse tipo de abordagem tem limites claros:</p>

<ul>
  <li>depende completamente do conjunto inicial</li>
  <li>não reage a mudanças depois</li>
  <li>o critério de “melhor distribuição” é aproximado</li>
  <li>não substitui funções de <em>hash</em> mais robustas</li>
</ul>

<p>Ou seja, não é solução geral — é uma variação pontual.</p>

<hr />

<h2 id="por-que-explorar-isso">Por que explorar isso</h2>

<p>O objetivo aqui não foi propor algo novo, mas tornar explícita uma ideia que normalmente fica implícita:</p>

<blockquote>
  <p>a escolha da função de <em>hash</em> pode (e talvez devesse) considerar os dados</p>
</blockquote>

<p>Mesmo que de forma simples.</p>

<p>Implementar isso em C, de maneira direta, foi mais um exercício de exploração do que de otimização.</p>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>Esse projeto fica num meio-termo interessante:</p>

<p>não muda a estrutura clássica, mas também não aceita completamente a rigidez dela.</p>

<p>É só um pequeno ajuste de perspectiva —
de função fixa para função influenciada pelos dados.</p>

<p>E às vezes esse tipo de ajuste já abre espaço pra pensar diferente.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Hash-Table">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Data Structures" /><category term="Hashing" /><summary type="html"><![CDATA[Uma variação de tabela hash que escolhe o índice com base na distribuição dos próprios dados.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/umbrellan.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/umbrellan.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Medindo o tempo de execução do algoritmo Heap Sort em um experimento simples</title><link href="https://natamleao.github.io/heap-heapsort-c-with-time-measurement/" rel="alternate" type="text/html" title="Medindo o tempo de execução do algoritmo Heap Sort em um experimento simples" /><published>2026-03-30T00:00:00+00:00</published><updated>2026-03-30T00:00:00+00:00</updated><id>https://natamleao.github.io/heap-heapsort-c-with-time-measurement</id><content type="html" xml:base="https://natamleao.github.io/heap-heapsort-c-with-time-measurement/"><![CDATA[<h2 id="o-ponto-de-partida">O ponto de partida</h2>

<p><em>Heap</em> e <em>Heap Sort</em> são estruturas e algoritmos bem estabelecidos, bem documentados e já explorados até o limite.</p>

<p>Então a motivação aqui não foi “entender como funciona”.</p>

<p>Foi outra coisa:</p>

<blockquote>
  <p>sair do nível teórico e olhar o comportamento real.</p>
</blockquote>

<hr />

<h2 id="implementar-é-o-básico">Implementar é o básico</h2>

<p>A implementação seguiu o caminho padrão:</p>

<ul>
  <li><em>max-heap</em> baseada em <em>array</em></li>
  <li>relações clássicas entre pai e filhos</li>
  <li>construção da <em>heap</em></li>
  <li><em>heapify</em></li>
  <li>ordenação <em>in-place</em></li>
</ul>

<p>Nada fora do esperado.</p>

<p>E isso é proposital — quando a base é conhecida, fica mais fácil observar o que realmente interessa depois.</p>

<hr />

<h2 id="onde-começa-a-ficar-interessante">Onde começa a ficar interessante</h2>

<p>A diferença aqui foi adicionar medição de tempo de execução usando <code class="language-plaintext highlighter-rouge">clock_gettime</code> com <code class="language-plaintext highlighter-rouge">CLOCK_MONOTONIC</code>.</p>

<p>Não como detalhe, mas como parte central do projeto.</p>

<p>Porque até então, <em>Heap Sort</em> é só mais um algoritmo com complexidade $O(n \log n)$.</p>

<p>Mas isso, sozinho, não diz muita coisa na prática.</p>

<hr />

<h2 id="o-que-acontece-quando-se-mede">O que acontece quando se mede</h2>

<p>Quando você roda o algoritmo com volumes maiores de dados, algumas coisas começam a ficar mais concretas:</p>

<ul>
  <li>o crescimento do tempo deixa de ser abstrato</li>
  <li>o custo de reorganizar a <em>heap</em> aparece com mais clareza</li>
  <li>o impacto de trabalhar <em>in-place</em> fica evidente</li>
</ul>

<p>Nada disso contradiz a teoria — mas também não é algo que você “sente” só olhando pra notação assintótica.</p>

<p>Tem uma diferença grande entre saber e observar.</p>

<hr />

<h2 id="gráfico">Gráfico</h2>

<div style="text-align: center;">
  <img src="/assets/images/post-images/graphics/CIH/graphic - heap sort.svg" alt="Gráfico Heap Sort" style="display: block; margin: 0 auto; max-width: 100%; width: 100%;" />
  <p style="font-size: 0.9em; color: gray; text-align: center;">
    Heap Sort — crescimento do tempo de execução
  </p>
</div>

<p>O comportamento é bem estável.</p>

<p>O tempo cresce, claro, mas de forma controlada — sem aquele salto abrupto que aparece em algoritmos quadráticos.</p>

<hr />

<h2 id="um-detalhe-que-chama-atenção">Um detalhe que chama atenção</h2>

<p>Uma coisa que fica clara é como o custo está concentrado.</p>

<p>A construção da <em>heap</em> é relativamente rápida.
Mas o processo de extração + <em>heapify</em> repetido é onde o algoritmo realmente “paga o preço”.</p>

<p>É ali que o $\log n$ aparece repetidamente, acumulando custo ao longo da execução.</p>

<hr />

<h2 id="limitações">Limitações</h2>

<p>A medição aqui não tenta ser rigorosa ao extremo:</p>

<ul>
  <li>uma execução por cenário</li>
  <li>dados gerados de forma simples</li>
  <li>sem análise estatística aprofundada</li>
</ul>

<p>Não é <em>benchmark</em> científico.</p>

<p>Mas também não era essa a intenção.</p>

<hr />

<h2 id="por-que-esse-projeto-existe">Por que esse projeto existe</h2>

<p>Esse projeto não é sobre reinventar <em>Heap Sort</em>.</p>

<p>É sobre dar um passo além do “funciona” e entrar no “como se comporta”.</p>

<p>Porque em algum momento, só saber a teoria começa a ficar pouco.</p>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>No fim, a estrutura e o algoritmo são os mesmos de sempre.</p>

<p>O que muda é a forma de olhar.</p>

<p>Quando você mede, a complexidade deixa de ser só uma ideia e vira algo observável — quase palpável.</p>

<p>E isso, por mais simples que pareça, muda bastante a forma como você enxerga algoritmos clássicos.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Heap-HeapSort">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Data Structures" /><category term="Sorting" /><summary type="html"><![CDATA[Implementação de heap e Heap Sort em C com medição de tempo para observar o comportamento na prática.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/umbrellan.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/umbrellan.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Gerenciando alunos e persistindo dados</title><link href="https://natamleao.github.io/student-management-system/" rel="alternate" type="text/html" title="Gerenciando alunos e persistindo dados" /><published>2026-03-24T00:00:00+00:00</published><updated>2026-03-24T00:00:00+00:00</updated><id>https://natamleao.github.io/student-management-system</id><content type="html" xml:base="https://natamleao.github.io/student-management-system/"><![CDATA[<h2 id="um-passo-além-da-memória">Um passo além da memória</h2>

<p>Esse projeto nasceu de uma mudança simples de foco.</p>

<p>Até então, os programas começavam, rodavam e acabavam — tudo existia só durante a execução.</p>

<p>Aqui a ideia foi outra:</p>

<blockquote>
  <p>manter dados vivos mesmo depois do programa terminar</p>
</blockquote>

<p>Isso muda bastante o tipo de problema.</p>

<hr />

<h2 id="não-é-só-armazenar--é-manter-consistência">Não é só armazenar — é manter consistência</h2>

<p>Gerenciar alunos, por si só, não é complicado:</p>

<ul>
  <li>cadastrar</li>
  <li>remover</li>
  <li>buscar</li>
  <li>listar</li>
</ul>

<p>Mas quando você adiciona persistência, entra outra camada:</p>

<ul>
  <li>o que está em memória precisa refletir o que está em disco</li>
  <li>leitura e escrita precisam ser compatíveis</li>
  <li>qualquer inconsistência quebra tudo silenciosamente</li>
</ul>

<p>Deixa de ser só manipulação de dados — vira manutenção de estado.</p>

<hr />

<h2 id="a-escolha-da-estrutura">A escolha da estrutura</h2>

<p>Usei uma <strong>lista duplamente encadeada</strong>.</p>

<p>Aqui a escolha foi bem intencional.</p>

<p>Ela não é a mais simples, mas resolve bem algumas operações:</p>

<ul>
  <li>remoção sem precisar percorrer desde o início</li>
  <li>navegação nos dois sentidos</li>
  <li>inserções mais flexíveis</li>
</ul>

<p>Para um sistema que sofre alterações frequentes, isso ajuda a manter o comportamento previsível.</p>

<hr />

<h2 id="onde-o-projeto-realmente-começa">Onde o projeto realmente começa</h2>

<p>A parte mais interessante não foi a lista.</p>

<p>Foi a persistência em <strong>arquivo binário</strong>.</p>

<p>Em vez de serializar para texto, a ideia foi trabalhar direto com bytes.</p>

<p>Isso exige mais cuidado:</p>

<ul>
  <li>definir exatamente o que é escrito</li>
  <li>garantir que a leitura reconstrua corretamente</li>
  <li>lidar com tamanhos variáveis (principalmente strings)</li>
</ul>

<p>Nada disso é difícil isoladamente — mas tudo precisa bater exatamente.</p>

<hr />

<h2 id="um-detalhe-que-faz-diferença">Um detalhe que faz diferença</h2>

<p>Strings não são triviais aqui.</p>

<p>Salvar um inteiro é direto.
Salvar uma string exige:</p>

<ul>
  <li>armazenar o tamanho</li>
  <li>escrever o conteúdo</li>
  <li>reconstruir corretamente depois</li>
</ul>

<p>Se qualquer parte disso falhar, o dado corrompe.</p>

<p>E não tem aviso.</p>

<hr />

<h2 id="quando-as-coisas-começam-a-se-conectar">Quando as coisas começam a se conectar</h2>

<p>Esse projeto junta várias camadas que isoladamente são simples:</p>

<ul>
  <li>memória dinâmica</li>
  <li>estruturas encadeadas</li>
  <li>manipulação de arquivos</li>
</ul>

<p>Mas juntas, elas exigem mais rigor.</p>

<p>Você precisa garantir:</p>

<ul>
  <li>alocação correta</li>
  <li>liberação correta</li>
  <li>cópia correta</li>
  <li>leitura e escrita coerentes</li>
</ul>

<p>Não dá pra “deixar passar”.</p>

<hr />

<h2 id="o-que-esse-projeto-representa">O que esse projeto representa</h2>

<p>Ele fica num ponto interessante entre exercício e sistema real.</p>

<p>Não é complexo, mas já força a pensar em:</p>

<ul>
  <li>persistência</li>
  <li>consistência</li>
  <li>organização de dados</li>
</ul>

<p>Coisas que não aparecem quando tudo vive só em memória.</p>

<hr />

<h2 id="fechamento">Fechamento</h2>

<p>A principal diferença aqui não está no algoritmo nem na estrutura.</p>

<p>Está no fato de que os dados continuam existindo depois.</p>

<p>E isso muda o tipo de cuidado que o código exige.</p>

<p>Quando você passa a lidar com estado persistente, pequenos erros deixam de ser locais — eles se acumulam.</p>

<p>E é aí que o nível do problema sobe.</p>

<hr />

<h2 id="código">Código</h2>

<p>Você pode encontrar o projeto completo <a href="https://github.com/natamleao/Student-Management-System">aqui</a>.</p>

<hr />]]></content><author><name>Natam L. Ferreria</name></author><category term="portfolio" /><category term="C" /><category term="Data Structures" /><category term="Persistence" /><summary type="html"><![CDATA[Sistema em C com lista duplamente encadeada e persistência em arquivo binário.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://natamleao.github.io/assets/images/student-management-c-thumbnail.png" /><media:content medium="image" url="https://natamleao.github.io/assets/images/student-management-c-thumbnail.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>