Escolar Documentos
Profissional Documentos
Cultura Documentos
Algoritmos Livro Completo PDF
Algoritmos Livro Completo PDF
Técnicas de Programação
José Augusto N. G. Manzano
Ecivaldo Matos
André Evandro Lourenço
Algoritmos
Técnicas de Programação
1ª Edição
www.editoraerica.com.br
1
Dados Internacionais de Catalogação na Publicação (CIP)
(Câmara Brasileira do Livro, SP, Brasil)
Bibliografia
ISBN 978-85-365-0737-8
14-01498 CDD-005.1
Os Autores e a Editora acreditam que todas as informações aqui apresentadas estão corretas e podem ser utilizadas para qualquer fim legal.
Entretanto, não existe qualquer garantia, explícita ou implícita, de que o uso de tais informações conduzirá sempre ao resultado desejado.
Os nomes de sites e empresas, porventura mencionados, foram utilizados apenas para ilustrar os exemplos, não tendo vínculo nenhum com
o livro, não garantindo a sua existência nem divulgação. Eventuais erratas estarão disponíveis para download no site da Editora Érica.
Conteúdo adaptado ao Novo Acordo Ortográfico da Língua Portuguesa, em execução desde 1º de janeiro de 2009.
A ilustração de capa e algumas imagens de miolo foram retiradas de <www.shutterstock.com>, empresa com a qual se mantém contrato
ativo na data de publicação do livro. Outras foram obtidas da Coleção MasterClips/MasterPhotos© da IMSI, 100 Rowland Way, 3rd floor
Novato, CA 94945, USA, e do CorelDRAW X5 e X6, Corel Gallery e Corel Corporation Samples. Copyright© 2013 Editora Érica, Corel
Corporation e seus licenciadores. Todos os direitos reservados.
Todos os esforços foram feitos para creditar devidamente os detentores dos direitos das imagens utilizadas neste livro. Eventuais omissões
de crédito e copyright não são intencionais e serão devidamente solucionadas nas próximas edições, bastando que seus proprietários conta-
tem os editores.
Agradecemos a todos aqueles que acreditam na educação como um dos elementos fundamen-
tais para a mudança social.
3
Sobre os autores
5
Capítulo 4 - Programação com Laços......................................................................................... 53
4.1 Controle de ciclos............................................................................................................................................................53
4.2 Laço condicional pré-teste..............................................................................................................................................54
4.3 Laço condicional pós-teste.............................................................................................................................................57
4.4 Laço incondicional..........................................................................................................................................................60
Agora é com você!..................................................................................................................................................................62
Bibliografia........................................................................................................................... 125
7
No Capítulo 7 é apresentada a técnica de criação e uso de registros, uma maneira de incorpo-
rar em uma única matriz dados de tipos diferentes.
A organização de um programa em módulos é tema do Capítulo 8, onde são apresentadas
as definições e uso de funções, procedimentos e passagens de parâmetro. Nesse capítulo são apre-
sentados, ainda, o escopo de uso de variáveis, as ideias de uso dos métodos de trabalho top-down e
bottom-up, além da visão sobre os princípios de dividir para conquistar.
O Capítulo 9, último deste trabalho, apresenta temas relacionados a princípios de uso de
algoritmos. Nesse contexto, é indicada, de forma introdutória, noções sobre fundamentos de medi-
das de complexidade de algoritmos, fundamentos de otimalidade de algoritmos, fundamentos de
backtracking e ações de busca de padrões em strings.
Esperamos que este trabalho seja útil não só ao aluno, mas também ao professor.
Um abraço a todos!
Os autores
Para começar
Com o objetivo de desenvolver um bom raciocínio lógico, é apresentado neste capítulo um breve
panorama da história do computador, bem como os conceitos de lógica de programação e, com base em
normas internacionais (ISO 5807:1985), as formas de representação em diagrama de bloco e português
estruturado. No fim do capítulo, você encontrará uma apresentação do princípio usado para resolução
de problemas.
9
Wikimedia Commons/Mike Cowlishaw
Figura 1.1 - Ábaco.
Somente em 1642, o francês Blaise Pascal cria uma máquina com capacidade para realizar
somas e subtração de forma automática. Essa máquina, batizada de Pascalina, simulava por meio de
rodas dentadas o funcionamento do ábaco. Alguns anos depois, em 1672, Gottfried Wilhelm Leibniz
consegue modificar a máquina de Pascal com o objetivo de incluir operações de multiplicar e dividir.
Wikimedia Commons/Tieum512
Diversas máquinas foram criadas ao longo do tempo por diversos inventores. O objetivo das
máquinas sempre foi auxiliar em cálculos complexos ou substituir o homem em alguma atividade.
Entre as diversas máquinas que foram criadas, temos: máquina de tear (1804), de Joseph Marie
Jacquard; máquina de diferenças (1822); máquina analítica, (1837) de Charles Babbage; e máquina
de Hollerith (1890), de Hermann Hollerith.
Todas as máquinas criadas até então eram dispositivos mecânicos e não utilizavam nenhum
tipo de circuito elétrico.
A utilização de circuitos elétricos só foi possível depois que um matemático chamado George
Boole criou um sistema lógico no qual tudo poderia ser representado por meio de dois algarismos:
0 ou 1. Nascia aí a Lógica Booleana, utilizada até hoje em todos os modernos computadores. Em
sua teoria, 0 (zero) representa algo que esteja desligado, apagado, sem valor. Já o valor 1 (um) repre-
senta ligado, aceso. Com a combinação desses algarismos, podemos criar outros valores. Vejamos
um exemplo que utilize 2 algarismos:
00 - Apagado
01 - Luz verde
10 - Luz amarela
11 - Luz vermelha
Fique de olho!
Para cada algarismo utilizado, damos o nome de bit. No exemplo acima, temos dois bits. Para construir os símbolos uti-
lizados em nosso alfabeto (letras e números) utilizamos oito algarismos, ou seja, oito bits. Para um conjunto de oito bits
damos o nome de byte. Portanto, para formar uma letra ou número do nosso alfabeto, utilizamos um byte.
1 byte = 8 bits
Para que o tratamento dos dados de entrada ocorra, ou seja, para transformar (processar) os
dados em informação de saída, é necessário o uso de um algoritmo desenvolvido para tal finalidade.
Os algoritmos são utilizados para descrever os passos necessários para que um programa de compu-
tador execute uma tarefa que ele foi designado a fazer. Portanto, a lógica de programação é a forma
como se escreve esse algoritmo, significando o uso correto das leis do pensamento, da “ordem da
razão” e de processos de raciocínio (FORBELLONE; EBERSPACHER, 2000) para resolver o pro-
blema proposto.
A literatura está repleta de definições para algoritmo:
»» processo sistemático para a resolução de um problema (SZWARCFITER; MARKENZON,
1994);
»» sequência de passos que visam atingir um objetivo bem definido (FORBELLONE;
EBERSPACHER, 2000);
»» sequência ordenada de passos que deve ser seguida para a realização de uma tarefa
(SALIBA, 1993; BERG; FIGUEIRÓ, 1998).
Um programador de computador poderá criar um projeto de algoritmo utilizando uma repre-
sentação gráfica (diagrama de blocos) ou até mesmo uma representação textual (português estrutu-
rado). Ambas são bastante utilizadas no mercado e podemos defini-las como:
Fique de olho!
Em 1961, no Instituto Tecnológico de Aeronáutica (ITA), foi criado o primeiro computador brasileiro, chamado Zezinho.
No ano seguinte, ele foi desmontado e suas peças foram usadas em outros projetos.
Solução
Início
N1, N2
RESULTADO N1+N2
RESULTADO
Fim
Português estruturado
programa SOMA
var
N1, N2, RESULTADO: real
inicio
leia N1, N2
RESULTADO ← N1 + N2
escreva RESULTADO
fim
Fique de olho!
Um computador quântico é um equipamento com a capacidade de executar cálculos com base no uso de propriedades de
Mecânica Quântica, como interferência e sobreposição. Um exemplo de computador desse tipo é o D-Wave Two, desen-
volvido pela empresa D-Wave Systems, Inc.
1.6.1 Interpretadores
São chamados interpretadores quando o programa conversor lê uma instrução do código-
-fonte, a converte em instrução de máquina (código binário) e já a executa. Em seguida, ele pega a
próxima instrução e repete o processo até que todas as instruções sejam executadas.
1.6.2 Compiladores
Um compilador trabalha de forma semelhante ao interpretador, porém, ao final do processo de
conversão, é gerado um arquivo chamado código executável (programa executável). O código execu-
tável é um arquivo binário que será executado diretamente pela máquina sem a necessidade de inter-
pretação linha a linha.
»» Vantagens
É executado de forma mais rápida e diretamente ao nível de máquina.
»» Desvantagens
A manutenção só é possível a partir do código-fonte.
O código executável gerado só poderá ser executado em máquinas de mesma arquitetura.
Exemplos: C, C++, C#, Pascal etc.
1.6.3 Tradutores
Existe um terceiro método que trabalha de forma intermediária entre os compiladores e inter-
pretadores. Um tradutor gera, a partir do código-fonte, um código intermediário, mas que não exige
tanto espaço de memória quanto o código original. É gerado, a partir do código intermediário, o
código executável da forma que um interpretador funciona.
»» Vantagens
Independência da arquitetura que fará a execução final.
»» Desvantagens
Necessidade de um interpretador específico na máquina do usuário final que fará a inter-
pretação.
Exemplo: Java
Exercício resolvido
Um funcionário recebe determinado salário mensal. Faça um programa que leia o valor do
salário mensal e o índice (em porcentagem) do reajuste a ser concedido. O programa deverá
imprimir na tela o novo salário do funcionário.
Solução
Em primeiro lugar, devemos entender o principal problema a ser resolvido. Nesse caso, calcu-
lar o valor do novo salário.
A segunda etapa apresenta um detalhamento no que se refere à entrada e saída, ou seja, deve-
-se entrar com o valor do salário atual e com o índice do reajuste para que, após o cálculo, seja
exibido o valor do novo salário.
Início
SALÁRIO, ÍNDICE
novoSalário
Fim
Você viu, na Figura 1.10, a primeira forma de notação gráfica. Agora, iremos transcrever o dia-
grama de blocos para uma forma narrativa denominada pseudocódigo ou português estruturado.
O português estruturado se aproxima muito da linguagem (por exemplo, PASCAL, C,
FORTRAN, BASIC, JAVA, PHP etc.) utilizada pelos computadores para gerarem o programa a
ser executado, porém, não tem o mesmo rigor sintático de sua escrita.
A seguir, é apresentado um exemplo do algoritmo escrito em português estruturado.
programa SALARIO
var
SALÁRIO, ÍNDICE, NOVOSALÁRIO: real
inicio
leia SALÁRIO, ÍNDICE
NOVOSALÁRIO ← SALÁRIO + SALÁRIO * ÍNDICE / 100
escreva NOVOSALÁRIO
fim
Tabela 1.3 - Tabela ASCII estendida (códigos de caracteres 128-255; página de código 850)
Vamos recapitular?
Foi apresentado a você um panorama básico da história do computador, bem como os conceitos
de lógica de programação de computadores, suas formas de representação em diagrama de blocos e por-
tuguês estruturado, permitindo um contato inicial com o conteúdo que será abordado mais detalhada-
mente ao longo desta obra.
Para começar
Neste capítulo, vamos estudar uma das mais importantes técnicas de programação de computado-
res: a programação sequencial. Ao final do capítulo, será possível identificar e implementar programas
sequenciais usando tipos primitivos de dados, variáveis, constantes, operadores fundamentais e mecanis-
mos básicos de entrada de dados, processamento e saída de dados.
25
lógicas e/ou aritméticas, como em um cálculo de índice de massa corporal, ou, ainda, utilizá-los para
geração de outros dados e informações.
A última etapa, saída de dados, só ocorre após haver algum tipo de processamento, pois ela
apresenta os dados novos ou transformados por ele, com os quais é possível verificar situações e
tomar decisões; por exemplo, procurar estudar mais a fim de aprender e ser considerado aprovado
em determinada disciplina, ao verificar que, após o cálculo, a média parcial do aluno está muito
abaixo do valor mínimo para aprovação.
Fique de olho!
Geralmente, não conseguimos fazer cálculos com dados do tipo string, sendo mais utilizados
em operações de entrada e saída de dados, como receber o nome de um profissional (entrada de
dados) ou emitir um parecer sobre sua habilitação (saída de dados).
2.3 Variáveis
Variável é uma entidade computacional que representa um espaço reservado na memória
do computador para armazenamento de dados durante a execução de um programa. Esses dados
podem sofrer modificação ao longo da execução do programa, por isso têm esse nome.
Para definir as variáveis, é necessário atribuir um nome a elas, conhecido como identificador.
Identificar previamente as variáveis é muito importante para usá-las adequadamente no programa.
Em muitas linguagens, isso é obrigatório. Antes de identificar as variáveis, é preciso saber que tipo
de dado elas representarão. Você pode, inclusive, definir um padrão de nomenclatura para facilitar a
identificação do tipo de dado que a variável contém.
Além disso, há algumas regras que todo programador deve seguir ao estabelecer o nome de
uma variável:
»» Inicie o nome apenas por caractere alfabético ou “_” (underline); nenhum outro caractere
é aceito no início de um identificador (nome de variável).
»» Não utilize caracteres especiais na nomenclatura de uma variável (é, ç, ã, &, %, #, @, *, -,
entre outros), exceto o caractere “_” (underline).
Programação Sequencial 27
»» Se a variável for identificada por um nome composto, não pode haver espaço em branco;
um modo comum de identificar variáveis com nomes compostos é utilizar o caractere “_”
(underline).
»» Não escolha nomes que já sejam utilizados pela linguagem de programação para representar
comandos ou instruções predefinidas; esses nomes são chamados de palavras reservadas.
»» Crie identificadores que representem o conteúdo da variável.
»» Não utilize o mesmo nome para mais de uma variável.
São exemplos de nomes válidos: TipoDeInvestimento; CPF_Titular; FUNCIONAL_SETOR10;
endereco. Por sua vez, os identificadores a seguir não são válidos, pois violam as regras expostas
anteriormente: Endereço; 1a.Avaliacao; 7FONE; Resultado#Calculo; E-mail.
Há, ainda, palavras reservadas às linguagens de progra-
mação (mas isso varia conforme a linguagem), as quais foram
previamente definidas para funcionarem como comandos ou ins-
truções do computador, não podendo ser usadas para outro fim. Os identificadores são de uso único.
Em geral, algumas palavras reservadas
Para declarar variáveis em algoritmos, ou seja, criar variá- são típicas, tais como: int, integer, real,
veis com identificador único, utilizamos o comando var. Logo boolean, function, result, return, entre outras.
abaixo da palavra var podemos indicar as variáveis que deseja-
mos criar, sempre respeitando a sintaxe apresentada a seguir:
var
<identificador da variável> : <tipo primitivo associado à variável>
2.4 Constantes
Além das variáveis, temos as entidades conhecidas como constantes, que são valores fixos, está-
veis. Uma vez atribuído, o valor de uma constante mantém-se o mesmo até o final da execução do
programa.
Constantes são muito úteis na definição de valores que serão usados diversas vezes no pro-
grama e que não devem sofrer alteração. Como exemplos, temos a grandeza matemática pi (π), cujo
valor aproximado é 3,14159, ou, ainda, a alíquota base de cálculo do INSS (11%).
No programa, esses valores poderiam ser definidos como constantes e ser representados pelos
identificadores dessas constantes. Por exemplo: pi e aliquota_inss.
Para declarar constantes em algoritmos, ou seja, criar constantes com identificador único, uti-
lizamos o comando const. Logo abaixo da palavra const podemos indicar as constantes que deseja-
mos criar, sempre observando a sintaxe apresentada a seguir:
const
PI = 3.14159
ALIQUOTA_INSS = 0.11
Fique de olho!
A operação matemática para o cálculo da exponenciação é definida de maneira muito variada nas linguagens de progra-
mação de computadores. Considerando a operação de ax, esse cálculo pode ser executado, dependendo da linguagem de
programação em uso, como: pow(a,x); power(a,x); a^x ou a**x, entre outras formas. No entanto, há uma maneira gené-
rica de realizar esse cálculo: por meio das funções dos logaritmos neperiano e exponencial, com base na expressão ex ln a,
que pode ser escrita de modo genérico - para as linguagens de programação - pela expressão exp(x * ln(a)), considerando que
as funções exp() e ln() podem ser referenciadas, na linguagem em uso, de modo um pouco diferente do apresen-
tado neste livro, mas mantendo essa característica.
Programação Sequencial 29
Por exemplo, considere o cálculo do salário dos funcionários de uma empresa de logística, cuja
fórmula é a seguinte: SALÁRIO = COMISSÃO2 – (SALÁRIO x ALÍQUOTA DO INSS). SALÁRIO e
COMISSÃO contêm valores que variam conforme o funcionário. Por sua vez, ALÍQUOTA DO INSS
possui um valor fixo (0.11) que vale para todos os funcionários daquela instituição.
Ao converter essa fórmula na linguagem computacional, é preciso fazer algumas adequações.
A primeira é definir os identificadores das variáveis e das constantes, de acordo com as regras estudadas.
A segunda é substituir os operadores matemáticos pelos aritméticos da computação. Por exemplo, o
símbolo que representa a operação de multiplicação - x - será substituído pelo operador *.
Logo, a fórmula do salário ora apresentada poderia ser implementada no algoritmo da seguinte
forma:
SALARIO ← (COMISSAO ↑ 2) – (SALARIO * ALIQUOTA_INSS)
ou
SALARIO ← (COMISSÃO * COMISSAO) – (SALARIO * ALIQUOTA_INSS)
Em programação, utilizamos parênteses para indicar qualquer ordem de prioridade; logo, as
chaves e os colchetes empregados em operações aritméticas, na matemática, são substituídos pelos
parênteses. O sinal de igual (=) também é substituído pelo sinal de atribuição de valores (←); ele
indica que o valor da expressão aritmética será armazenado na variável à esquerda do sinal. No
exemplo, o cálculo será armazenado na variável SALARIO. Antes de efetuar a atribuição, o compu-
tador limpa o conteúdo da variável para não haver interferência do valor antigo sobre o novo valor.
Considere, ainda, a necessidade de representar a fórmula de cálculo da área da coroa circular:
R
Acc = (R2 r2)
Programação Sequencial 31
Diagrama de bloco
Diagrama de bloco é uma notação utilizada para descrever algoritmos. Veja na Figura 2.2
alguns dos símbolos mais comuns.
Figura 2.2 - Estrutura dos símbolos para as instruções de entrada e de saída de dados.
Português estruturado
Outra forma de descrever algoritmos é por meio do formato português estruturado, em que o
algoritmo é definido textualmente, muito próximo de como será implementado no computador, mas
em idioma português. Por exemplo, podemos utilizar a seguinte sintaxe para escrever um pequeno
trecho de programa para saque bancário:
[...]
escreva "Quanto deseja sacar?"
leia VALOR_SAQUE
se (SALDO >= VALOR_SAQUE) então
SALDO ← SALDO – VALOR_SAQUE
escreva "Saque realizado com sucesso, retire seu dinheiro."
senão
escreva "Saque não realizado."
fim_se
[...]
Programa é um conjunto estruturado de instruções que permitem ao computador realizar operações de transformação de
dados. Para isso, o programa deve apresentar um conjunto de instruções que indiquem ao computador como executar
tais operações. Essas instruções são formadas por estruturas de controle que variam de uma linguagem para outra. Os
modos mais comuns de estruturação desse controle são:
» Estruturação iterativa: baseada em controle de iteração (ciclos); não permite desvios incondicionais.
» Estruturação recursiva: baseada em mecanismos de sub-rotinas recursivas, ou seja, que se utilizam de si próprias para
definir a próxima operação, sendo uma forma indutiva de especificar operações; assim como a estruturação iterativa,
não permite desvios incondicionais.
Exemplo
Elaborar um programa de computador que calcule e apresente o volume de um cubo.
Solução
Para calcular o volume de um cubo, é necessário conhecer dois elementos. O primeiro é a fór-
mula de cálculo do volume de cubo, sendo V = L3, em que L é o valor de um dos lados do cubo
(todos os lados têm o mesmo comprimento) e V é o volume calculado. Sendo assim, basta
estabelecer que a fórmula matemática deve ser convertida em uma expressão aritmética algo-
rítmica equivalente, que pode ser:
V ← L * L * L ou V ← L ↑ 3.
Diagrama de bloco
Início
V L 3
Fim
Português estruturado
programa VOLUME_CUBO
var
L : real
V : real
início
leia L
V ← L ↑ 3
escreva V
fim
Programação Sequencial 33
Desenvolver um programa que calcule o índice de massa corpórea (IMC) de uma pessoa. Para
elaborar o programa, é necessário possuir dois dados: altura e peso da pessoa. Para cálculo do
IMC, a fórmula matemática é a seguinte: IMC = peso (altura x altura).
Solução
Para solucionar o problema, é necessário realizar os seguintes passos:
1) Efetuar a leitura do peso da pessoa (quantidade de massa).
2) Ler a altura da pessoa.
3) Calcular o IMC.
4) Exibir o valor do IMC calculado.
A ordem de leitura dos dados de entrada não faz diferença no cálculo e na computação dos
dados.
Diagrama de bloco
INÍCIO
PESO; ALTURA
IMC PESO /
(ALTURA ALTURA)
IMC
FIM
Português estruturado
programa IMC
var
IMC : real
PESO, ALTURA : real
início
leia PESO
leia ALTURA
IMC ← PESO / (ALTURA * ALTURA)
escreva IMC
fim
programa CALC_IPVA
const
ALIQUOTA = 0.04
var
VALOR_VENAL : real
IPVA : real
início
leia VALOR_VENAL
IPVA ← VALOR_VENAL * ALIQUOTA
escreva IPVA
fim
Como regra geral de trabalho e de organização, as constantes com o comando const sempre
serão definidas à frente das variáveis com o comando var.
Vamos recapitular?
Neste capítulo, estudamos alguns elementos fundamentais da programação sequencial, como a defi-
nição dos tipos de dados primitivos e sua finalidade. Vimos, também, identificadores, variáveis, constantes,
operadores relacionais, expressões aritméticas e sua adequação à linguagem algorítmica, além das etapas de
processamento de um programa sequencial (entrada de dados, processamento e saída de dados).
Programação Sequencial 35
Agora é com você!
Para começar
O objetivo principal deste capítulo é o estudo dos mecanismos para que possamos efetuar uma
tomada de decisão simples, composta ou encadeada, por meio do processamento lógico. Para que
uma tomada de decisão seja efetuada pela máquina, destacamos, também, operadores lógicos e relacionais.
37
Amplie seus conhecimentos
Você sabia que o ENIAC (Electronic Numerical Integrator and Computer) é considerado o primeiro computador eletrônico
digital? Foi construído entre 1943 e 1945, entrando em operação em julho de 1946 com a finalidade de ser usado em
cálculos de balística. Para seu funcionamento havia 17.468 válvulas, 1.500 relés, além de diversos outros componentes
eletrônicos, pesando 30 toneladas! Os técnicos que o operavam trabalhavam dentro do computador.
Operador Descrição
= Igual a
<> Diferente de
Diagrama de blocos
N S
Condição
Português estruturado
se (<condição>) então
<Bloco de comandos que só será executado quando a condição for verdadeira>
fim_se
<bloco de comandos que sempre será executado>
Solução
Para este exercício, precisamos definir apenas três variáveis reais, ou seja, numero1, numero2 e
media. Após calcular e imprimir a média, devemos testar, por meio de desvio condicional sim-
ples, se a média é maior ou igual a 7.0, imprimindo a mensagem “aprovado”.
Diagrama de blocos
INÍCIO
NUMERO1, NUMERO2
(NUMERO1+NUMERO2)/2
MEDIA
N S
MEDIA >= 7
‘‘APROVADO’’
FIM
Português estruturado
programa MEDIA
var
NUMERO1, NUMERO2, MEDIA : real
início
leia NUMERO1
leia NUMERO2
MEDIA ← (NUMERO1 + NUMERO2)/ 2
escreva MEDIA
se (MEDIA > 7) então
escreva "aprovado"
fim_se
fim
Diagrama de blocos
N S
Condição
Português estruturado
se (<condição>) então
<instruções para condição verdadeira>
senão
<instruções para condição falsa>
fim_se
Exemplo
Construir um algoritmo que leia dois números reais e calcule a média aritmética desses núme-
ros. O programa deve imprimir APROVADO se a média for maior ou igual a 7.0; caso contrá-
rio, deve imprimir REPROVADO.
Solução
Para este exemplo, tomaremos como base o exemplo anterior, em que não é possível optar por
imprimir APROVADO, se a média for superior ou igual a 7, ou, do contrário, REPROVADO.
Note que devemos utilizar, aqui, o SENÃO.
INÍCIO
NUMERO1, NUMERO2
MEDIA (NUMERO1+NUMERO2)/2
MEDIA
N S
MEDIA >=7
‘‘REPROVADO’’ ‘‘APROVADO’’
FIM
Português estruturado
programa MEDIA_2
var
NUMERO1, NUMERO2, MEDIA : real
início
leia NUMERO1
leia NUMERO2
MEDIA ← (NUMERO1 + NUMERO2)/ 2
escreva MEDIA
se (MEDIA > 7) então
escreva "aprovado"
senão
escreva "reprovado"
fim_se
fim
N Condição 1 S
S N
Condição 2
Português estruturado
N Condição 1 S
S Condição 2 N
Diagrama de blocos
N
Bloco de comandos que
S
Condição 2 só será executado quando a
condição 2 for verdadeira
caso <variável>
seja <opção 1> faça
[ação para condição 1 verdadeira]
seja <opção 2> faça
[ação para condição 2 verdadeira]
seja <opção 3> faça
[ação para condição 3 verdadeira]
senão
[ação para nenhuma condição satisfeita]
fim_caso
3.4 Divisibilidade
Vamos ver agora um assunto que com certeza já é conhecido dos primeiros anos do ensino
básico, que são operações aritméticas de divisão realizadas com números naturais1. Operações aritmé-
ticas de divisão com números naturais são aquelas que possuem valor de resto com quociente inteiro.
A Figura 3.10 nos apresenta os passos da divisão do número natural 5 com o número natural 2,
que dá como resultado um quociente igual a 2 e um valor de resto igual a 1.
5 2 5 2 5 2
2 2 x
Dividendo
Divisor
5 2
5 2 5 2 -4
4 2 -4 2 2
1
1 Quociente
Resto
A Figura 3.11 nos mostra que para obter o valor do resto, devemos fazer a subtração do valor
do quociente multiplicado pelo divisor sob o valor do dividendo. Assim, devemos usar a expressão
Resto = Dividendo – Divisor ⋅ Quociente, que, computacionalmente, pode ser escrita como sendo:
Resto ← Dividendo – Divisor * (Dividendo div Divisor), onde o operador aritmético div faz o cál-
culo de divisão com quociente inteiro.
Exemplo
Desenvolver um programa de computador que leia um valor numérico inteiro e mostre men-
sagem informando se o número lido é par ou ímpar.
1
Números naturais são formados pelo conjunto de todos os números inteiros positivos, incluindo-se zero e representado pela letra
N maiúscula, sendo: N = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ... }.
Diagrama de blocos
INÍCIO
N S
RESTO = 0
‘‘VALOR ‘‘VALOR
IMPAR’’ PAR’’
FIM
Português estruturado
programa PAR_OU_IMPAR
var
N, RESTO : inteiro
início
leia N
RESTO ← N - 2 * (N div 2)
se (RESTO = 0) então
escreva "Valor par"
senão
escreva "Valor impar"
fim_se
fim
Observe o uso da expressão RESTO ← N − 2 * (N div 2), que obtém o valor de divisibili-
dade da operação de divisão entre a variável N (dividendo) e o valor 2 (divisor) gerando o valor do
RESTO da divisão entre N e 2. A instrução de tomada de decisão faz uso da condição (RESTO = 0),
que é verdadeira se o valor de RESTO for igual a zero.
Diagrama de blocos
N Condição 1 S
.e.
Condição 2
Português estruturado
fim_se
Exemplo
Construir um algoritmo que leia dois valores. Um valor representa a média aritmética; o outro, o
número total de faltas. O programa deve imprimir APROVADO se a média for maior ou igual a
7.0 e o número total de faltas for inferior a 20; caso contrário, deve mostrar REPROVADO.
programa APROVADO_REPROVADO
var
FALTAS : inteiro
MEDIA : real
início
leia FALTAS
leia MEDIA
se (MEDIA >= 7.0) .e. (FALTAS < 20) então
escreva "aprovado"
senão
escreva "reprovado"
fim_se
fim
Note que, na sentença do SE, ambas as condições devem ser verdadeiras para que seja
impresso “aprovado” na tela. Sendo assim, observe na Tabela 3.3 algumas simulações de digitação:
Tabela 3.3 - Exemplo de utilização do operador lógico .e.
Média Faltas Média >= 7.0 Faltas < 20 Condição 1 .e. Condição 2
N Condição 1 S
.ou.
Condição 2
Português estruturado
Exemplo
programa TESTE_OU
var
CODIGO: inteiro
início
leia CODIGO
se (CODIGO = 100) .ou. (CODIGO = 200) então
escreva "código válido"
senão
escreva "o código digitado está inválido"
fim_se
fim
Verdadeira Falsa
Falsa Verdadeira
Diagrama de blocos
N .não. S
condição
O operador lógico .não. inverte o resultado da condição e é muito utilizado em diversas cons-
truções.
Veja o exemplo a seguir:
Exemplo
programa TESTA_NÃO
var
NÚMERO : inteiro
início
escreva "Digite um número par: "
leia NÚMERO
RESTO ← NÚMERO – 2 * (NÚMERO div 2)
se .não. (RESTO <> 0) então
escreva "Você digitou um número par"
senão
escreva "Você não digitou um número par"
fim_se
fim
Neste exemplo, utilizamos o operador matemático mod (visto brevemente no Capítulo 2), que
retorna o resto de uma divisão inteira. No exemplo, mod é utilizado para verificar se o número digi-
tado pelo usuário é divisor de 2. Se o resto da divisão de qualquer número por 2 for igual a 0, indi-
cará que o número é par. No entanto, 0 indica falsidade; sendo assim, para que a condição de SE seja
verdadeira, a fim de imprimir corretamente a mensagem desejada, invertemos o conteúdo da variá-
vel RESTO de maneira a transformá-la em 1 (verdadeiro). Para compreender melhor, fique atento ao
próximo tópico.
Vamos recapitular?
Aprendemos, neste capítulo, como fazer a máquina tomar decisões. Esse aprendizado é de suma
importância para a compreensão dos próximos capítulos. Outro assunto abordado foram os operadores
relacionais e lógicos, que ampliam o uso das estruturas de seleção ao compor mais de uma condição na
mesma cláusula de consulta.
Para começar
Neste capítulo, vamos estudar como implementar uma técnica fundamental em programação:
laços ou repetições. Ela permite executar trechos de um programa quantas vezes for necessário. A quan-
tidade de vezes pode ser parametrizada pelo programador ou pela própria situação-problema. Vamos
explicar o laço com teste prévio de veracidade (pré-teste), o laço com texto de veracidade a posteriori
(pós-teste) e o laço incondicional.
53
Tecnicamente, costumamos chamar o laço pelo seu correspondente em língua inglesa: loop ou
looping. Os laços podem ser executados mediante uma condição predefinida pelo programador (laço
condicional) ou sem uma condição predefinida (laço incondicional).
Além disso, é importante esclarecer que, durante a execução de um laço condicional, podemos
ter operações interativas (com a intervenção do usuário) e iterativas (sem o n - sem intervenção do
usuário, apenas iteração/repetição). Os laços incondicionais permitem apenas operações iterativas.
Veremos adiante os principais comandos para controle de laço ou repetição: enquanto...faça e
repita...até que, para loops condicionais, e a instrução para...de...até...passo...faça, para loops incondicionais.
Todo algoritmo tem um custo de processamento associado. Para analisar esse custo, existe uma área, na Ciência da Compu-
tação, chamada Análise de Algoritmos e Otimização. Por meio de estudos nessa área é possível verificar, dentre outras infor-
mações, a complexidade do programa (por meios matemáticos), o seu tempo de execução, o espaço de memória necessário
e a robustez do programa. O laço tem papel importante no cálculo da complexidade de um algoritmo. Muitos laços podem
aumentar o nível de complexidade de processamento do programa, portanto use esse recurso com parcimônia.
Para saber mais, consulte: Complexidade de Algoritmos. Série Livros Didáticos Informática – UFRGS. Vol. 13, 3. ed.
Laira Vieira Toscani; Paulo A. S. Veloso. Editora Bookman, 2012.
Diagrama de blocos
Teste lógico
N
Condição
S
Instruções a
serem executadas
Exemplo
Desenvolver um programa de computador que leia um valor inteiro qualquer, divida-o suces-
sivas vezes por 2, até que o seu valor seja menor que 5, e apresente como resultado a quantidade
de sucessivas divisões realizadas.
Solução
O programa deve possuir a variável de entrada (N) e uma segunda variável que será usada para cal-
cular a quantidade de ações (divisões sucessivas) realizadas. A variável R será iniciada com valor 0
e, a cada iteração, deverá ser acrescida de 1.
R R+1
escreva R
fim
Fim
No exemplo, utilizamos a variável R para dois propósitos: guardar o resultado final a ser exi-
bido e servir de contador. O laço apresentado efetua um pré-teste antes de permitir a execução da
rotina associada à repetição: N >= 5. Isso significa que, se o valor de N for inferior a 5, as instruções
subordinadas ao laço não serão (mais) executadas.
Ao entrar no laço, o valor de N é alterado por 2 pela divisão inteira (div). Repare que o processa-
mento está sendo realizado e o valor da variável de entrada é alterado por esse processamento. Não foi
necessário criar outra variável para guardar o cálculo. Logo em seguida, e ainda dentro do laço, o valor
do contador é alterado. Isso se repetirá até que o valor de N se torne menor que 5. Quando isso aconte-
cer, o laço será interrompido e o valor do contador, apresentado como saída do programa.
Exemplo
Desenvolver um programa de computa- Solução
dor que leia as avaliações de consumidores
O programa deve possuir, além da variável
sobre determinado produto. Essas avaliações
de entrada (N), uma segunda variável (R)
são valores reais entre 5 e 10. Calcule a nota
para acumular os valores lidos e uma ter-
média atribuída ao produto. O usuário digi-
ceira variável (CONT) para contar quantos
tará quantas avaliações considerar necessário,
valores já foram lidos. É o valor lido a cada
até que o valor digitado seja inválido (menor
iteração que será acrescido à variável R.
que 5 ou maior que 10).
A variável CONT será iniciada com valor
Diagrama de blocos 0 e, a cada leitura diferente de 0, deve ser
acrescida de 1.
Início
Português estruturado
R 0
programa LACO_INTERATIVO
var
CONT 0
N, R : real
CONT : inteiro
N
início
R ← 0
N CONT ← 0
N >= 5
.e. leia N
N <= 10
enquanto (N >= 5) .e. (N <= 10) faça
S R ← R + N
R R+N CONT ← CONT + 1
leia N
fim_enquanto
CONT CONT + 1
se (CONT > 0) então
R ← R / CONT
N
escreva R
fim
S N
CONT > 0
R R / CONT
Fim
repita
<instruções executadas>
até_que (<condição - para quando verdadeiro>)
Exemplo
Desenvolver um programa de computador que apresente a soma dos cinco primeiros números
inteiros.
Solução
O programa deve possuir uma variável de processamento do cálculo (R) que será modificada
a cada interação. Também será necessária uma segunda variável (I) para controle do laço.
A variável I será iniciada com valor 1 e, a cada iteração, deverá ser acrescida de 1 até o limite
de 5. Note que este programa é semelhante ao do exemplo anterior, mas, para solucioná-lo,
será usado um laço pós-teste.
Diagrama de blocos Português estruturado
Fim
No exemplo, também utilizamos uma variável (I) que servirá como contador. Foi atribuído o
valor 1, pois usaremos a estrutura de repetição repita...até que, a qual garante ao menos uma itera-
ção. Portanto, o programa executará as instruções subordinadas ao laço ao menos uma vez.
Exemplo
Desenvolver um programa de computador que leia um número inteiro qualquer, eleve-o ao
quadrado e apresente o resultado do cálculo. Essa operação deve ocorrer até que o usuário res-
ponda SIM à pergunta “Deseja encerrar cálculo?”.
Solução
O programa deve possuir, além das variáveis de entrada (N) e de processamento do cálculo (R)
solicitado, uma terceira variável que será usada para guardar a resposta do usuário (ENCER).
A variável ENCER é iniciada com valor NÃO e, cada vez que a resposta dada for diferente de
SIM, o programa repetirá o laço, que somente será interrompido quando a resposta for SIM.
Diagrama de blocos Português estruturado
Sim
Fim
Diagrama de blocos
Var
Início, Fim,
Incremento
Instruções
Português estruturado
Atenção! Observe que não houve uso de condição para a escrita desse laço; não há, portanto,
teste lógico: verifica-se, apenas, se a variável de controle já chegou ao seu limite superior/inferior.
Para exemplificar o uso do laço incondicional, considere o exemplo seguinte:
I N + 1,
N + 10, 1
R R+I
Fim
Verifique a diferença deste exemplo em relação aos demais. Neste, desde o início o programa-
dor sabia quantas iterações seriam necessárias; ele não dependia de nenhuma condição a ser veri-
ficada a cada ciclo. A variável de controle foi iniciada com o valor N + 1 e incrementada de 1 em 1
Vamos recapitular?
Neste capítulo, estudamos o conceito de laço e as principais técnicas de loop, utilizando as seguin-
tes estruturas de repetição: enquanto...faça, para laços interativos e iterativos com pré-teste condicional
verdadeiro; repita...até que, para laços interativos e iterativos com pós-teste condicional falso; e para...
de...até...passo...faça, para laços incondicionais.
Para começar
Neste capítulo, vamos explicar a criação e o uso das variáveis compostas homogêneas (de uma e
duas dimensões, respectivamente denominadas vetores e matrizes), as quais possibilitam o armazena-
mento de mais de um valor em certa região de memória.
63
valor em uma posição de memória, mas podemos criar diversas posições de memória, cada uma
com um valor, e chamar esse conjunto de memória por meio de uma única variável.
O conjunto de dados referenciado em memória com um único nome recebe diferentes nomes
como variável indexada, variável subscrita, arranjo, vetor, matriz, tabela em memória, lista ou arrays.
Neste livro ela será chamada apenas de vetor quando se tratar de uma linha e diversas colunas e de
matriz quando tratar de diversas linhas e colunas.
As variáveis compostas homogêneas podem ter qualquer dimensão (linhas x colunas). Vetores
são comumente utilizados para estruturas de dados com uma dimensão; já matrizes são usadas para
estruturas com duas dimensões. Neste capítulo, vamos abordar apenas os vetores e as matrizes por
serem suficientes para tratar a maioria dos problemas, além de servirem de base para a criação de
estruturas com outras dimensões, como os cubos.
A criação do nome de uma variável composta segue as mesmas regras atribuídas aos nomes de
variáveis simples; o nome também é definido por meio do comando var, com auxílio do comando con-
junto. Além disso, a dimensão de uma matriz é formada por constantes inteiras e sempre positivas.
Fique de olho!
A linguagem de programação BASIC, criada em 1956, não deve ser traduzida literalmente como “básica”. O nome é, na
verdade, uma sigla para o termo: Beginner’s All-purpose Symbolic Instruction Code, que significa código de instrução sim-
bólica de uso geral para principiantes.
Em um vetor, cada posição tem um endereço conhecido como índice. Dependendo da lingua-
gem de programação utilizada, esse índice pode ser iniciado com 0 ou com 1. Para referenciar deter-
minada posição dentro do vetor, devemos utilizar o nome da variável seguido do número do índice.
Sendo assim, para o exemplo da Figura 5.1, temos vetorTeste[3] = 10.
Exemplo
Desenvolver um programa que leia cinco valores e armazene os dados em um vetor de cinco
posições. Em seguida, o programa deve imprimir o valor lido e o quadrado desse número.
Solução
Para este exercício, vamos ler cinco números inteiros e, em seguida, apresentar o vetor, impri-
mindo o valor armazenado e o seu quadrado.
Note como o vetor é criado (matriz unidimensional). No código, observe que o vetor num
(aparece no espaço de criação de variáveis) é seguido pelo comando conjunto (que indica a utiliza-
ção de matrizes) e pela indicação de sua dimensão. Sendo assim, a criação de vetores pode utilizar a
seguinte regra:
VARIÁVEL : conjunto[<dimensão>] de <tipo de dado>
Em que:
<dimensão> é a indicação dos valores inicial e final do tamanho do vetor;
<tipo de dado> é o tipo de dado utilizado (por exemplo: real, inteiro, lógico ou caractere).
Lembre-se de que um vetor pode armazenar diversos valores, mas em cada posição (índice) só
é possível armazenar um valor, assim como nas variáveis simples.
Observe ainda que, no caso do exemplo anterior, temos o cálculo do quadrado para cada uma
das cinco posições do vetor. Se precisarmos ampliar esse cálculo, basta substituir 5 pelo valor desejado.
Outro detalhe importante é com relação à variável i, aqui chamada de índice. O índice nada
mais é que uma variável do tipo inteiro que indexa o vetor. Para acessar determinada posição dentro
do vetor, devemos utilizar a variável vetor com o índice: Variável[indice].
INÍCIO
programa MANIPULACAO_INDICE
var
I 1, 10, 1
I, RESTO : inteiro
A, B : conjunto[1..10] de real
A[I]
início
para I de 1 até 10 passo 1 faça
Exemplo
Desenvolver um programa que leia cinco elementos e armazene-os em um vetor. O programa
deve imprimir na tela a média aritmética dos elementos lidos.
Solução
Utilizando o princípio de ENTRADA → PROCESSAMENTO → SAÍDA, devemos ler todos
os dados, processá-los conforme a solicitação e, por fim, executar a impressão na tela. Sendo
assim, faremos três laços, um para cada etapa. A variável i (índice) será utilizada em todas as
etapas e vai controlar o acesso ao conteúdo do vetor.
Diagrama de blocos Português estruturado
FIM
1 2 3 4
2
Índice das linhas
3 54
Exemplo
Considerar uma sala de aula com cinco alunos e uma disciplina, com três provas aplicadas
pelo professor. Construir um programa (diagrama de blocos e português estruturado) que leia
as notas dessas provas para cada aluno. Ao final, o programa deve imprimir as notas.
Solução
Neste exercício, vamos usar uma tabela semelhante à exibida anteriormente na Figura 5.5.
Índice das colunas
1 2 3
2
Índice das linhas
Diagrama de blocos
INÍCIO
I 1, 5, 1
J 1, 3, 1
NOTAS[I,J]
I 1, 5, 1
J 1, 3, 1
NOTAS[I,J]
FIM
programa MANIPULACAO_MATRIZ
var
NOTAS : conjunto[1..5, 1..3] de real
I, J : inteiro
início
Na criação de um vetor, devemos indicar apenas uma dimensão, ou seja, a quantidade de colu-
nas, já que a quantidade de linhas é sempre igual a 1. Já na criação de uma matriz de duas dimen-
sões, serão atribuídas ao comando conjunto as informações de duas dimensões, isto é, a quantidade
de linhas e de colunas. A sintaxe geral de criação é:
VARIÁVEL: conjunto[<dimensão1:dimensão2>] de <tipo de dado>
Em que:
<dimensão1> e <dimensão2> indicam a quantidade de linhas e colunas, respectivamente;
<tipo de dado> é o tipo de dado utilizado (por exemplo: real, inteiro, lógico ou caractere).
Nos vetores, devemos processar um elemento por vez; nas matrizes, o procedimento é o
mesmo, ou seja, os dados são manipulados passo a passo, um elemento de cada vez.
fim
1 2 3 4 5 6 7 8 9
vetor
b) Construa um programa em que o usuário deve ler dois vetores, A e B, cada qual com
20 elementos. O código deve montar os elementos de uma terceira matriz, C,
em que cada elemento é o produto dos elementos da matriz A com a B. Ao final,
mostre na tela os elementos das três matrizes.
c) Faça um programa que leia os elementos de dois vetores: o primeiro com dez
elementos e o segundo com 15 elementos. O usuário deve digitar os elemen-
tos dos dois vetores já em ordem crescente, ou seja, do menor para o maior.
O programa deve criar de forma automática um terceiro vetor que seja a junção
dos dois vetores lidos, mas todos os elementos devem estar em ordem crescente.
Para ilustrar, veja o exemplo a seguir:
Índice 1 2 3
Vetor A
Valor 10 12 23
Índice 1 2 3 4
Vetor B
Valor 5 15 18 25
Índice 1 2 3 4 5 6 7
Vetor C
Valor 5 10 12 15 18 23 25
2) Agora que já sabemos trabalhar com vetores, vamos exercitar o que aprendemos com
matrizes. Para cada um dos exercícios seguintes, desenvolva os diagramas de blocos e
o português estruturado:
a) Faça um programa em que o usuário deve digitar números inteiros para uma
matriz de ordem 5 (linhas e colunas iguais a 5). Após a digitação dos núme-
ros, o usuário deve digitar um número aleatório e verificar se ele se encontra na
matriz. Ao final, os índices da linha e da coluna devem ser impressos se o ele-
mento for encontrado; caso contrário, a mensagem “elemento não encontrado”
deve ser impressa.
b) Desenvolva um programa que leia duas matrizes de cinco linhas por três colu-
nas e apresente na tela a soma delas. É preciso utilizar uma terceira matriz para
armazenar o resultado.
c) Desenvolva um programa que leia duas matrizes quadradas de ordem 3. Ele
deve construir uma terceira matriz que seja o produto das duas matrizes lidas.
Para começar
Neste capítulo, vamos estudar exemplos práticos de matrizes com base em duas técnicas muito
importantes: ordenação e busca de elementos. Ao final do capítulo, será possível implementar rotinas
para ordenar dados armazenados em matrizes, bem como desenvolver algoritmos para buscar valores
armazenados em matrizes.
73
Considerando a ordenação da tabela ASCII, quando a classificação dos dados é alfabética, pri-
meiro se ordenam os caracteres maiúsculos, depois os minúsculos. Além dos caracteres convencio-
nais, esse tipo de ordenação abrange os demais elementos gráficos previstos na tabela ASCII (consulte
essa tabela no Capítulo 1).
Para classificar dados, há diversos algoritmos clássicos na Ciência da Computação. Cada um
tem seus pontos fortes e suas fragilidades. O programador precisa apenas escolher o algoritmo a ser
implementado, considerando o seu problema e, principalmente, a quantidade de dados que possui.
Dentre os algoritmos de classificação de dados mais conhecidos, destacam-se os seguintes grupos
(AZEREDO, 1996):
»» Classificação por inserção: método da inserção direta, método da inserção direta com
busca binária, método dos incrementos decrescentes - shellsort.
»» Classificação por troca: método da bolha - bubblesort, método da agitação - shakesort,
método do pente - combsort, método de partição e troca - quicksort.
»» Classificação por seleção: método da seleção direta, método da seleção em árvore - heapsort,
método de seleção em árvore amarrada - threadedheapsort.
»» Classificação por distribuição de chaves: método de indexação direta - radixsort.
»» Classificação por intercalação: método da intercalação simples - mergesort, método de
intercalação de sequências naturais.
»» Classificação por cálculo de endereços: método das listas de colisão, método da solução
postergada das colisões.
Neste capítulo, você estudará um algoritmo do tipo classificação por troca, por ser um dos
métodos mais simples, útil para ordenar um conjunto com poucos dados. Apesar de sua simplici-
dade e eficácia (atinge o objetivo de ordenar), ele não é eficiente quando comparado a outros mais
complexos, como o radixsort, o mergesort ou o quicksort. É importante ressaltar que um algoritmo é
considerado não eficiente se o seu tempo de execução for muito alto quando comparado a outros do
mesmo gênero/finalidade.
O método de classificação de dados a ser apresentado executa trocas sucessivas de valores
entre os elementos da matriz até que os dados estejam totalmente ordenados. Por esse motivo, a téc-
nica é considerada um método de classificação por troca.
Para facilitar o entendimento, imagine uma matriz de inteiros com cinco elementos, conforme
a Tabela 6.1.
Matriz: A
Índice Elemento
1 45
2 8
3 17
4 5
5 2
Solução
Deve-se utilizar uma matriz de uma dimensão (vetor) para armazenar os nomes dos estudan-
tes. Essa matriz pode ser preenchida por meio de um laço. A cada iteração o sistema receberá
um nome e alimentará o vetor. Para a ordenação, é preciso aplicar o algoritmo de troca estuda-
do, fazendo as devidas comparações, de modo que, ao final, o conjunto esteja ordenado.
I 1, 19, 1
para I de 1 até 20 passo 1 faça
leia ESTUD[I]
J 1, 20, 1 fim_para
ESTUD[I]
para I de 1 até 20 passo 1 faça
escreva ESTUD[I]
Fim
fim_para
Figura 6.1 - Diagrama de blocos para a
ordenação dos nomes dos estudantes. fim
Isso acontece porque precisamos efetuar, para cada elemento do vetor, as comparações e pos-
síveis trocas com todos os elementos posteriores (laço interno). Esse processo é repetido para cada
elemento do vetor (laço mais externo).
No primeiro laço, foi utilizada uma variável auxiliar I para controlar o elemento do vetor
usado como base de comparação. Observe que a variável I começa com o valor 1 (primeiro ele-
mento do vetor) e termina com 19 (penúltimo elemento do vetor). Isso acontece porque o último elemen-
to (de número 20) nunca será base de comparação, já que não há elemento após ele a ser comparado.
Veja, também, que a variável J começa com o valor I + 1 porque o primeiro elemento a ser
comparado será sempre o subsequente (I + 1) ao elemento utilizado como base de comparação
naquela iteração. Dessa forma, se I = 1 (estamos falando do elemento A[1]), J terá de ser igual a I + 1;
logo, compararemos A[I] com A[J] ou A[1] com A[2] e assim por diante.
Ao longo da execução do algoritmo, a variável J tomará vários valores. Verifique a Tabela 6.2.
Tabela 6.2 - Variação dos valores de I e J durante a execução do algoritmo de ordenação por troca
1 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
2 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
3 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
4 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
5 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
[...]
17 18, 19, 20
18 19, 20
19 20
O valor de J sempre será subsequente ao de I e seguirá incrementado até atingir o valor 20, que
corresponde ao índice do último elemento desse vetor de 20 posições, pois o problema considera
20 estudantes.
Ao final de cada iteração do laço mais interno (controlado por J), o fluxo do programa volta
ao laço externo (controlado por I) e acrescenta 1 ao valor de I, até chegar a 19, o que significa que o
programa alcançou sua última iteração de processamento da ordenação.
Um ponto importante a observar é o algoritmo de troca de valores (swap). Verifica-se, antes
da troca, se o elemento comparado tem valor menor que o elemento-base. Se houver, a troca é reali-
zada. Para efetuar a troca sem perder os valores, utiliza-se uma variável auxiliar (X) de mesmo tipo.
Por que o termo inglês bug, que significa inseto, é empregado para representar um erro computacional? Acredita-se que o
termo foi inicialmente usado em 1878, por Thomas Edson, quando um inseto causou problemas de leitura em seu fonó-
grafo. No entanto, foi Grace Murray Hopper (criadora da linguagem COBOL) que, em 1945, documentou um erro provo-
cado por uma mariposa que teria ficado presa entre os relés do computador que ela usava.
Índice Nomes
1 Ludmila
2 João
3 Jefferson
4 Maria Luísa
5 Silvia Amélia
6 Clodis
7 Alexandre
A Tabela 6.3 representa uma matriz unidimensional (vetor) com sete elementos do tipo string
(cadeia). Deseja-se pesquisar um dos seus elementos (efetuar uma busca). Essa busca pretende
encontrar Jefferson que, no caso, está na posição de índice 3.
A pesquisa sequencial é a mais simples e corriqueira. Verifica-se, elemento por elemento, se
o valor armazenado corresponde à chave de busca (elemento que se procura). Isso é feito até que a
pesquisa localize o elemento desejado ou chegue ao final do arquivo sem obter resultado.
No exemplo da Tabela 6.3, o valor da chave de busca (Jefferson) foi comparado com o valor
do primeiro elemento. Como eram diferentes, a comparação passou ao segundo elemento; também
diferentes. A chave de busca foi comparada, então, com o próximo elemento, cujo valor era igual ao
procurado. Assim, o algoritmo parou a pesquisa e informou que ela foi realizada com sucesso.
Exemplo
Em uma aula de Biologia, o professor deseja que a classe descubra os nomes de determinados
animais após descrever suas características. Cada grupo de alunos deve, portanto, tentar des-
cobrir o nome de um animal procurando-o no computador. Se ele não for encontrado no con-
junto de dados do programa, é porque o grupo escolheu o animal errado. Deve-se elaborar um
programa que leia e armazene dez nomes de animais, além de permitir a busca e a apresenta-
ção do nome de certo animal utilizando o algoritmo de pesquisa sequencial.
Solução
O algoritmo para este problema deve receber como entrada dez nomes de animais que possam
ser solicitados como chaves de busca. Para realizar a pesquisa sequencial, será necessário um
laço que efetue a busca enquanto o usuário desejar. Também deve ser solicitada a chave de
busca (dado a ser pesquisado), que será comparada com cada um dos dez nomes de animais
do conjunto. Além disso, o nome deve ser mostrado, quando encontrado. Caso ele não seja
localizado, é preciso informar que o nome pesquisado não existe no conjunto.
programa PESQUISA
var
ANIM : conjunto[1..10] de cadeia
I : inteiro
PESQ, RESP : cadeia
ACHA : lógico
início
RESP ← "SIM"
enquanto (RESP = "SIM") faça
escreva "Digite o nome do animal a ser pesquisado: "
leia PESQ
I ← 1
ACHA ← Falso
enquanto (I <= 10) e (ACHA = Falso) faça
se (PESQ = ANIM [I]) então
ACHA ← Verdadeiro
senão
I ← I + 1
fim_se
fim_enquanto
se (ACHA = Verdadeiro) então
escreva PESQ, " foi localizado na posição ", I
senão
escreva PESQ, " não foi localizado no conjunto de dados"
fim_se
escreva "Deseja continuar? (SIM/NÃO): "
leia RESP
fim_enquanto
fim
RESP ‘‘SIM’’
N
RESP = ‘‘SIM’’
PESQ
I 1
ACHA F
N
I <= 10 e
ACHA = F
N S
PESQ=ANIM[I]
I I+1 ACHA V
N S
ACHA = V
PESQ ‘‘foi
PESQ ‘‘não foi localizado na
localizado’’ posição’’ I
RESP
Fim
Anteriormente, foi montada a rotina de pesquisa com base em um contexto. Observe, a seguir,
o trecho que executa a pesquisa com seus comentários:
A primeira linha solicita o nome do animal que servirá de chave de busca, o qual será atribuí-
do à variável PESQ. Na segunda linha, o contador recebe o valor-base 1 para que possa atuar no
loop; ele é incrementado até 10, que é o total de elementos do conjunto de dados (vetor).
Na linha 3, a variável flag (bandeira - utilizada para indicar algo) ACHA é colocada como falsa;
ela informa se o dado procurado foi encontrado no vetor. No começo, como o dado ainda não foi
encontrado, seu valor inicial deve ser Falso. A linha 4 inicia o laço com a condição de verificar se
ainda há dados a pesquisar; para isso, utiliza a variável I, comparando-a com a quantidade máxima
de elementos (10); enquanto I <= 10 e o nome pesquisado não tiver sido encontrado (ACHA =
falso), o laço continuará a ser executado (linhas 5 a 9).
A partir da linha 5, a pesquisa é executada no vetor ANIMAIS, comparando a chave de busca
(valor da variável PESQ) com cada elemento desse vetor. Se o nome procurado estiver na posição
I do vetor, a flag ACHA será alterada para Verdadeiro; caso contrário, a variável de controle I será
incrementada apenas para a próxima iteração. Isso acontecerá até que I atinja o valor 10 ou a flag
tenha valor verdadeiro.
Ao final do laço por um dos motivos mencionados, nas linhas 11 a 15 verificaremos o valor da
flag. Se for Verdadeiro, significa que o nome do animal pesquisado existe no conjunto de dados; do
contrário, será informado que o nome pesquisado não consta no conjunto de dados.
Atente para a importância de uma variável flag como ACHA. No começo da execução do algo-
ritmo, a bandeira fica “abaixada”, ou seja, indica falsidade. Quando o nome pesquisado é encontrado,
ela passa a ter valor verdadeiro, como se tivesse sido “levantada”.
Neste capítulo, estudamos algumas operações práticas para ordenação e pesquisa de elementos em
matrizes. Com esse conhecimento você pode escrever algoritmos mais elaborados no paradigma de pro-
gramação sequencial!
Para começar
Este capítulo mostra como efetuar o trabalho de estruturação de dados com base na criação de
tipos de dados derivados. Assim sendo, serão apresentados os detalhes para definição, implementação e
uso de registros e definição de novos tipos de dados com base nos dados básicos existentes.
85
Um tipo de dados derivado, após a sua criação, será visto por todo o projeto e deve ser defi-
nido antes da criação de uma variável.
Para a criação de novos tipos com base nos tipos primitivos, deve-se seguir a seguinte sintaxe:
tipo
<identificador> = <tipo de dado primitivo>
Em que:
» Identificador: é a definição do nome do novo tipo a ser utilizado no sistema.
» Tipo de dado primitivo: é um dos tipos de dados primitivos já existentes.
Partindo da definição de um novo tipo de dados, é possível criar uma variável baseada no tipo
de dados que acaba de ser criado.
Por exemplo:
tipo
NOTA = real
var
NOTA_PROVA : NOTA
Veja que nota_prova é uma variável definida com base no tipo NOTA.
Você sabia que o sítio The Language List apresenta informações básicas de 2500 linguagens de programação. Para saber
mais, consulte o endereço Web: <http://people.ku.edu/~nkinners/LangList/Extras/langlist.htm>. Acesso em: 21 nov. 2013.
fim_registro
Ficha acadêmica
Dados pessoais
Nome:
Nome do registro
Vale lembrar que o correto preenchimento do quadro de definição de registros facilita a cons-
trução de todo algoritmo que processa os dados. Essa documentação dever estar acessível a todos
aqueles da equipe de desenvolvimento que estão envolvidos na criação do projeto.
Com base na estrutura definida na Tabela 7.2, tem-se a codificação do seguinte português
estruturado:
tipo
FICHA_ACADEMICA = registro
NOME : cadeia
SERIE : inteiro
TURMA : caractere
DISCIPLINA : cadeia
FALTA1 : inteiro
NOTA1 : real
FALTA2 : inteiro
NOTA2 : real
FALTA3 : inteiro
NOTA3 : real
FALTA4 : inteiro
NOTA4 : real
fim_registro
var
aluno : ficha_academica
Foi definido, primeiramente, o tipo de dados, isto é, a FICHA_ACADEMICA, para depois criar a variável aluno, definida
como do tipo FICHA_ACADEMICA. Sendo assim, aluno seria uma variável como outra qualquer, se não fosse por uma
diferença: é criada para um projeto específico; por esse motivo que recebe o nome de variável composta heterogênea.
Observe que, dessa forma, podemos guardar em uma única estrutura vários tipos diferentes de dados.
Em todas as operações (leitura e escrita) que manipula, a variável aluno, é necessário utilizar-
mos um operador de escopo, já que ela é composta por diversos campos. A sintaxe geral de acesso é:
[nome da variável de registro].[nome do campo]
Observe o exemplo:
Para acessar o campo NOME do registro FICHA_ACADEMICA, utilizaremos:
ALUNO.NOME
Ficha acadêmica
Dados pessoais
Nome:
1 2 3 4 1 2 3 4
Nota Falta
A documentação também deve ser ajustada para a nova configuração, pois temos, agora, a
estrutura de dois vetores dentro da ficha acadêmica. Observe a documentação da estrutura proposta
na Tabela 7.3 e sua descrição em português estruturado.
FICHA_ACADEMICA
nome cadeia
série inteiro
turma caractere
disciplina cadeia
tipo
FICHA_ACADEMICA = registro
NOME : cadeia
SERIE : inteiro
TURMA : caractere
DISCIPLINA : cadeia
fim_registro
var
aluno : ficha_academica
Note que a definição para a criação da variável não sofreu ajuste; houve alteração apenas na
estrutura do registro.
BIMESTRE
FICHA_ACADEMICA
nome cadeia
série inteiro
turma caractere
disciplina cadeia
boletim bimestre
O português estruturado para a estrutura criada na Tabela 7.5 pode ser observado a seguir:
tipo
BIMESTRE = registro
fim_registro
FICHA_ACADEMICA = registro
NOME : cadeia
SERIE : inteiro
TURMA : caractere
DISCIPLINA : cadeia
BOLETIM : bimestre
fim_registro
var
ALUNO : ficha_academica
Após o uso do comando var, é indicada a variável de registro aluno, formada por um conjunto
(vetor) de cinco registros do tipo de dado derivado FICHA_ACADEMICA.
Solução
Uma vez definida a estrutura de dados que
comportará os dados, deve-se efetuar a leitu- 1
ra de todos os dados e, em seguida (na fase de
processamento), calcular e imprimir a soma
i 1, 5, 1
de todas as faltas e as médias de cada aluno.
Diagrama de blocos
total_faltas 0
soma_notas 0
INÍCIO
I 1, 5, 1 j 1, 4, 1
ALUNO[I].SERIE
media soma_notas/4
ALUNO[I].TURMA
aluno[i].nome
ALUNO[I].DISCIPLINA
aluno[i].serie
J 1, 4, 1 aluno[i].turma
aluno[i].disciplina
ALUNO[I].BOLETIM.FALTA[J]
total_faltas
ALUNO[I].BOLETIM.NOTA[J]
media
1 Fim
Figura 7.3a - Trecho responsável pela Figura 7.3b - Trecho responsável pelo
entrada de dados dos registros. processamento e pela saída de dados.
programa MEDIA_SALA_AULA
tipo
BIMESTRE = registro
FALTA : conjunto[1..4] de inteiro
NOTA : conjunto[1..4] de real
fim_registro
FICHA_ACADEMICA = registro
NOME : cadeia
SERIE : inteiro
TURMA : caractere
DISCIPLINA : cadeia
BOLETIM : bimestre
fim_registro
var
ALUNO : conjunto[1..5] de ficha_academica
TOTAL_FALTAS : inteiro
SOMA_NOTAS : real
MEDIA : real
início
para I de 1 até 5 passo 1 faça
leia ALUNO[I].NOME
leia ALUNO[I].SERIE
leia ALUNO[I].TURMA
leia ALUNO[I].DISCIPLINA
escreva ALUNO[I].NOME
escreva ALUNO[I].SERIE
escreva ALUNO[I].TURMA
escreva ALUNO[I].DISCIPLINA
escreva TOTAL_FALTAS
escreva MEDIA
fim_para
fim
Aprendemos neste capítulo como criar e definir uma variável heterogênea, ou seja, um registro.
Vimos, também, como é possível trabalhar com vetores/matrizes com base em um registro.
Ficha cadastral
Dados
Nome:
Ano de
Série: Turma: Sexo: M F
nascimento:
Média: Aprovado: Sim Não
Distribuidor
DIS_Zona Cadeia Divisão de uma cidade. Ex.: Norte, Oeste, Leste e Sul
Para começar
Neste capítulo você aprenderá a utilizar sub-rotinas e técnicas de programação mais avançadas,
como a técnica conhecida por “dividir para conquistar”. Com esse conhecimento, você poderá produzir
códigos-fonte mais legíveis, fáceis de fazer manutenção e essencialmente mais modularizados. Com o uso
adequado das técnicas avançadas de programação, você desenvolverá programas ainda mais profissionais.
97
1) Dividir: separar o problema em dois ou mais subproblemas menores similares ao original,
pois solucionar vários pequenos problemas, em vez de um grande, é, do ponto de vista
computacional, supostamente mais fácil;
2) Conquistar: resolver cada subproblema em separado utilizando o próprio algoritmo que
está sendo projetado;
3) Combinar: obter uma solução para o problema original a partir da combinação das solu-
ções dos subproblemas.
Cada um desses procedimentos pode ser implementado de modo diferente, de acordo com
o algoritmo que projetamos e com as necessidades do nosso problema. Por exemplo, para dividir o
problema original, podemos usar divisão binária (dividir o problema em dois subproblemas) ou
divisão n-ária (dividir o problema em n partes), conforme mostra a Figura 8.1.
Problema
original
Divisão
Solução
do problema
original
Note que esse é um problema simples que poderíamos resolver de uma forma igualmente sim-
ples, pela força bruta, sem uso de técnicas sofisticadas. Inclusive, não há ganho de eficiência se resol-
vermos esse algoritmo por meio de um único laço, considerando de uma só vez todo o conjunto de
dados. Isso mostra que nem todos os problemas são melhorados por meio da técnica de divisão e
conquista. Essa premissa é verdadeira até mesmo para outras técnicas de programação. Eis, então,
a responsabilidade do programador em escolher a técnica mais adequada para o seu problema.
Então, quando devo aplicar a técnica de divisão e conquista? Basicamente quando:
»» o seu problema puder ser particionado em problemas similares menores, o que chama-
mos de resolução por indução;
»» os subproblemas gerados possam ser solucionados independentemente uns dos outros;
»» for possível combinar as soluções dos subproblemas de modo eficiente.
Vejamos agora outro exemplo, um pouco mais complexo. Digamos que desejamos efetuar uma
busca em um vetor ordenado.
O modo mais trivial de solucionar esse problema é por meio da pesquisa sequencial, estudada no
Capítulo 6. Todavia, em um conjunto de dados muito grande, a pesquisa sequencial é pouco eficiente.
Nesse caso, a divisão e conquista passam a ser uma estratégia interessante se o conjunto de dados esti-
ver ordenado. Um dos algoritmos de busca por divisão e conquista mais utilizados é a pesquisa binária.
A pesquisa binária consiste em dividir o conjunto de dados (com n elementos) em duas partes,
por isso o adjetivo “binário”. Logo, o problema também é reduzido a dois subproblemas menores. Se
o conjunto tiver apenas um valor (n = 1) e o elemento procurado for a1, então o valor procurado foi
encontrado; caso contrário, esse valor não está no conjunto.
Se n > 1, então verificaremos se a chave de busca está na posição do meio (n/2), ou seja, se
k = an/2. Se estiver, o valor procurado foi encontrado; caso contrário, será necessário continuar a
busca. Como o conjunto está ordenado, é simples saber se o valor procurado, caso exista, está na pri-
meira ou na segunda parte do conjunto.
Utilização de Sub-rotinas 99
Dividimos, portanto, o problema em dois a depender do valor da chave de busca:
»» busca binária nos elementos a1 até a(n/2)-1 do vetor;
»» busca binária nos elementos a(n/2)+1 até an do vetor.
O processo de divisão e conquista se repete até que, então, o valor procurado seja encontrado
ou descubra-se que o valor não está no vetor. Note que o processo de divisão é realizado quantas
vezes for necessário.
Perceba também que nesse algoritmo, os processos de dividir e de conquistar são iterativos e
incrementais, ou seja, ocorrem várias vezes, e a cada vez que ocorrem, o algoritmo está mais pró-
ximo de encontrar a chave de busca1 (ou de informar que ela não consta no conjunto de dados).
Note que esse método é semelhante ao que realizamos ao procurar uma palavra (nossa chave
de busca) em um dicionário.
Vejamos um exemplo da aplicação desse algoritmo. Encontrar o número 4 no vetor ordenado
V = {1, 3, 4, 9, 10, 11, 15}. Antes de resolver, saiba que o vetor V possui 7 elementos, logo n = 7.
A primeira decisão é aplicada: 4 está na posição n/2, ou seja, na posição 3? Não, pois nessa
posição está o valor 9. Então, dividiremos o vetor em dois subconjuntos:
»» valores abaixo da posição 3: V1 = {1, 3, 4};
»» valores acima da posição 3: V2 = {10, 11, 15}.
Como 4 é menor que 9, então o conjunto representado pelo vetor V2 será descartado e, mais
uma vez, o algoritmo de busca binária será executado, dessa vez sobre o vetor V1.
Neste subproblema, a primeira decisão será similar à utilizada no problema original: 4 é menor
que o elemento do meio? A reposta é não. O elemento do meio (posição 1) é 3. Então, o vetor será
novamente dividido em dois novos subconjuntos:
»» valor abaixo da posição 1: V11 = {1};
»» valor acima da posição 1: V12 = {4}.
INÍCIO
programa BUSCA_BINARIA
var
V : conjunto[1..20] de inteiro
I 1, 20, 1
X, N, MEIO, ESQUERDA, DIREITA, I: inteiro
início
leia X
N
ESQUERDA <= DIREITA enquanto (ESQUERDA <= DIREITA) faça
MEIO ← (ESQUERDA + DIREITA) DIV 2
ESQUERDA ← MEIO + 1
N S MEIO
V [MEIO] < X
senão
DIREITA ← MEIO - 1
DIREITA MEIO - 1 ESQUERDA MEIO + 1
fim_se
fim_se
fim_enquanto
fim
FIM
8.3 Procedimentos
O programador deve sempre se lembrar de manter a legibilidade dos algoritmos e códigos-
-fonte dos seus programas. Para isso, é preciso utilizar-se de vários mecanismos. Um deles é a modu-
larização do programa. Ao criar módulos de programas, o programador permite estabelecer ações
pontuais para cada porção de código, deixando o programa mais limpo e, consequentemente, mais
fácil de efetuar manutenção.
Atualmente, há diversas formas de modularização. Neste capítulo, estudaremos duas formas:
procedimentos e funções.
Procedimentos são subprogramas (também conhecidas por sub-rotinas), ou seja, partes de um
programa maior, com finalidade específica. Os procedimentos executam uma determinada tarefa com
uso ou não de parâmetros de entrada, mas jamais retornam valores ao programa que o chamou. Os
parâmetros são valores enviados pelo programa principal para correta execução dos procedimentos.
Todo procedimento deve ter uma assinatura. A assinatura é composta pelo identificador do
procedimento e seus parâmetros de entrada, caso tenha. Também pode ter espaço para declaração de
variáveis. Essas variáveis só poderão ser utilizadas dentro do procedimento onde foram declaradas.
fim_procedimento
Nesse exemplo, a função Media_Anual será executada ao programa tentar validar a condição
SALARIO > Media_Anual (VETOR, N). Para efetuar a comparação do salário com a média anual,
será necessário executar a função Media_Anual. Essa função recebe o vetor com os salários de todos
os meses como parâmetro de entrada e, ao final, retorna como parâmetro de saída o valor médio dos
salários recebidos durante o ano.
Toda função deve ter uma assinatura. A assinatura é composta pelos parâmetros de saída, o iden-
tificador da função e pelos parâmetros de entrada, caso tenha. No corpo da função pode ter espaço
para declaração de variáveis, que só poderão ser utilizadas dentro da função onde foram declaradas.
Em português estruturado, podemos definir uma função da seguinte forma:
tipo_retorno Função <nome da função>(<lista de parâmetros>)
<comandos>
fim_funcao
retorne ACUMULADOR / 12
fim_função
Você sabia que o conceito de linguagem de programação advém da linguística? Os linguistas tentaram definir precisa-
mente quais eram as sentenças válidas descritas em uma determinada linguagem, bem como definiram regras de escrita.
O mesmo ocorre com as linguagens de programação. Muitos dos códigos-fonte escritos em linguagens de programação
passam por analisadores baseados em processos linguísticos de análise léxica, sintática e semântica. Para saber mais,
consulte Price e Toscani (2008).
retorne ACUMULADOR / 12
fim_funcao
fim
escreva N
fim
Nesse exemplo, considerando que foi lido o valor N = 10, será apresentada na tela a
seguinte saída:
10
100
10
Nesse exemplo, considerando que foi lido o valor N = 10, será apresentada na tela a
seguinte saída:
10
100
100
O último valor refere-se à variável N, que, por ter sido passada por referência à função
Cálculo, foi alterado dentro dessa função, diferente do que ocorreu no exemplo em que N foi pas-
sada por valor. Veja, também, que para passar valor por referência a uma função ou procedimento é
necessário colocar a palavra “var” antes do nome do parâmetro na assinatura da sub-rotina.
Vamos recapitular?
1) Escreva uma sub-rotina que receba por parâmetro um número inteiro e imprima se
ele é divisível por 7 ou não. Decida se usará procedimento ou função e explique o
porquê da escolha.
2) Escreva um procedimento que receba por parâmetro o código do grupo ao qual um
determinado animal pertence e, em seguida, imprima o nome do grupo e a constante
K para cálculo da taxa metabólica basal (TMB), conforme tabela a seguir:
1 Passeriformes 129
2 Não passeriformes 78
3 Mamíferos Placentários 70
4 Marsupiais e Edentatas 49
5 Répteis 10
3) Escreva uma função que receba por parâmetro o peso metabólico (PM) de um ani-
mal (peso total do animal expresso em quilogramas) e o grupo ao qual esse animal
pertence e calcule a taxa metabólica basal (TMB). A taxa metabólica basal é o peso
metabólico elevado a 0,75 e multiplicado por uma constante K. O valor de K depen-
de do grupo do animal, conforme tabela do exercício anterior.
Fórmula de cálculo da taxa metabólica basal: TMB = (PM ↑ 0,75) * K
4) Escreva um procedimento que receba por parâmetro o peso metabólico (PM) de um
animal (peso total do animal expresso em quilogramas), o grupo ao qual esse animal
pertence e uma letra (flag). Se a letra for B, o procedimento deve chamar uma função
para calcular a taxa metabólica basal (TMB). Se a letra for E, o procedimento deve
chamar uma função para calcular a taxa metabólica específica (TME). Para o cálculo
da TME, usar a fórmula a seguir. O valor da constante K corresponde ao grupo do
animal, sendo o mesmo tanto para o cálculo da TMB como da TME.
Fórmula de cálculo da taxa metabólica específica: TME = (PM ↑ 0,25) * K
Para começar
Neste capítulo, serão abordados de maneira introdutória e mais didática possível conceitos preli-
minares do uso de medidas de complexidade de algoritmos para estudantes iniciantes em programação
de computadores.
109
A disciplina de Análise de Algoritmos tem por objetivo estudar os problemas computacionais
que se apresentam de forma recorrente, que se mostram de diferentes formas (FEOFILOFF, 2013).
Nesse sentido, podemos entender que essa disciplina visa, para um dado problema, nos mostrar:
»» a prova de que o algoritmo que estamos usando está correto.
»» a estimativa da complexidade do tempo que a execução do algoritmo consome.
»» a estimativa do espaço de memória usada para seu armazenamento.
Veja que, com base no exposto fica fácil concordar com as palavras de Almeida (2000, p. 3)
que nos diz que um “algoritmo não é a solução de um problema, pois, se assim fosse, cada problema
teria um único algoritmo”. Note que poderão existir para a solução de um problema vários cami-
nhos, vários algoritmos. Assim sendo, um problema poderá ser resolvidos com o uso de vários algo-
ritmos (PRESTES, 2011), Almeida (2000, p. 3) acrescenta ainda que “algoritmo é um caminho para
a solução de um problema, e em geral, os caminhos que levam a uma solução são muitas”. Análise de
Algoritmos é a forma com a qual poderemos medir qual algoritmo é o melhor para responder a certo
problema, pois como afirma Prestes (2011) “o fato de um algoritmo resolver um dado problema não
significa que seja aceitável na prática”.
Se tivermos dois algoritmos para a solução de um mesmo problema, a ação prática da Análise
de Algoritmos nos permitirá decidir dos dois algoritmos fornecidos, aquele que seja melhor eficiente
(otimilidade de algoritmos). É fundamental sabermos que eficiência é a forma como um algoritmo
realiza sua tarefa até atingir seu objetivo e que eficácia é o objetivo em si. Os algoritmos devem ser
eficazes, devem atender seus objetivos, devem fornecer uma solução de boa qualidade a certo pro-
blema. Assim, a eficiência é o grau de satisfação de boa qualidade medido pela Análise de Algoritmos
para saber qual dos algoritmos encontrados é o melhor para solucionar o problema existente.
Para analisar a eficiência no uso de certo algoritmo, é necessário levar em consideração a exis-
tência de duas possibilidades de análise, como mostra Matos (2008, p. 13):
»» Pormenorizada: mais exata e direta, em geral menos útil, pois:
é expressa em segundos;
resultado da avaliação da eficiência (por exemplo, tempo gasto): único e dependente da
velocidade e características do processador.
»» Por meio de ordens de grandeza: ignora as constantes multiplicativas e é uma análise
assintótica (método para medir o tempo total gasto por um algoritmo para realizar certa
tarefa computacional), isto é:
expressa em ordens de grandeza;
resultado da avaliação da eficiência: paramétrico (uma função do comprimento dos
dados) e independente da velocidade e características do processador.
Com base nessa explicação, podemos dizer que a Análise de Algoritmos é uma disciplina de
Engenharia, de Matemática ou da Ciência da Computação, dependendo do enfoque que queira se
dar, que procura prever o comportamento de um algoritmo antes mesmo que seja implementado
O estudo de análise e complexidade de algoritmos pode ser ampliado com base na consulta de diversos outros materiais
publicados na grande rede mundial (Internet), em português, a partir dos seguintes sítios (acesso em: 19 dez. 2013):
http://www.inf.ufrgs.br/~prestes/site/Welcome.html (slides);
Exemplo
Como exemplo de métrica de tempo de execução (a mais comum) em nível abstrato consi-
dere o uso dos algoritmos de pesquisa sequencial e binária para localizar em um vetor A de
1000 posições classificados em ordem crescente determinado valor pesquisado. Os valores do
vetor A são compostos por números reais múltiplos de 10 iniciando-se em 10. Assim sendo,
A[1] = 10, A[2] = 20, A[3] = 30, A[4] = 40, ..., A[1000] = 10000.
RESP ''SIM''
N
RESP = ''SIM''
PESQ
COMEÇO 1
FINAL 1000
ACHA .F.
N S
PESQ = A [MEIO]
ACHA .V.
N S
PESQ < A [MEIO]
N S
ACHA = .V.
RESP
programa PESQUISA_BINÁRIA
var
A : conjunto[1..1000] de real
I, J, COMEÇO, FINAL, MEIO : inteiro
RESP : cadeia
PESQ : real
ACHA : lógico
Início
A[1] ← 10
RESP ← "SIM"
enquanto (RESP = "SIM") faça
escreva "Entre valor a ser pesquisado: "
leia PESQ
COMEÇO ← 1
FINAL ← 1000
ACHA ← .Falso.
enquanto (COMEÇO <= FINAL) .e. (ACHA = .Falso.) faça
MEIO ← (COMEÇO + FINAL) div 2
se (PESQ = A[MEIO]) então
ACHA ← .Verdadeiro.
senão
se (PESQ < A[MEIO]) então
FINAL ← MEIO - 1
senão
COMEÇO ← MEIO + 1
fim_se
fim_se
fim_enquanto
se (ACHA = .Verdadeiro.) então
escreva PESQ, " foi localizado na posição ", MEIO
senão
escreva PESQ, " não foi localizado"
fim_se
escreva "Deseja continuar? (SIM/NÃO): "
leia RESP
fim_enquanto
fim
S I ← I + 1
I I+1 fim_enquanto
TAMANHO ← I - 1
fim
Tamanho I-1
Retorna tamanho
A partir da definição da função tamanho() para a linguagem português estruturado fica fácil
implementar um programa que localize certa janela de caracteres em uma sentença definida pelo
método de força bruta.
Exemplo
Como exemplo de busca de padrões em uma sequência de caracteres considere um programa
que efetue a busca de uma janela em uma sentença pelo método da força bruta e retorne como
resultado de sua operação as posições iniciais onde os padrões de caracteres ocorrem na sen-
tença. A sentença será “YCTABWATAB” e a janela a ser pesquisada será “AB”.
Observe a seguir a disposição do conteúdo da sentença em cada uma das posições do vetor
que faz parte com certa sequência de caracteres.
Sentença
1 2 3 4 5 6 7 8 9 10
Y C T A B W A T A B
Janela
A B
FRASE ← "YCTABWATAB"
I 1, N - M + 1, 1 JANELA ← "AB"
J O N ← tamanho(FRASE)
M ← tamanho(JANELA)
J<M
.E. N
para I de 1 até N – M + 1 passo 1 faça
FRASE [I + J] = JANELA [I + 1]
J ← 0
enquanto (J < M) .e. (FRASE[I + J] = JANELA
[I + 1]) faça
J J+1
J ← J + 1
fim_enquanto
N S
J=M
se (J = M) então
I escreva I
fim_se
fim_para
FIM
fim
Figura 9.3 - Diagrama para busca
de padrões de caracteres.
Exemplo
Como exemplo de recursividade considere uma função que calcule o valor do fatorial de um
número inteiro qualquer N.
Diagrama de blocos
N S
LIMITE N <= 1
programa CALC_FAT
var
LIMITE : inteiro
início
escreva "Qual fatorial: " leia LIMITE
escreva FATORIAL(LIMITE)
fim
FATORIAL (N)
Processamento de execuções recursivas.
5!
Ação de desempilhamento.
24 * 5 120
Ação de empilhamento.
4! * 5
FATORIAL FATORIAL (N - 1) * N
3! * 4 6*4 24
FATORIAL FATORIAL (N - 1) * N
2! * 3 2*3 6
FATORIAL FATORIAL (N - 1) * N
1! * 2 1*2 2
FATORIAL FATORIAL (N - 1) * N
0! * 1 1*1 1
Sobre ações de uso de backtraking pesquise os algoritmos relacionados aos problemas: da mochila, do labirinto, das oito
rainhas e do passeio do cavalo.
Vamos recapitular?
ALCALDE, E.; GARCIA, M.; PENUELAS, S. Informática básica. São Paulo: Makron, 1991.
ALMEIDA, C. Técnicas de Linguagem de Programação. Portugal: Escola Secundária de Emídio
Navarro, 2000. Disponível em: <http://www.esenviseu.net/Recursos/Recursos.ASP?CodId=59>.
Acesso em: 19 dez. 2013.
ALVAREZ, B.; ESMERALDA, M. Organização, Sistemas e Métodos. São Paulo: Makron, 1991.
AZEREDO, P. A. Métodos de Classificação de Dados e Análise de suas Complexidades. Rio de
Janeiro: Campus, 1996.
BATISTA, L. Elementos de Programação. São Paulo: Edgard Blücher, 1983.
BELLIS, M. Fortran: The First Successful High Level Programming Language. About.com Inven-
tors, Disponível em: <http://inventors.about.com/od/computersoftware/a/Fortran.htm>. Acesso em:
12 nov. 2013.
BERG, A. C.; FIGUEIRÓ, J. P. Lógica de Programação. Editora ULBRA, Rio Grande do Sul: Editora
ULBRA, 1998.
BIO. Ada Augusta. Bio. True History. Disponível em: <http://www.biography.com/people/ada-love-
lace-20825323>. Acesso em: 12 nov. 2013
BOUTE, R. T. The Euclidean definition of the functions div and mod. ACM Transactions on Pro-
gramming Languages and Systems (TOPLAS). v. 14, p. 127-144, 2 abr. 1992.
CAINE, S.; GORDON, K. PDL: A Tool for Software Design. Proc. National Computer Conference,
AFIPS Press, 1975.
CHAPIN, N. A New Format for Flowcharts. Software - Practice and Experience, v. 4, n. 4, 1974.
CLAUDINO, R. Análise de Algoritmos. Minas Gerais: Universidade Federal de Itajubá, 2013. Dis-
ponível em: < https://sites.google.com/site/analgorit/ccf140-12>. Acesso em: 19 dez. 2013.
CORMEN, T. H.; LEISERSON, C. E.; RIVEST, R. L.; STEIN, C. Algoritmos: teoria e prática. Tradu-
ção da segunda edição americana. Tradutor: Vandenberg D. de Souza. Rio de Janeiro: Elsevier, 2002.
DAHL, O. J.; et al. Structured Programming. Academic Press, 1972.
DIVERIO, T. A.; MENEZES, P. B. Teoria da computação: máquinas universais e computabilidade.
3. ed. Porto Alegre: Bookman, 2011. v. 5. (Série Livros Didáticos - Instituto de Informática UFRGS).
FARRER, H. Algoritmos Estruturados. Guanabara, 1986.
FEOFILOFF, P. Algoritmos em linguagem C. Rio de Janeiro: Elsevier, 2008.
FEOFILOFF, P. Minicurso de Análise de Algoritmos. São Paulo: Universidade de São Paulo, 2013. Dis-
ponível em: <http://www.ime.usp.br/~pf/livrinho-AA/AA-BOOKLET.pdf>. Acesso em: 19 dez. 2013.
Bibliografia 125
FORBELLONE, A. L. V.; EBERSPÄCHER, H. F. Lógica de programação: A construção de Algorit-
mos e Estruturas de Dados. São Paulo: Makron, 2000.
GUIMARÃES, A. M.; LAGES, N. A. C. Algoritmos e Estruturas de Dados. Rio de Janeiro: LTC, 1994.
HOUAISS, A. Dicionário Eletrônico Houaiss da Língua Portuguesa: Versão 2.0a. Brasil: Instituto
Antônio Houaiss/Editora Objetiva, 2007.
KNUTH, D. E. The Art of Computer Programming: Fundamental Algorithms. Massachusetts:
Addison-Wesley, vol 1, 1972.
LEIJEN, D. Division and Modulus for Computer Scientists. 2001. Disponível em: <http://research.
microsoft.com/en-us/um/people/daan/download/papers/divmodnote.pdf>. Acesso em: 6 dez. 2013.
LOUREIRO, A. A. F. Algoritmos e Estruturas de Dados II – Introdução. Belo Horizonte: Univer-
sidade Federal de Minas Gerais, 2005. Disponível em: <http://homepages.dcc.ufmg.br/~loureiro/
alg/052/>. Acesso em: 19 dez. 2013.
MANZANO, J. A. N. G.; FIGUEIREDO, J. Algoritmos: Lógica para Desenvolvimento de Programa-
ção de Computadores. São Paulo: Érica, 2012.
MARTIN, J. Técnicas Estruturadas e Case. São Paulo: Makron, 1991.
MATOS, A. Tópicos Avançados de Algoritmos. Portugal: Universidade do Porto, 2008. Disponível
em: <http://www.dcc.fc.up.pt/~ap/taa/1011/>. Acesso em: 18 dez. 2013.
MOREIRA, G de A. Algoritmos para Busca de Padrões: uma análise empírica. São Paulo: Facul-
dade de Tecnologia de São Paulo, 2012. Disponível em: <http://www.fatecsp.br/dti/tcc/tcc00058.
pdf>. Acesso em: 22 de dez. de 2013.
NASSI, I.; SHNEIDERMAN, B. Flowchart Techiques for Structured Programming. SIGPLAN
Notices, ACM, 1973.
PACIEVITCH, Y. Lógica de Programação. Nov. 2013. Disponível em: <http://www.infoescola.com/
informatica/logica-de-programacao/>. Acesso em: 11 nov. 2013.
POTROS, J. de P. Medida do Tempo de Execução. Minas Gerais: IF Sudeste de MG, 2013. Disponí-
vel em: <https://sistemas.riopomba.ifsudestemg.edu.br/dcc/index.php?arquivo=disciplina_materiais.
php&codigo_disciplina=166&codigo_professor=6593>. Acesso em: 20 dez. 2013.
PRESSMAN, R. S. Engenharia de Software. São Paulo: Makron, 1995.
PRESTES, E. Complexidade de Algoritmos. Rio Grande do Sul: Universidade Federal do Rio
Grande do Sul, 2011. Disponível em: <http://www.inf.ufrgs.br/~prestes/Courses/Complexity/>
Acesso em: 18 dez. 2013.
PRICE, A. M. A.; TOSCANI, S. S. Implementação de Linguagens de Programação: compiladores.
3. ed. Porto Alegre: Bookman, 2008.
PRINCETON. ALGOL. Princeton University. Disponível em: <http://www.princeton.edu/~achaney/
tmve/wiki100k/docs/ALGOL.html>. Acesso em: 12 nov. 2013
Bibliografia 127
Marcas Registradas
Todos os nomes registrados, marcas registradas ou direitos de uso citados neste livro pertencem aos
seus respectivos proprietários.