Projeto final: da descrição do problema ao algoritmo
Construa um projeto completo, transformando requisitos em dados, funções, pseudocódigo, testes e análise de eficiência.

Construa uma solução completa, não apenas um trecho de pseudocódigo
Este projeto final reúne o percurso da trilha em uma entrega verificável. Você receberá uma descrição de problema, transformará expectativas em regras explícitas, modelará os dados, dividirá responsabilidades, escreverá o algoritmo, criará testes e justificará a eficiência.
O desafio é construir um analisador de consumo de energia. Ele recebe leituras organizadas por dia e período, calcula indicadores e sinaliza os dias cujo total ultrapassa um limite definido. Os dados são hipotéticos: o objetivo é praticar lógica de programação, não interpretar uma conta de energia real.
Ao terminar, você terá um pequeno portfólio técnico composto por:
- contrato do problema;
- exemplos e casos de teste com resultados esperados;
- decomposição em funções;
- pseudocódigo completo;
- teste de mesa;
- análise de correção e eficiência;
- reflexão sobre decisões e possíveis extensões.
Esse formato segue uma prática importante: uma solução precisa comunicar o algoritmo, demonstrar como ele funciona, sustentar sua correção e analisar seu custo. O projeto não termina quando o pseudocódigo parece plausível.
Comece pelo enunciado do projeto
Uma organização registra o consumo de energia de vários dias. Cada dia possui a mesma quantidade de períodos de medição. O algoritmo deve produzir um relatório com:
- total consumido em todos os dias;
- média por leitura;
- total de cada dia;
- maior leitura e sua posição;
- dias cujo total ficou acima de um limite diário;
- quantidade de dias sinalizados.
Considere a matriz abaixo, em quilowatt-hora, apenas como exemplo:
leituras = [
[2, 3, 1],
[4, 2, 2],
[1, 5, 3]
]
limiteDiario = 7
Não escreva o algoritmo ainda. Primeiro identifique o que o texto afirma, o que ele deixa implícito e quais decisões precisam ser registradas. Requisitos bem formados reduzem interpretações incompatíveis entre quem pede, constrói e testa uma solução.
Transforme o enunciado em um contrato testável
Um contrato descreve entradas permitidas, resultados e comportamento diante de situações excepcionais. Ele não precisa mencionar variáveis locais nem antecipar todos os detalhes da implementação.
Use este contrato para o projeto:
Entradas válidas
leiturasé uma matriz não vazia;- todas as linhas possuem a mesma quantidade positiva de colunas;
- cada leitura é numérica, finita e maior ou igual a zero;
limiteDiarioé numérico, finito e maior que zero.
Regras
- um dia é sinalizado somente quando seu total é maior que o limite;
- total igual ao limite não gera alerta;
- se duas leituras tiverem o mesmo valor máximo, vence a primeira na ordem de percurso;
- dias e períodos são apresentados a partir de 1 para o usuário;
- qualquer entrada inválida interrompe o processamento antes da criação do relatório.
Saída
O relatório contém totalGeral, mediaPorLeitura, totaisPorDia, maiorLeitura, diaDoPico, periodoDoPico, diasAcimaDoLimite e quantidadeDeAlertas.
O contrato também delimita o escopo. Conversão de moedas, preço de tarifa, persistência, interface e previsão de consumo não pertencem a esta entrega. Esse recorte impede que o projeto cresça antes de sua base estar correta.
Se quiser revisar esse raciocínio, retome como validar entradas e regras de negócio.
Calcule o exemplo esperado antes de implementar
O resultado esperado funciona como oráculo de teste: a referência usada para decidir se a saída observada está correta. Calcule-o sem depender do algoritmo que será testado.
Para a matriz proposta:
| Medida | Resultado esperado |
|---|---|
| Totais por dia | [6, 8, 9] |
| Total geral | 23 |
| Quantidade de leituras | 9 |
| Média por leitura | 23 ÷ 9 ≈ 2,56 |
| Maior leitura | 5 |
| Posição do pico | dia 3, período 2 |
| Dias acima de 7 | [2, 3] |
| Quantidade de alertas | 2 |
Não arredonde dentro do algoritmo, a menos que o contrato defina essa regra. O relatório pode formatar a média com duas casas para exibição, enquanto preserva o valor calculado internamente.
Essa separação evita um erro frequente: fazer o teste repetir exatamente a mesma lógica da implementação e, assim, reproduzir o mesmo defeito dos dois lados.
Projete os testes antes da solução
Testar primeiro pressiona o contrato: ao tentar prever resultados, você descobre ambiguidades antes de investir na implementação. Escolha casos de famílias diferentes, não apenas matrizes maiores com o mesmo comportamento.
| Caso | Entrada essencial | Regra verificada | Resultado esperado |
|---|---|---|---|
| caminho comum | exemplo de três dias | cálculos integrados | relatório calculado acima |
| fronteira | [[3, 4]], limite 7 | igual não é acima | nenhum alerta |
| acima por pouco | [[3, 4.1]], limite 7 | comparação estrita | dia 1 sinalizado |
| empate no pico | [[5, 1], [2, 5]] | primeira ocorrência vence | dia 1, período 1 |
| mínimo válido | [[0]], limite 1 | zero é leitura válida | total 0, sem alerta |
| matriz irregular | [[1, 2], [3]] | formato retangular | erro, sem relatório |
| domínio inválido | [[2, -1]] | consumo não negativo | erro, sem relatório |
| limite inválido | [[1]], limite 0 | limite positivo | erro, sem relatório |
A técnica de particionar o espaço de entradas procura representantes de grupos com comportamento semelhante. Os limites merecem atenção especial porque uma troca entre > e >= costuma falhar exatamente na fronteira. A aula sobre casos extremos e entradas inválidas aprofunda essa distinção.
Modele dados e estados necessários
A matriz expressa duas dimensões independentes:
linhas → dias
colunas → períodos de cada dia
Durante o percurso, o algoritmo precisa manter apenas estados com finalidade clara:
totalGeral: soma de todas as leituras processadas;quantidadeDeLeituras: divisor da média;totaisPorDia: resultado agregado de cada linha;maiorLeitura: maior valor conhecido até o momento;diaDoPicoeperiodoDoPico: posição associada ao maior valor;diasAcimaDoLimite: dias sinalizados após o fechamento de cada linha.
Inicialize maiorLeitura com a primeira leitura válida, não com zero por hábito. Neste projeto, zero funcionaria porque valores negativos são proibidos, mas depender silenciosamente dessa restrição torna a lógica menos reutilizável. A primeira leitura conecta a inicialização diretamente ao domínio aceito.
Escolha também quando cada estado pode mudar. O pico é atualizado durante o percurso das colunas; o total diário só pode ser comparado ao limite depois que a linha inteira foi somada.
Decomponha a solução por responsabilidade
Uma única função pode calcular tudo, mas mistura validação, percurso, desempate e montagem do relatório. Divida a solução em partes que possuam entrada, saída e motivo próprios:
validarEntradas(leituras, limiteDiario)
analisarDia(linha, indiceDoDia, estadoDoPico)
atualizarPico(valor, dia, periodo, estadoDoPico)
montarRelatorio(estados)
analisarConsumo(leituras, limiteDiario)
analisarConsumo coordena o fluxo. Ela não deve esconder todas as operações novamente; chama funções menores e conecta seus resultados.
Não fragmente operações triviais apenas para aumentar a quantidade de funções. O objetivo de dividir um algoritmo em partes é tornar responsabilidades observáveis, e não trocar uma função longa por dezenas de nomes sem significado.
Escreva primeiro a visão coordenadora
Comece pelo nível que conta a história do algoritmo:
função analisarConsumo(leituras, limiteDiario):
validarEntradas(leituras, limiteDiario)
totalGeral <- 0
quantidadeDeLeituras <- 0
totaisPorDia <- vetor vazio
diasAcimaDoLimite <- vetor vazio
maiorLeitura <- leituras[0][0]
diaDoPico <- 0
periodoDoPico <- 0
para dia de 0 até quantidadeDeLinhas(leituras) - 1:
totalDoDia <- 0
para periodo de 0 até quantidadeDeColunas(leituras) - 1:
valor <- leituras[dia][periodo]
totalDoDia <- totalDoDia + valor
totalGeral <- totalGeral + valor
quantidadeDeLeituras <- quantidadeDeLeituras + 1
se valor > maiorLeitura:
maiorLeitura <- valor
diaDoPico <- dia
periodoDoPico <- periodo
adicione totalDoDia em totaisPorDia
se totalDoDia > limiteDiario:
adicione dia + 1 em diasAcimaDoLimite
mediaPorLeitura <- totalGeral / quantidadeDeLeituras
retorne relatório com:
totalGeral
mediaPorLeitura
totaisPorDia
maiorLeitura
diaDoPico + 1
periodoDoPico + 1
diasAcimaDoLimite
tamanho(diasAcimaDoLimite)
A condição do pico usa > em vez de >=. Assim, um valor igual não substitui a posição já registrada e a primeira ocorrência vence, conforme o contrato.
Observe também a diferença entre índices internos e posições apresentadas. A matriz usa índices iniciados em zero; o relatório converte dia e período para números iniciados em um. Misturar essas convenções durante o percurso é uma fonte comum de erros de deslocamento.
Faça a validação proteger o restante do algoritmo
Escreva a validação de forma que, após seu sucesso, o restante possa confiar nas pré-condições:
função validarEntradas(leituras, limiteDiario):
se leituras não é uma matriz ou quantidadeDeLinhas(leituras) = 0:
gere erro "Informe ao menos um dia"
se limiteDiario não é número finito ou limiteDiario <= 0:
gere erro "O limite diário deve ser positivo"
quantidadeDePeriodos <- tamanho(leituras[0])
se quantidadeDePeriodos = 0:
gere erro "Informe ao menos um período"
para cada linha em leituras:
se tamanho(linha) != quantidadeDePeriodos:
gere erro "Todos os dias devem ter a mesma quantidade de períodos"
para cada valor em linha:
se valor não é número finito ou valor < 0:
gere erro "Cada leitura deve ser um número não negativo"
Validar toda a matriz antes de produzir o relatório garante uma propriedade simples: ou há um resultado completo, ou há um erro. Não existe saída parcialmente calculada que possa ser confundida com sucesso.
Em uma implementação real, a linguagem define como representar erros, números não finitos e matrizes. Adapte a sintaxe sem alterar o contrato observável.
Rastreie o exemplo e localize divergências
Faça um teste de mesa com rastreamento de variáveis para o caso principal. Não registre cada detalhe mecânico; acompanhe os estados que sustentam o relatório.
| Etapa concluída | totalDoDia | totalGeral | maiorLeitura | diasAcimaDoLimite |
|---|---|---|---|---|
| dia 1 | 6 | 6 | 3 | [] |
| dia 2 | 8 | 14 | 4 | [2] |
| dia 3 | 9 | 23 | 5 | [2, 3] |
Ao fim, quantidadeDeLeituras vale 9, e a média é 23 / 9. O pico foi encontrado no segundo período do terceiro dia.
Agora rastreie o caso de empate [[5, 1], [2, 5]]. O segundo 5 não atualiza o pico porque não é maior que o valor registrado. Se sua posição final for dia 2, período 2, a implementação descumpriu a regra de desempate.
Argumente por que a solução está correta
Testes aumentam a confiança, mas não cobrem todas as matrizes possíveis. Complete a verificação com propriedades que permanecem verdadeiras durante o percurso:
- após processar uma leitura,
totalGeralé a soma de todas as leituras vistas até ali; - após encerrar uma linha,
totalDoDiaé a soma de todos os períodos daquele dia; maiorLeituraé o maior valor entre todas as posições já visitadas;- a posição do pico aponta para a primeira ocorrência desse valor;
diasAcimaDoLimitecontém exatamente os dias completos cujo total é maior que o limite.
Essas propriedades são invariantes: afirmações preservadas a cada repetição. A inicialização as torna verdadeiras antes do percurso; cada atualização as mantém; quando os laços terminam, elas implicam os campos centrais do relatório.
Revise também o término. Os dois laços percorrem intervalos finitos, e seus índices avançam uma posição por iteração. Portanto, toda entrada que passou pela validação chega ao final.
Analise o custo sem contar apenas laços visíveis
Defina:
d = quantidade de dias
p = quantidade de períodos por dia
n = d × p = quantidade total de leituras
A validação visita as n leituras uma vez. O cálculo principal também visita as n leituras uma vez. Os dois percursos são sequenciais:
T(n) = n + n + trabalho constante
Logo, o tempo é Θ(n), ou Θ(d × p). Não é necessário multiplicar os dois percursos entre si; eles não estão aninhados um dentro do outro.
O relatório armazena um total para cada dia e, no pior caso, todos os dias na lista de alertas. Portanto, o espaço auxiliar é Θ(d). Os demais acumuladores ocupam espaço constante.
Esse tempo é apropriado ao requisito: para calcular a soma e descobrir o maior valor de dados arbitrários, cada leitura precisa ser observada pelo menos uma vez. A introdução à eficiência de algoritmos mostra como ligar a classe à contagem, em vez de deduzi-la apenas pelo formato do código.
Avalie a entrega com uma rubrica objetiva
Use a rubrica abaixo antes de considerar o projeto concluído. Cada critério vale de 0 a 2 pontos:
| Critério | 0 | 1 | 2 |
|---|---|---|---|
| contrato | ausente | parcial ou ambíguo | entradas, regras, saídas e erros claros |
| modelagem | estados sem propósito | maioria adequada | dados e estados mínimos bem justificados |
| decomposição | bloco único confuso | funções pouco coesas | responsabilidades claras e conectadas |
| algoritmo | incompleto | funciona no caso comum | cumpre contrato, fronteiras e desempate |
| testes | exemplos sem oráculo | poucos riscos cobertos | partições, fronteiras, inválidos e empate |
| rastreamento | ausente | apenas resultado final | estados intermediários localizam divergências |
| eficiência | rótulo sem justificativa | contagem parcial | dimensões, tempo e espaço explicados |
| comunicação | difícil de reproduzir | compreensível com lacunas | outra pessoa consegue revisar e executar |
Interpretação sugerida:
- 13 a 16 pontos: entrega consistente;
- 9 a 12 pontos: base funcional, com revisões localizadas;
- 0 a 8 pontos: retorne ao contrato e aos testes antes de acrescentar recursos.
A pontuação serve para orientar revisão, não para esconder defeitos. Uma entrada inválida aceita silenciosamente continua sendo um problema mesmo que as outras categorias estejam boas.
Estenda somente depois da versão básica estar correta
Escolha uma extensão por vez e atualize contrato, testes e custo:
- aceitar limites diferentes para cada dia;
- informar também o período com maior total acumulado;
- permitir leituras ausentes, definindo se devem ser rejeitadas ou ignoradas;
- comparar a semana atual com outra matriz de mesma dimensão;
- receber os dados em fluxo, sem armazenar toda a matriz;
- implementar a solução em uma linguagem e automatizar os testes.
Cada mudança altera decisões. Se leituras ausentes forem ignoradas, por exemplo, a média deve dividir apenas pela quantidade válida. Se os dados chegarem em fluxo, talvez não seja possível validar toda a entrada antes de calcular; o contrato de erro precisa ser redesenhado.
Não considere uma extensão concluída porque o caso feliz funcionou. Acrescente seu caso de teste, atualize a análise de eficiência e registre o que mudou na rubrica.
Entregue evidências, não apenas a resposta final
Organize sua entrega nesta ordem:
1. problema reescrito em uma frase
2. contrato de entradas, regras, saídas e erros
3. tabela de casos de teste e resultados esperados
4. decomposição das responsabilidades
5. pseudocódigo completo
6. teste de mesa do caso principal e de uma fronteira
7. argumento de correção
8. análise de tempo e espaço
9. autoavaliação pela rubrica
10. uma extensão proposta, mesmo que ainda não implementada
Essa sequência permite que outra pessoa verifique a solução sem adivinhar suas intenções. Clareza não é acabamento decorativo: faz parte da qualidade técnica do algoritmo.
O que você consolidou na trilha
Você partiu de uma descrição informal e chegou a uma solução especificada, modular, testada e analisada. Para isso, combinou entrada e saída, tipos, matrizes, decisões, repetições, acumuladores, funções, validação, rastreamento e eficiência.
O resultado mais importante não é memorizar este pseudocódigo. É dominar um processo que pode ser reutilizado:
entender → especificar → exemplificar → decompor → construir → testar → analisar → comunicar
Este é o último conteúdo da sequência atual. Use o catálogo da trilha de Lógica de Programação como mapa de revisão: retorne ao conceito correspondente sempre que a rubrica revelar uma lacuna e refaça o projeto com outro domínio quando quiser consolidar o método.
Referências
- ISO — ISO/IEC/IEEE 29148:2018: Requirements engineering. Acesso em 22 set. 2026.
- MIT OpenCourseWare — Introduction to Algorithms: Describing Algorithms. Acesso em 22 set. 2026.
- MIT OpenCourseWare — Software Construction: Testing. Acesso em 22 set. 2026.
- MIT OpenCourseWare — Software Construction: Specifications. Acesso em 22 set. 2026.
- ISTQB — Certified Tester Foundation Level Syllabus v4.0.1. Acesso em 22 set. 2026.
- MIT OpenCourseWare — 6.006 Recitation 1: Correctness and Efficiency. Acesso em 22 set. 2026.