Partilhar: WhatsApp
aulify · Sebenta
UC · Unidade de Competência · UC00245

Desenvolver algoritmos

Sebenta · Pensamento computacional, representação algorítmica e técnicas de construção
25h · 2.25 pontos crédito Curso: T. Desenv. Software, T. Multimédia, T. Inform. Gestão ↗ Referencial oficial SNQ
Índice

Apresentação

Esta sebenta apoia o estudo da unidade de competência UC00245 — Desenvolver algoritmos, com 25 horas de duração e 2,25 pontos de crédito, integrada na componente tecnológica dos cursos profissionais Técnico/a de Desenvolvimento de Software (481RA116), Técnico/a de Sistemas de Computação e Redes (481RA118) e Técnico/a de Multimédia (213RA104), publicados no Catálogo Nacional de Qualificações da ANQEP em outubro de 2025.

O documento segue rigorosamente o referencial oficial: os conhecimentos, realizações e critérios de desempenho apresentados são os que constam da base de dados pública do SNQ. Os exemplos, esquemas, exercícios resolvidos e comentários pedagógicos foram desenvolvidos pela equipa Aulify para apoiar a aprendizagem em sala de aula e o estudo autónomo.

A unidade tem por finalidade fornecer os alicerces conceptuais do pensamento algorítmico — não a programação numa linguagem específica. Os algoritmos aqui apresentados são posteriormente implementados em linguagens reais (Python, C, JavaScript) nas unidades de competência subsequentes. Quem dominar bem esta unidade adquire uma vantagem desproporcionada nas seguintes: a programação torna-se transcrição mecânica de um plano já feito.

Como usar esta sebenta. Cada capítulo combina exposição teórica, exemplos resolvidos, esquemas visuais e exercícios propostos. Recomenda-se a leitura sequencial dos capítulos 1 a 7 e a resolução prática dos capítulos 8 e 9 a par dos slides e fichas de trabalho disponíveis no portal Aulify.


Pensamento computacional

1.1 · Conceito e origem

O termo computational thinking foi popularizado por Jeannette Wing num artigo seminal publicado em 2006 na revista Communications of the ACM, no qual a autora propõe a sua adopção como competência fundamental do século XXI, ao nível da leitura, escrita e aritmética. Wing define-o como:

«O conjunto de processos de pensamento envolvidos na formulação de um problema e na expressão da sua solução, de tal forma que tanto um computador (humano ou máquina) possa eficazmente executá-la.» Jeannette Wing, Carnegie Mellon University, 2006

A definição é deliberadamente ampla. Pensamento computacional não é o domínio do software corrente (Word, Photoshop, Excel) — essa competência é designada por literacia digital. Tão pouco se confunde com a programação numa linguagem específica. O pensamento computacional é uma forma estruturada de raciocinar sobre problemas, independente do meio de execução. Um chef que organiza a mise en place da cozinha, um engenheiro que decompõe um projecto em fases, ou um médico que segue um protocolo clínico, estão a aplicar pensamento computacional, mesmo sem qualquer computador presente.

1.2 · Os quatro pilares

Embora diferentes autores proponham variantes, a divisão mais consensual na literatura pedagógica (em particular nos materiais da Computer Science Teachers Association e da BBC) identifica quatro pilares:

Pilar Definição operativa
Decomposição Dividir um problema complexo em sub-problemas mais pequenos e tratáveis.
Reconhecimento de padrões Identificar semelhanças entre o problema actual e problemas previamente resolvidos.
Abstração Ignorar detalhes irrelevantes e focar apenas no essencial; construir modelos simplificados da realidade.
Algoritmo Definir uma sequência ordenada, finita e executável de passos para resolver o problema.

Decomposição

Permite enfrentar problemas grandes. Em vez de resolver «construir uma casa», decompõe-se em fundações, estrutura, telhado, instalações, acabamentos. Cada sub-problema pode ser atribuído a uma equipa diferente, executado em paralelo, e testado isoladamente. Em informática, a decomposição é a base da modularização (funções, módulos, micro-serviços).

Reconhecimento de padrões

Permite reutilizar conhecimento. Quem aprendeu a ordenar uma lista de números por ordem crescente pode adaptar o algoritmo para ordenar palavras por ordem alfabética: o padrão de comparação-e-troca é o mesmo, muda apenas o critério. Reconhecer padrões poupa tempo e reduz a probabilidade de erros.

Abstração

Permite gerir complexidade. Um mapa do metro de Lisboa ignora distâncias reais, ruas, edifícios, altimetria — preserva apenas as paragens e as ligações entre elas, porque é essa informação que o utilizador precisa. Em programação, a abstração manifesta-se em conceitos como variáveis (que abstraem endereços de memória), funções (que abstraem sequências de instruções) e classes (que abstraem entidades do mundo real).

Algoritmo

É a síntese dos três pilares anteriores. Depois de decompor, reconhecer padrões e abstrair, expressa-se a solução como uma sequência precisa de instruções executáveis.

Pilar adicional · Generalização Alguns autores propõem um quinto pilar — generalização — que consiste em alargar uma solução particular a uma família mais ampla de problemas. Por simplicidade, esta sebenta integra-o no pilar do reconhecimento de padrões.

1.3 · Caso aplicado · localizar um livro numa biblioteca

Problema. Encontrar o exemplar de Os Lusíadas numa biblioteca municipal com cerca de 50 000 volumes.

