Worked examples: por que estudar soluções resolvidas acelera o aprendizado
Quando comecei a programar, eu acreditava que sofrer no problema do zero era a única forma de aprender. Hoje sei que, no início de um assunto novo, isso muitas vezes atrasa o aprendizado, e existe pesquisa séria sobre isso.
O que é o “worked example effect”
Um worked example (exemplo resolvido) é um problema apresentado já com a solução passo a passo. O worked example effect, da Teoria da Carga Cognitiva (John Sweller), mostra que iniciantes aprendem mais rápido estudando soluções resolvidas do que tentando resolver tudo sozinhos.
O motivo é carga cognitiva: resolver do zero gasta a maior parte da memória de trabalho procurando o que fazer (busca cega), sobrando pouca energia pra de fato entender por que aquilo funciona. O exemplo resolvido tira o peso da busca e deixa você focar no raciocínio.
Não é só teoria: um estudo de 2023 (Chen, Retnowati, Chan & Kalyuga, Educational Psychology) comparou estudar worked examples vs. resolver do zero, e os worked examples saíram melhor tanto na retenção quanto na transferência (aplicar o aprendido em problemas novos), com menos carga cognitiva.
E tem um detalhe que derruba o “sem dor, sem ganho”: o grupo que resolveu do zero enfrentou mais carga cognitiva e mais “desafio”, mas não se sentiu mais orgulhoso do que quem estudou os exemplos (a diferença em “orgulho” não foi estatisticamente significativa). Resumindo: sofrer mais não rendeu nem mais aprendizado, nem mais sensação de conquista.
Atenção: o efeito se inverte conforme você ganha experiência (o “expertise reversal effect”). Pra quem já domina, exemplos demais viram redundância, e aí resolver sozinho volta a ser melhor.
Worked examples na prática: peça a uma LLM
Hoje dá pra gerar worked examples sob demanda: pegue um problema (ex.: um do LeetCode) e peça a uma LLM a solução em formato worked example, com os passos quebrados e resolvidos um a um.
Como pedir (um prompt que funciona bem):
“Me dê um worked example deste problema do LeetCode: quebre a solução em passos, resolvidos e justificados um a um, como um exercício de matemática resolvido. Mostre o raciocínio e a complexidade antes de chegar ao código final.”
Qual a vantagem: em vez de receber só o código pronto (que vira copia-e-cola), você recebe o raciocínio explícito, o porquê de cada passo. É justamente isso que reduz a carga cognitiva e faz o aprendizado transferir pra problemas parecidos. E você controla a dificuldade: peça pra LLM apagar partes (completion) e ir removendo passos (fading) até resolver sozinho.
Exemplo real (LLM + LeetCode 42 · Trapping Rain Water)
Mandei exatamente esse prompt para o LeetCode 42 (Trapping Rain Water). A resposta veio assim:
Enunciado. Dado um array height de inteiros não-negativos (cada barra com
largura 1), calcule quanta água fica retida sobre o relevo depois da chuva. Caso
canônico:
height = [0,1,0,2,1,0,1,3,2,1,2,1] → resposta = 6
Passo 1 — água em uma única posição. A água sobre i é limitada pelo menor
dos dois muros que cercam i: o maior à esquerda (inclusive i) e o maior à
direita (inclusive i). Definindo L[i] = max(height[0..i]) e
R[i] = max(height[i..n-1]):
water[i] = min(L[i], R[i]) - height[i]
e o total é a soma de water[i] (sempre ≥ 0, porque height[i] entra nos dois
máximos). Conferindo em i = 5: L[5]=2, R[5]=3 → min(2,3) - 0 = 2. ✓
Passo 2 — brute force (tradução direta). Para cada i, varre esquerda e
direita procurando os máximos:
int total = 0;
for (int i = 0; i < n; i++) {
int maxL = 0, maxR = 0;
for (int j = 0; j <= i; j++) maxL = Math.max(maxL, height[j]);
for (int j = i; j < n; j++) maxR = Math.max(maxR, height[j]);
total += Math.min(maxL, maxR) - height[i];
}
return total;
O(n²) tempo, O(1) espaço. Correto, mas recalcula os mesmos máximos várias vezes.
Passo 3 — pré-computar L e R (dois passes). Os máximos têm recorrência simples:
L[i] = max(L[i-1], height[i]) e R[i] = max(R[i+1], height[i]).
height : 0 1 0 2 1 0 1 3 2 1 2 1
L[i] : 0 1 1 2 2 2 2 3 3 3 3 3
R[i] : 3 3 3 3 3 3 3 3 2 2 2 1
min(L,R) : 0 1 1 2 2 2 2 3 2 2 2 1
- height : 0 0 1 0 1 2 1 0 0 1 0 0 → soma = 6 ✓
O(n) tempo, O(n) espaço. Ótimo em tempo; resta cortar o espaço.
Passo 4 — dois ponteiros (O(1) espaço). Não preciso do valor exato de
min(L, R), só saber qual lado limita. Com dois ponteiros l/r correndo um
para o outro e os máximos maxL/maxR: se height[l] < height[r], existe à
direita uma barra mais alta, então o lado esquerdo é o limitante e dá pra processar
l só com maxL (senão, simétrico à direita).
int l = 0, r = n - 1, maxL = 0, maxR = 0, total = 0;
while (l < r) {
if (height[l] < height[r]) {
if (height[l] >= maxL) maxL = height[l];
else total += maxL - height[l];
l++;
} else {
if (height[r] >= maxR) maxR = height[r];
else total += maxR - height[r];
r--;
}
}
return total;
O(n) tempo (cada índice visitado uma vez), O(1) espaço.
| Abordagem | Tempo | Espaço |
|---|---|---|
| Brute force | O(n²) | O(1) |
| Pré-computar L e R | O(n) | O(n) |
| Dois ponteiros | O(n) | O(1) |
A moral: a fórmula min(L[i], R[i]) - height[i] é o invariante; cada passo de
otimização só muda como obtemos esses máximos — recomputando (O(n²)), memoizando
(O(n) espaço), ou trocando a memória por uma comparação local entre as pontas (O(1)).
⚠️ A armadilha: ler a resposta da LLM e sentir que aprendeu é uma ilusão (fluência ≠ aprendizado). O ganho real só vem quando você fecha tudo e reproduz sozinho. Use a LLM como um professor de exemplos resolvidos, não como muleta.
Pra levar
Não confunda esforço com aprendizado. No começo de um tópico, um bom exemplo resolvido + explicar pra si mesmo rende mais que horas travado. Guarde o “resolver do zero” pra quando ele realmente ensina: depois que a base já entrou.
Referências:
- Problema: LeetCode 42 — Trapping Rain Water
- Chen, O., Retnowati, E., Chan, B. C., & Kalyuga, S. (2023). The effect of worked examples on learning solution steps and knowledge transfer. Educational Psychology. tandfonline.com
When I started programming, I believed that struggling through a problem from scratch was the only way to learn. Today I know that, at the start of a new topic, that often slows down learning, and there’s solid research on it.
What is the “worked example effect”
A worked example is a problem presented together with its step-by-step solution. The worked example effect, from Cognitive Load Theory (John Sweller), shows that novices learn faster by studying solved solutions than by trying to solve everything on their own.
The reason is cognitive load: solving from scratch spends most of your working memory searching for what to do (blind search), leaving little energy to actually understand why it works. A worked example removes the search and lets you focus on the reasoning.
It’s not just theory: a 2023 study (Chen, Retnowati, Chan & Kalyuga, Educational Psychology) compared studying worked examples vs. solving from scratch, and the worked examples came out ahead on both retention and transfer (applying what you learned to new problems), with lower cognitive load.
And here’s a detail that breaks the “no pain, no gain” myth: the group that solved from scratch faced more cognitive load and more “challenge”, but did not feel prouder than the group that studied the examples (the difference in “pride” was not statistically significant). In short: suffering more brought neither more learning nor a greater sense of achievement.
Note: the effect reverses as you gain expertise (the “expertise reversal effect”). For someone who already masters the topic, too many examples become redundant, and solving on your own becomes better again.
Worked examples in practice: ask an LLM
These days you can generate worked examples on demand: take a problem (e.g., one from LeetCode) and ask an LLM for the solution as a worked example, with the steps broken down and solved one by one.
How to ask (a prompt that works well):
“Give me a worked example of this LeetCode problem: break the solution into steps, solved and justified one by one, like a solved math exercise. Show the reasoning and the complexity before getting to the final code.”
The advantage: instead of getting just the finished code (which turns into copy-paste), you get the explicit reasoning, the why behind each step. That’s exactly what lowers cognitive load and makes the learning transfer to similar problems. And you control the difficulty: ask the LLM to remove parts (completion) and gradually drop steps (fading) until you solve it yourself.
A real example (LLM + LeetCode 42 · Trapping Rain Water)
I sent exactly that prompt for LeetCode 42 (Trapping Rain Water). The answer came back like this:
Statement. Given an array height of non-negative integers (each bar of width
1), compute how much water is trapped over the terrain after the rain. Canonical
case:
height = [0,1,0,2,1,0,1,3,2,1,2,1] → answer = 6
Step 1 — water at a single position. The water above i is limited by the
shorter of the two walls around i: the tallest to the left (including i) and the
tallest to the right (including i). Defining L[i] = max(height[0..i]) and
R[i] = max(height[i..n-1]):
water[i] = min(L[i], R[i]) - height[i]
and the total is the sum of water[i] (always ≥ 0, because height[i] appears in
both maxima). Checking at i = 5: L[5]=2, R[5]=3 → min(2,3) - 0 = 2. ✓
Step 2 — brute force (direct translation). For each i, scan left and right
looking for the maxima:
int total = 0;
for (int i = 0; i < n; i++) {
int maxL = 0, maxR = 0;
for (int j = 0; j <= i; j++) maxL = Math.max(maxL, height[j]);
for (int j = i; j < n; j++) maxR = Math.max(maxR, height[j]);
total += Math.min(maxL, maxR) - height[i];
}
return total;
O(n²) time, O(1) space. Correct, but it recomputes the same maxima over and over.
Step 3 — precompute L and R (two passes). The maxima have a simple recurrence:
L[i] = max(L[i-1], height[i]) and R[i] = max(R[i+1], height[i]).
height : 0 1 0 2 1 0 1 3 2 1 2 1
L[i] : 0 1 1 2 2 2 2 3 3 3 3 3
R[i] : 3 3 3 3 3 3 3 3 2 2 2 1
min(L,R) : 0 1 1 2 2 2 2 3 2 2 2 1
- height : 0 0 1 0 1 2 1 0 0 1 0 0 → sum = 6 ✓
O(n) time, O(n) space. Optimal in time; now to cut the space.
Step 4 — two pointers (O(1) space). I don’t need the exact value of
min(L, R), only which side limits. With two pointers l/r moving toward each
other and the running maxima maxL/maxR: if height[l] < height[r], there’s a
taller bar on the right, so the left side is the limiter and I can process l using
only maxL (otherwise, symmetric on the right).
int l = 0, r = n - 1, maxL = 0, maxR = 0, total = 0;
while (l < r) {
if (height[l] < height[r]) {
if (height[l] >= maxL) maxL = height[l];
else total += maxL - height[l];
l++;
} else {
if (height[r] >= maxR) maxR = height[r];
else total += maxR - height[r];
r--;
}
}
return total;
O(n) time (each index visited once), O(1) space.
| Approach | Time | Space |
|---|---|---|
| Brute force | O(n²) | O(1) |
| Precompute L and R | O(n) | O(n) |
| Two pointers | O(n) | O(1) |
The moral: the formula min(L[i], R[i]) - height[i] is the invariant; each
optimization only changes how we get those maxima — recomputing (O(n²)),
memoizing (O(n) space), or trading memory for a local comparison between the two
ends (O(1)).
⚠️ The trap: reading the LLM’s answer and feeling you learned is an illusion (fluency ≠ learning). The real gain only comes when you close everything and reproduce it yourself. Use the LLM as a teacher of solved examples, not as a crutch.
Takeaway
Don’t confuse effort with learning. At the start of a topic, a good solved example plus explaining it to yourself beats hours stuck. Save “solving from scratch” for when it actually teaches: after the basics are in.
References:
- Problem: LeetCode 42 — Trapping Rain Water
- Chen, O., Retnowati, E., Chan, B. C., & Kalyuga, S. (2023). The effect of worked examples on learning solution steps and knowledge transfer. Educational Psychology. tandfonline.com