Aplicação dos pilares.

INICIO
   Identificar autor: Camões, Luís de
   Localizar secção "Literatura Portuguesa"
   Localizar estante "C"
   PARA cada livro na prateleira FAÇA
      SE título = "Os Lusíadas" ENTÃO
         retirar livro
         SAIR DO CICLO
      FIM SE
   FIM PARA
   SE livro não encontrado ENTÃO
      pedir ajuda à bibliotecária
   FIM SE
FIM

Este exemplo, embora simples, já contém três estruturas de controlo que estudaremos formalmente no capítulo 4: sequência (passos numerados), iteração (PARA cada livro) e selecção (SE título = ...).

1.4 · Pensamento computacional em profissões não-informáticas

A literatura recente em didáctica das ciências documenta a aplicação produtiva do pensamento computacional em domínios diversos:

Esta omnipresença justifica o estatuto de competência transversal do pensamento computacional, mesmo para alunos que não venham a exercer profissões directamente ligadas à informática.


O conceito de algoritmo

2.1 · Definição formal

A definição mais consolidada na literatura é a de Donald Knuth (The Art of Computer Programming, 1968):

«Um algoritmo é uma sequência finita, ordenada e não-ambígua de instruções, formalmente bem definida, que resolve um problema ou executa uma tarefa em tempo finito.»

Esta definição condensa cinco propriedades que constituem o teste de validade de qualquer algoritmo. Se alguma delas falha, não estamos perante um algoritmo, mas perante uma heurística, um procedimento informal ou uma simples descrição.

2.2 · As cinco características essenciais

Característica Significado
Finitude Termina ao fim de um número finito de passos.
Definição Cada passo é claro e sem ambiguidade.
Eficácia Cada passo é executável com os meios disponíveis.
Determinismo A mesma entrada produz sempre a mesma saída.
Entrada e Saída Recebe zero ou mais entradas, produz uma ou mais saídas.

Finitude

«Calcular todos os números primos» não constitui algoritmo, porque os números primos são infinitos e o procedimento nunca termina. «Calcular todos os números primos menores que mil» constitui algoritmo, porque o conjunto de candidatos é finito.

Definição

A instrução «misture bem» não é definida — depende da interpretação subjectiva. A instrução «agite vigorosamente durante trinta segundos» é definida. Em programação, esta característica traduz-se na escolha cuidadosa de operadores, palavras-chave e nomes não ambíguos.

Eficácia

Cada passo tem de ser computável com os meios disponíveis. «Dividir por zero», «calcular a raiz quadrada de um número negativo no domínio dos reais» ou «escolher aleatoriamente um número irracional» não são eficazes — o passo não pode ser executado.

Determinismo

Para a mesma entrada, o algoritmo produz sempre a mesma saída. Esta propriedade é fundamental para a reprodutibilidade e para a depuração. Note-se, contudo, que existem algoritmos não-determinísticos úteis, como os algoritmos probabilísticos (criptografia, simulação de Monte Carlo, jogos), nos quais a aleatoriedade é uma escolha de design, não uma falha.

Entrada e Saída

Alguns algoritmos não recebem entrada explícita («calcular os primeiros dez números pares»), mas todos devem produzir alguma saída útil — caso contrário, não cumprem qualquer função.

2.3 · Acção, instrução e estado

Cada acção (ou instrução) de um algoritmo provoca uma transição no estado do sistema. O estado é o conjunto completo de valores que descrevem a situação do programa num dado instante — fundamentalmente, os valores das variáveis em memória, mas também ficheiros abertos, posições de cursor, conexões de rede, etc.

A execução de um algoritmo pode ser modelada como uma máquina de estados: parte de um estado inicial (E₀), e através de uma sequência de transições atinge um estado final (E_n). Esta visão é fundamental para a depuração: quando algo corre mal, identifica-se em que estado o sistema entrou indevidamente.

Exemplo · estados sucessivos

E₀:   x = 0,  y = 0    (estado inicial)
x ← 5
E₁:   x = 5,  y = 0
y ← x * 2
E₂:   x = 5,  y = 10
ESCREVER y
E₃:   x = 5,  y = 10   (estado final — y permanece, mas é exibido)

2.4 · Algoritmo versus programa

Esta distinção é das mais importantes de toda a programação:

Algoritmo Programa
Abstracto — linguagem natural, pseudocódigo ou fluxograma Concreto — código numa linguagem específica
Independente da linguagem de programação Específico de uma linguagem (Python, C, JavaScript, Java)
Independente do hardware Específico de uma plataforma
Pode ser executado por uma pessoa Executado por uma máquina
Vida útil potencialmente longa (décadas) Vida útil tipicamente curta (anos)

O mesmo algoritmo de ordenação quicksort dá origem a programas distintos em Python, C, Java ou JavaScript — programas com sintaxes muito diferentes, mas todos implementando a mesma lógica. Os algoritmos de Euclides, formulados no século III a.C., continuam a ser implementados em programas modernos de criptografia (cálculo do máximo divisor comum em chaves RSA). O algoritmo persiste; os programas evoluem.

Daí a precedência conceptual: pensar primeiro, codificar depois. Um bom algoritmo dá origem a programas correctos em qualquer linguagem; um mau algoritmo dá origem a bugs em todas elas.

2.5 · Algoritmos do quotidiano

O quadro seguinte ilustra a presença de lógica algorítmica em actividades quotidianas. Em cada caso, é possível identificar entradas, sequência ordenada de acções, condições explícitas e estado final claro.

Cenário Entrada Acções principais Saída
Preparar chá Água, saqueta, chávena Aquecer 2 min · Pôr saqueta · Aguardar 4 min · Retirar Chá pronto
Atravessar a passadeira Posição inicial Olhar à esquerda · Olhar à direita · Esperar se houver veículo · Atravessar Posição no outro lado
Levantar dinheiro na ATM Cartão, PIN, valor pretendido Inserir cartão · Marcar PIN · Indicar valor · Receber notas e talão Dinheiro em mão
Iniciar sessão num portal Email, palavra-passe Validar formato do email · Validar palavra-passe · Iniciar sessão · Redireccionar Acesso à área pessoal

Os exemplos digitais (ATM, login) são versões formalizadas, auditadas e executadas por máquinas dos exemplos físicos. A diferença não é de natureza mas de rigor.


Dados e operações

3.1 · Tipos de dados primitivos

Antes de poder formular operações, é preciso classificar os valores manipulados pelo algoritmo. Os tipos de dados primitivos mais comuns são:

Tipo Domínio Exemplos Operações típicas
Inteiro ℤ (números inteiros) 0, 42, −17, 1024 adição, subtracção, multiplicação, divisão inteira (DIV), resto (MOD)
Real ℝ (números reais) 3.14, −0.5, 2.71828 as anteriores, mais divisão real, potenciação
Carácter Unicode 'A', '?', 'ç' comparação, conversão para inteiro (código)
Cadeia (string) sequência de caracteres "Aulify", "olá mundo" concatenação, indexação, comprimento
Lógico (boolean) {Verdadeiro, Falso} V, F conjunção (E), disjunção (OU), negação (NÃO)

Cada linguagem de programação adopta nomes próprios para estes tipos (int, float, str, bool em Python; int, double, char, string, bool em C++), mas a essência conceptual é universal.

Um aspecto subtil mas importante é a distinção entre inteiro e real: a divisão 10 ÷ 3 produz resultados diferentes consoante o tipo. Em divisão inteira, 10 DIV 3 = 3 (resto 1); em divisão real, 10 / 3 = 3.333... A linguagem natural não distingue, mas em pseudocódigo e em programação a distinção é obrigatória.

A escolha do tipo certo para cada valor não é puramente técnica — tem consequências:

3.2 · Constantes e variáveis

Os algoritmos manipulam dois tipos de identificadores: constantes e variáveis.

Constante. Identificador associado a um valor que não muda durante a execução. Convenciona-se escrevê-la em maiúsculas. Exemplos típicos: PI = 3.14159, IVA = 0.23, MAX_TENTATIVAS = 3. O uso de constantes em vez de literais espalhados pelo código melhora a legibilidade e facilita a manutenção (alterar a taxa de IVA num único ponto).

Variável. Identificador associado a um valor que pode mudar durante a execução. Convenciona-se escrevê-la em minúsculas, com nomes descritivos (numAlunos, não n; precoUnitario, não p).

3.3 · Atribuição

A operação de atribuição associa um valor a uma variável. Em pseudocódigo português usa-se o símbolo (seta para a esquerda) ou, em alternativa, :=:

total ← 0
nome ← "Maria"
idade ← 17
total ← total + 10

A última linha causa confusão habitual em alunos com formação matemática sólida. Em matemática, x = x + 10 é uma contradição (implicaria 0 = 10). Em programação, a atribuição não é igualdade: é uma instrução com efeito sequencial. A leitura correcta é: «calcula a expressão à direita usando o valor actual de total, e guarda o resultado na mesma variável». Pode-se ler como «total recebe total mais 10».

A metáfora habitual é a da gaveta etiquetada: a variável é uma gaveta com um rótulo; a atribuição é abrir a gaveta, retirar o que lá está, fazer alguma operação e voltar a guardar.

3.4 · Entrada e saída de dados

Os algoritmos comunicam com o exterior através de operações de entrada (LER) e saída (ESCREVER):

LER idade
LER nome, apelido
ESCREVER "Olá, ", nome
ESCREVER "Tem ", idade, " anos."

Em programação real, estas operações correspondem a input() e print() em Python, a scanf e printf em C, a Scanner.nextLine() e System.out.println() em Java. Em pseudocódigo permanecemos no nível abstracto.

3.5 · Operadores

Aritméticos

Operador Significado Exemplo Resultado
+ adição 5 + 3 8
subtracção 5 − 3 2
* ou × multiplicação 5 * 3 15
/ divisão real 7 / 2 3.5
DIV divisão inteira 7 DIV 2 3
MOD resto da divisão inteira 7 MOD 2 1
^ potenciação 2 ^ 5 32

Os operadores DIV e MOD são particularmente úteis. Para testar se um número n é par, escreve-se n MOD 2 = 0. Para extrair os dígitos das unidades de um número, calcula-se n MOD 10. Para contar minutos numa duração em segundos, usa-se segundos DIV 60.

Relacionais

Operador Significado
= igual a
ou <> diferente de
< menor que
> maior que
ou <= menor ou igual a
ou >= maior ou igual a

Os operadores relacionais produzem sempre um valor lógico (Verdadeiro ou Falso). Por exemplo, a expressão idade ≥ 18 avalia a Verdadeiro quando a variável idade contém um valor maior ou igual a 18.

Lógicos

Operador Significado
E (AND) conjunção — Verdadeiro se ambos os operandos forem Verdadeiros
OU (OR) disjunção — Verdadeiro se pelo menos um operando for Verdadeiro
NÃO (NOT) negação — inverte o valor lógico

Estes operadores combinam expressões lógicas para formar condições complexas:

pode_votar ← (idade ≥ 18) E (nacionalidade = "PT")
pode_assistir ← (idade ≥ 16) OU acompanhado
indisponivel ← NÃO disponivel

3.6 · Precedência de operadores

À semelhança da matemática, os operadores têm uma ordem de avaliação definida. Em pseudocódigo (e na generalidade das linguagens):

  1. Parênteses
  2. Potenciação (^)
  3. Multiplicação, divisão, DIV, MOD
  4. Adição e subtracção
  5. Operadores relacionais
  6. NÃO
  7. E
  8. OU

Em caso de dúvida, usar parênteses. A clareza ganha-se sempre.


Estruturas lógicas básicas

4.1 · Teorema da programação estruturada

Em 1966, Corrado Böhm e Giuseppe Jacopini publicaram um teorema fundamental: qualquer algoritmo computável pode ser expresso usando apenas três estruturas de controlo de fluxo:

  1. Sequência — execução de instruções uma após outra.
  2. Selecção (alternativa) — escolha entre dois ou mais caminhos consoante uma condição.
  3. Iteração (repetição) — execução repetida de um bloco enquanto uma condição se mantiver.

Este resultado é a fundação teórica da programação estruturada, paradigma dominante no ensino e nas linguagens modernas. Tudo o que estudaremos nesta unidade combinará estas três estruturas.

4.2 · Estrutura sequencial

A mais simples e a default. As instruções são executadas exactamente pela ordem em que aparecem.

Exemplo · cálculo do preço com IVA

INICIO
   LER preco
   LER quantidade
   total ← preco * quantidade
   iva ← total * 0.23
   total_com_iva ← total + iva
   ESCREVER total_com_iva
FIM
A ordem importa. Se trocarmos a linha do `iva` com a do `total`, falhamos — a variável `total` ainda não existe quando se tenta calcular o IVA.

4.3 · Estrutura alternativa · SE-ENTÃO-SENÃO

Permite que o algoritmo tome diferentes caminhos consoante o valor de uma condição lógica.

Forma simples (sem alternativa):

SE idade ≥ 18 ENTÃO
   ESCREVER "Pode votar"
FIM SE

Forma composta (com alternativa):

SE idade ≥ 18 ENTÃO
   ESCREVER "Maior de idade"
SENÃO
   ESCREVER "Menor de idade"
FIM SE

Forma encadeada (várias alternativas):

SE media ≥ 18 ENTÃO
   ESCREVER "Muito Bom"
SENÃO SE media ≥ 14 ENTÃO
   ESCREVER "Bom"
SENÃO SE media ≥ 10 ENTÃO
   ESCREVER "Suficiente"
SENÃO
   ESCREVER "Insuficiente"
FIM SE

A condição é sempre uma expressão que avalia a Verdadeiro ou Falso. Quando há muitas alternativas mutuamente exclusivas, algumas linguagens (e variantes de pseudocódigo) oferecem uma estrutura mais legível conhecida por selecção múltipla, equivalente ao switch/case de C ou ao match de Python 3.10+. Não é objecto deste módulo.

Atenção aos casos limite A escolha do operador (`>`, `≥`, `<`, `≤`) determina o que acontece nos valores fronteiriços. Um aluno com média exactamente 14,0 deve ter «Bom» ou «Suficiente»? A resposta convencional é «Bom», por isso usamos `≥`. Verificar sempre o que acontece nos limites.

4.4 · Estrutura repetitiva

A iteração permite executar um bloco de instruções múltiplas vezes, evitando a duplicação de código e habilitando o processamento de quantidades arbitrárias de dados. Existem três variantes principais.

ENQUANTO (while) — pré-teste

Avalia a condição antes de executar o bloco. Se a condição for falsa logo no início, o bloco não chega a ser executado.

i ← 1
ENQUANTO i ≤ 10 FAÇA
   ESCREVER i
   i ← i + 1
FIM ENQUANTO

PARA (for) — contador

Usada quando o número de iterações é conhecido à partida.

PARA i DE 1 ATÉ 10 FAÇA
   ESCREVER i
FIM PARA

A variável i é automaticamente inicializada e incrementada. Alguns dialectos permitem definir um passo (DE 1 ATÉ 100 PASSO 5 percorre 1, 6, 11, …).

REPETIR-ATÉ (do-while) — pós-teste

Executa o bloco pelo menos uma vez, e só depois avalia a condição.

REPETIR
   ESCREVER "Insira a sua idade:"
   LER idade
ATÉ idade ≥ 0 E idade ≤ 120

Útil para validação de dados — pede-se sempre, e só se sai do ciclo quando o utilizador insere um valor válido.

Cuidado · ciclo infinito Se a condição de paragem nunca se tornar verdadeira (ou falsa, conforme o tipo de ciclo), o algoritmo nunca termina, violando a finitude. Garantir sempre que alguma instrução no corpo do ciclo altera a condição.

4.5 · Combinação de estruturas

As três estruturas combinam-se livremente e podem aninhar-se. O exemplo seguinte combina iteração, selecção e sequência:

Exemplo · contar pares e ímpares

INICIO
   pares ← 0
   impares ← 0

   PARA i DE 1 ATÉ 20 FAÇA
      SE i MOD 2 = 0 ENTÃO
         pares ← pares + 1
      SENÃO
         impares ← impares + 1
      FIM SE
   FIM PARA

   ESCREVER "Pares: ", pares
   ESCREVER "Ímpares: ", impares
FIM

A indentação não é decorativa: torna explícito o aninhamento das estruturas e é fundamental para a legibilidade.


Pseudocódigo

O pseudocódigo é uma notação textual que combina elementos da linguagem natural com convenções de programação. Não é executável por uma máquina, mas é precisa o suficiente para servir de especificação completa de um algoritmo, a partir da qual qualquer programador consegue produzir um programa numa linguagem real.

5.1 · Sintaxe portuguesa

Não existe norma oficial única; diferentes manuais usam variantes. A versão adoptada nesta sebenta é a mais comum nos materiais técnicos portugueses para os cursos profissionais.

Categoria Palavras-chave
Delimitação INICIOFIM
Entrada / Saída LER, ESCREVER
Atribuição (ou :=)
Selecção SEENTÃOSENÃOFIM SE
Iteração while ENQUANTOFAÇAFIM ENQUANTO
Iteração for PARADEATÉFAÇAFIM PARA
Iteração do-while REPETIRATÉ
Saída antecipada SAIR DO CICLO (ou INTERROMPER)
Comentários // linha · /* … */ bloco

5.2 · Boas práticas

Exemplo · cálculo da nota final de um aluno

INICIO
   // Lê três notas e calcula a média ponderada (50%, 30%, 20%)
   LER teste, ficha, oral

   // Validação básica
   SE teste < 0 OU teste > 20 OU ficha < 0 OU ficha > 20 OU oral < 0 OU oral > 20 ENTÃO
      ESCREVER "Notas inválidas — devem estar entre 0 e 20"
   SENÃO
      media ← teste * 0.5 + ficha * 0.3 + oral * 0.2

      SE media ≥ 10 ENTÃO
         ESCREVER "Aprovado com ", media
      SENÃO
         ESCREVER "Reprovado com ", media
      FIM SE
   FIM SE
FIM

Fluxograma

O fluxograma é a representação visual de um algoritmo, recorrendo a símbolos geométricos normalizados ligados por setas. Foi consagrado pela norma ISO 5807 (1985).

6.1 · Símbolos normativos

Símbolo Significado Exemplo
Elipse / stadium Terminal — início ou fim do algoritmo INÍCIO · FIM
Rectângulo Processo — operação ou cálculo total ← preco * qtd
Losango Decisão — pergunta com resposta binária idade ≥ 18 ?
Paralelogramo Entrada ou Saída — interacção com utilizador LER nome · ESCREVER total
Rectângulo com bordas laterais Sub-processo — chamada a sub-algoritmo ou função calcularMedia()
Círculo com letra Conector — liga partes distantes do diagrama A, B, C…
Seta Fluxo — define a sequência

6.2 · Convenções de leitura

6.3 · Ferramentas

A escolha entre pseudocódigo e fluxograma depende do contexto. Para algoritmos pequenos (até cerca de vinte instruções), o fluxograma é mais intuitivo, especialmente para audiências não-programadoras. Para algoritmos grandes, o pseudocódigo é mais económico — um fluxograma de cem nós torna-se ilegível.

6.4 · Conversão lado-a-lado

O mesmo algoritmo representado em pseudocódigo e em fluxograma — verificar se um número é par ou ímpar:

INICIO
   LER n
   SE n MOD 2 = 0 ENTÃO
      ESCREVER "Par"
   SENÃO
      ESCREVER "Ímpar"
   FIM SE
FIM

INÍCIO LER n n MOD 2 = 0? V F Par Ímpar FIM


Técnicas de construção · padrões reutilizáveis

Os algoritmos não-triviais combinam tipicamente um conjunto restrito de padrões que se repetem em milhares de problemas. Identificá-los economiza tempo e reduz erros. Esta secção apresenta os padrões mais frequentes em algoritmia básica.

7.1 · Contador

Conta ocorrências de um evento — quantos elementos satisfazem uma condição, quantas iterações foram feitas, etc.

Inicialização: n ← 0 Operação no ciclo: n ← n + 1

Exemplo · contar quantas notas são positivas

positivas ← 0
PARA i DE 1 ATÉ 10 FAÇA
   LER nota
   SE nota ≥ 10 ENTÃO
      positivas ← positivas + 1
   FIM SE
FIM PARA
ESCREVER "Notas positivas: ", positivas

7.2 · Acumulador

Acumula um valor — tipicamente a soma ou o produto de uma sequência.

Soma: inicializa a 0; operação soma ← soma + valor Produto: inicializa a 1; operação prod ← prod * valor

A escolha do valor inicial não é arbitrária: é o elemento neutro da operação (0 para a soma, 1 para o produto). Inicializar o produto a 0 seria fatal — todos os produtos seguintes dariam 0.

Exemplo · calcular o factorial de n

LER n
fact ← 1
PARA i DE 1 ATÉ n FAÇA
   fact ← fact * i
FIM PARA
ESCREVER "n! = ", fact

7.3 · Máximo e mínimo

Determina o maior (ou menor) elemento de uma sequência.

Estratégia recomendada: assumir que o primeiro elemento é o máximo (ou mínimo) provisório, e percorrer os restantes actualizando sempre que encontrar um valor maior (ou menor).

Exemplo · maior de n números lidos

LER n
LER primeiro
maior ← primeiro

PARA i DE 2 ATÉ n FAÇA
   LER valor
   SE valor > maior ENTÃO
      maior ← valor
   FIM SE
FIM PARA

ESCREVER "Maior valor: ", maior
A alternativa de inicializar com −∞ funciona, mas é desnecessariamente abstracta. Usar o primeiro elemento é mais elegante; obriga apenas a tratar o caso `n = 0` (lista vazia), que deve dar uma mensagem de erro em vez de uma resposta absurda.

7.4 · Sentinela

Valor especial que sinaliza o fim de uma sequência de comprimento indeterminado. Usa-se quando não se sabe à partida quantos elementos serão lidos.

Requisito: a sentinela tem de ser distinguível dos valores válidos. Para idades positivas, −1 é boa sentinela; para temperaturas (que podem ser negativas), há que escolher outro valor.

Exemplo · ler idades até aparecer −1 e calcular a média

soma ← 0
contador ← 0

LER idade
ENQUANTO idade ≠ -1 FAÇA
   soma ← soma + idade
   contador ← contador + 1
   LER idade
FIM ENQUANTO

SE contador > 0 ENTÃO
   ESCREVER "Média: ", soma / contador
SENÃO
   ESCREVER "Nenhuma idade lida"
FIM SE
O padrão **«ler antes do ciclo, ler dentro do ciclo»** evita processar a sentinela como se fosse um valor válido. A protecção contra divisão por zero (`SE contador > 0`) é essencial quando a sequência pode estar vazia.

7.5 · Validação de dados

Repete a leitura até que o valor introduzido satisfaça os critérios de validade. Implementa-se naturalmente com REPETIR-ATÉ.

Exemplo · validar uma nota entre 0 e 20

REPETIR
   ESCREVER "Insira a nota (0-20):"
   LER nota
   SE nota < 0 OU nota > 20 ENTÃO
      ESCREVER "Valor fora do intervalo. Tente outra vez."
   FIM SE
ATÉ nota  0 E nota  20

7.6 · Tabela-resumo

Padrão Quando usar Inicialização Operação
Contador Contar ocorrências n ← 0 n ← n + 1
Acumulador (soma) Somar valores soma ← 0 soma ← soma + valor
Acumulador (produto) Multiplicar valores prod ← 1 prod ← prod * valor
Máximo Maior valor visto primeiro elemento SE valor > max ENTÃO max ← valor
Mínimo Menor valor visto primeiro elemento SE valor < min ENTÃO min ← valor
Sentinela Parar leitura valor especial fora do domínio ENQUANTO valor ≠ SENTINELA
Validação Garantir entrada válida REPETIR … ATÉ condição

Casos resolvidos

Esta secção apresenta cinco problemas completos, cada um desenvolvido em três etapas: análise, algoritmo em pseudocódigo e implementação em Python. O leitor é convidado a tentar resolver cada problema antes de consultar a solução.

8.1 · Caso 1 · Maior de N números

Enunciado. Ler n números reais e indicar o maior.

Análise. Padrão «máximo». Lemos primeiro o n; depois o primeiro valor (que assumimos como provisório máximo); depois os restantes n − 1, actualizando o máximo sempre que encontramos um valor superior.

Pseudocódigo.

INICIO
   LER n

   SE n ≤ 0 ENTÃO
      ESCREVER "n tem de ser positivo"
      SAIR
   FIM SE

   LER primeiro
   maior ← primeiro

   PARA i DE 2 ATÉ n FAÇA
      LER valor
      SE valor > maior ENTÃO
         maior ← valor
      FIM SE
   FIM PARA

   ESCREVER "Maior: ", maior
FIM

Implementação em Python.

n = int(input("Quantos numeros? "))
if n <= 0:
    print("n tem de ser positivo")
else:
    maior = float(input("Numero 1: "))
    for i in range(2, n + 1):
        valor = float(input(f"Numero {i}: "))
        if valor > maior:
            maior = valor
    print(f"Maior: {maior}")

Teste. Para n = 4 e entradas 3.5, 7.2, 1.8, 5.0, a saída é Maior: 7.2.

8.2 · Caso 2 · Média com validação e sentinela

Enunciado. Ler notas (valores reais entre 0 e 20) até o utilizador introduzir −1. Calcular a média, ignorando notas inválidas e tratando o caso de não ter sido lida nenhuma nota válida.

Análise. Combinamos os padrões «sentinela», «validação», «contador» e «acumulador-soma».

Pseudocódigo.

INICIO
   soma ← 0
   contador ← 0

   ESCREVER "Insira notas (-1 para terminar):"
   LER nota

   ENQUANTO nota ≠ -1 FAÇA
      SE nota ≥ 0 E nota ≤ 20 ENTÃO
         soma ← soma + nota
         contador ← contador + 1
      SENÃO
         ESCREVER "Nota inválida — ignorada"
      FIM SE
      LER nota
   FIM ENQUANTO

   SE contador > 0 ENTÃO
      media ← soma / contador
      ESCREVER "Média de ", contador, " notas: ", media
   SENÃO
      ESCREVER "Nenhuma nota válida lida"
   FIM SE
FIM

Implementação em Python.

soma = 0.0
contador = 0

print("Insira notas (-1 para terminar):")
nota = float(input())
while nota != -1:
    if 0 <= nota <= 20:
        soma += nota
        contador += 1
    else:
        print("Nota invalida — ignorada")
    nota = float(input())

if contador > 0:
    print(f"Media de {contador} notas: {soma / contador:.2f}")
else:
    print("Nenhuma nota valida lida")

8.3 · Caso 3 · Tabuada de um número

Enunciado. Ler um número inteiro n e imprimir a sua tabuada (de 1 a 10).

Análise. Iteração simples com PARA e formatação de saída.

Pseudocódigo.

INICIO
   LER n
   PARA i DE 1 ATÉ 10 FAÇA
      ESCREVER n, " x ", i, " = ", n * i
   FIM PARA
FIM

Implementação em Python.

n = int(input("Numero: "))
for i in range(1, 11):
    print(f"{n} x {i} = {n * i}")

8.4 · Caso 4 · Verificar se um número é primo

Enunciado. Ler um número inteiro positivo n e indicar se é primo (divisível apenas por 1 e por si próprio).

Análise. Um número é primo se não tem divisores entre 2 e n − 1. Estratégia ingénua: testar todos. Estratégia melhor: bastam os divisores até √n (porque se houver divisor maior, existe correspondente menor). Usaremos a versão simples para clareza.

Pseudocódigo.

INICIO
   LER n

   SE n < 2 ENTÃO
      ESCREVER "Não é primo (n deve ser ≥ 2)"
      SAIR
   FIM SE

   ehPrimo ← Verdadeiro
   i ← 2

   ENQUANTO i < n E ehPrimo FAÇA
      SE n MOD i = 0 ENTÃO
         ehPrimo ← Falso
      FIM SE
      i ← i + 1
   FIM ENQUANTO

   SE ehPrimo ENTÃO
      ESCREVER n, " é primo"
   SENÃO
      ESCREVER n, " não é primo"
   FIM SE
FIM

Implementação em Python.

n = int(input("Numero: "))
if n < 2:
    print("Nao e primo (n deve ser >= 2)")
else:
    eh_primo = True
    i = 2
    while i < n and eh_primo:
        if n % i == 0:
            eh_primo = False
        i += 1
    print(f"{n} {'e' if eh_primo else 'nao e'} primo")

Observação. A condição i < n E ehPrimo permite sair cedo — assim que se descobre que n não é primo, o ciclo termina sem testar os restantes divisores.

8.5 · Caso 5 · Máximo, mínimo e média numa só passagem

Enunciado. Ler uma sequência de n valores reais. Devolver simultaneamente o máximo, o mínimo e a média.

Análise. Combinação dos padrões «máximo», «mínimo», «acumulador-soma» e «contador» — todos actualizados na mesma passagem pelos dados, em vez de três passagens separadas.

Pseudocódigo.

INICIO
   LER n

   SE n ≤ 0 ENTÃO
      ESCREVER "n tem de ser positivo"
      SAIR
   FIM SE

   LER primeiro
   max ← primeiro
   min ← primeiro
   soma ← primeiro

   PARA i DE 2 ATÉ n FAÇA
      LER valor
      SE valor > max ENTÃO max ← valor FIM SE
      SE valor < min ENTÃO min ← valor FIM SE
      soma ← soma + valor
   FIM PARA

   ESCREVER "Máximo: ", max
   ESCREVER "Mínimo: ", min
   ESCREVER "Média: ", soma / n
FIM

Este caso é importante: ilustra que vários padrões podem coexistir num único ciclo, evitando passagens redundantes pelos dados. Em algoritmos sobre grandes volumes de dados, esta optimização é crítica para o desempenho.


Testar e depurar

Construir um algoritmo é metade do trabalho; garantir que está correcto é a outra metade. Esta secção apresenta as técnicas básicas de teste e depuração.

9.1 · Trace table — execução à mão

A trace table (tabela de execução) é uma tabela que regista, passo a passo, o valor de cada variável após cada instrução. Permite simular a execução do algoritmo no papel, sem computador.

Exemplo · trace table para o factorial de 4

LER n            // 4
fact ← 1
PARA i DE 1 ATÉ n FAÇA
   fact ← fact * i
FIM PARA
| Passo | `n` | `i` | `fact` | Observação | |---|---|---|---|---| | Inicial | 4 | — | 1 | Após inicializações | | Iteração 1 | 4 | 1 | 1 | 1 × 1 | | Iteração 2 | 4 | 2 | 2 | 1 × 2 | | Iteração 3 | 4 | 3 | 6 | 2 × 3 | | Iteração 4 | 4 | 4 | **24** | 6 × 4 | Resultado: `fact = 24`. Verificação matemática: 4! = 4 × 3 × 2 × 1 = 24. ✓

A trace table é especialmente útil quando um algoritmo produz um resultado errado e não se compreende porquê: a tabela mostra exactamente em que iteração e em que variável surgiu o desvio.

9.2 · Erros típicos

Listamos os erros mais frequentes em algoritmia básica, com sintomas e causas:

Tipo Sintoma Exemplo
Off-by-one Conta uma a mais ou a menos PARA i DE 1 ATÉ n quando devia ser n − 1 (ou vice-versa)
Inicialização errada Resultados absurdos soma ← 1 em vez de ← 0; prod ← 0 em vez de ← 1
Condição invertida Caminho errado SE x < 10 quando se queria >
Ciclo infinito Algoritmo nunca termina Esquecer i ← i + 1 no corpo de um ENQUANTO
Divisão por zero Erro de execução Falta SE contador > 0 antes de calcular média
Comparação de tipos diferentes Comparações sempre falsas Comparar a cadeia "10" com o inteiro 10
Lógica errada Resultado errado nos casos limite Usar E quando se queria OU
Uso de variável não inicializada Comportamento imprevisível Usar total sem antes lhe atribuir valor

A frequência do erro off-by-one justifica o seu nome próprio na literatura anglo-saxónica (lê-se off-by-one error). Ocorre tipicamente quando o programador hesita entre incluir ou excluir um limite do intervalo.

9.3 · Programação defensiva

A programação defensiva consiste em assumir que tudo o que pode correr mal eventualmente correrá. Em vez de confiar em utilizadores cooperativos e contextos perfeitos, antecipam-se falhas e protege-se o algoritmo:

A regra prática: antes de cada operação arriscada, perguntar «em que circunstância isto pode falhar?» e acrescentar protecção apropriada.

9.4 · Estratégias de optimização

Optimizar significa tornar o algoritmo melhor — habitualmente mais rápido, mais económico em memória, ou mais legível — sem alterar o resultado.

Estratégia Exemplo
Sair cedo Em vez de continuar um ciclo após já saber a resposta, interromper (SAIR DO CICLO)
Evitar recálculo Guardar em variável um resultado intermédio que se reutiliza
Escolher a estrutura certa PARA é mais claro que ENQUANTO quando o número de iterações é conhecido
Reduzir aninhamento Achatar SE encadeados, recorrer a SENÃO SE
Nomes descritivos mediaAlunos em vez de m — beneficio gratuito de legibilidade
Comentar o «porquê» O quê já se vê; o porquê não

A célebre advertência de Knuth permanece actual:

«A optimização prematura é a raiz de todo o mal.» Donald Knuth, 1974

A leitura correcta não é «nunca optimizar», mas sim: primeiro fazer funcionar correctamente, depois optimizar — e apenas onde for medível e justificado. Em particular, optimizar para legibilidade é sempre defensável; código é lido muitas mais vezes do que escrito.


Apêndices

A · Sintaxe de pseudocódigo · referência rápida

INICIO                       // delimita o algoritmo
   ...
FIM

LER variavel                 // entrada
ESCREVER expressao           // saída
variavel  valor             // atribuição

SE condição ENTÃO            // selecção
   ...
SENÃO SE condição ENTÃO
   ...
SENÃO
   ...
FIM SE

PARA i DE 1 ATÉ n FAÇA       // iteração contada
   ...
FIM PARA

ENQUANTO condição FAÇA       // iteração pré-teste
   ...
FIM ENQUANTO

REPETIR                      // iteração pós-teste
   ...
ATÉ condição

SAIR DO CICLO                // interrompe ciclo
SAIR                         // interrompe algoritmo

// comentário de linha
/* comentário de bloco */

B · Glossário

Abstração. Processo de simplificar a realidade, ignorando detalhes irrelevantes para o problema em causa.

Acumulador. Variável que armazena a soma (ou produto) progressiva de uma sequência de valores.

Algoritmo. Sequência finita, ordenada e não-ambígua de instruções que resolve um problema em tempo finito.

Atribuição. Operação que associa um valor a uma variável. Notada por em pseudocódigo PT.

Booleano. Tipo de dado lógico, com dois valores possíveis: Verdadeiro e Falso.

Cadeia (string). Tipo de dado que representa uma sequência ordenada de caracteres.

Ciclo infinito. Iteração cuja condição de paragem nunca se torna falsa, violando a finitude do algoritmo.

Constante. Identificador cujo valor não muda durante a execução do algoritmo.

Contador. Variável que regista o número de ocorrências de um evento.

Critério de desempenho. No referencial SNQ, requisito qualitativo que o aluno deve cumprir para a realização ser considerada bem executada.

Decomposição. Divisão de um problema complexo em sub-problemas mais pequenos.

Determinismo. Propriedade de um algoritmo cujo resultado depende exclusivamente da entrada.

Estado. Conjunto dos valores de todas as variáveis num dado instante da execução.

Fluxograma. Representação visual de um algoritmo, usando símbolos geométricos normalizados (ISO 5807).

Iteração. Estrutura de controlo que repete um bloco de instruções enquanto uma condição se mantém verdadeira (ou até deixar de ser falsa).

Padrão de programação. Receita reutilizável aplicável a uma família de problemas semelhantes.

Pseudocódigo. Notação textual semi-formal para descrever algoritmos.

Selecção. Estrutura de controlo que escolhe entre dois ou mais caminhos consoante o valor de uma condição.

Sentinela. Valor especial que sinaliza o fim de uma sequência de comprimento desconhecido.

Sequência. Estrutura de controlo na qual as instruções são executadas pela ordem em que aparecem.

Tipo de dado. Categoria que define os valores possíveis para uma variável e as operações que lhe podem ser aplicadas.

Trace table (tabela de execução). Tabela que regista o valor das variáveis a cada passo do algoritmo.

Variável. Identificador associado a um valor que pode mudar durante a execução.

C · Recursos para aprofundamento

Software e ambientes.

Cursos online (gratuitos).

Leitura complementar.

Referencial oficial.