CAPÍTULO 1 INTRODUÇÃO À LÓGICA Fabio Augusto Oliveira Guilherme da Cunha Fonseca FEPI – Centro Universitário de Itajubá Curso de Engenharia de Produção 1 INTRODUÇÃO Um programa de computador é essencialmente composto por cálculos e decisões lógicas. Com os cálculos já estamos acostumados: são contas de soma, subtração, multiplicação, dentre tantas outras. Mas e a parte das decisões lógicas? As decisões lógicas são aquelas que coordenam a execução dos cálculos, para que, em conjunto, eles produzam o resultado para um problema maior. São as decisões lógicas que definem se um cliente tem ou não desconto em sua compra, ou se um aluno foi ou não aprovado em seu curso. Dada a importância da lógica neste processo, pode-se dizer que programar um computador é, em grande parte, um exercício de lógica. Esta nota de aula apresentará o conceito de lógica e como usá-la para construir algoritmos, isto é, coordenar a execução de cálculos para resolver um problema maior. 2 O QUE É LÓGICA? Ainda que não percebamos, a lógica está presente em nosso dia-a-dia. Quando o professor diz ao aluno que ele precisa ter frequência de pelo menos 75% E média de Programação Computacional – Notas de Aula – Capítulo 01 – 2 pelo menos 6,0 para ser aprovado, o aluno sabe que não adianta atender aos 75% de frequência se não tiver média 6,0; o contrário também vale: de nada adianta ter 10,0 de média se a frequência for inferior a 75%. Caso o professor tivesse dito que basta o aluno ter frequência acima de 75% OU média de pelo menos 6,0 para ser aprovado, a história seria completamente diferente, não é mesmo? A diferença de interpretação destas duas colocações está, exatamente, na lógica apresentada por elas. Repare nas pequenas partículas em negrito, E e OU. São elas que fazem toda a diferença. 3 ALGORITMOS Algoritmo é a forma organizada de expressar uma sequência de passos que visam atingir um objetivo definido. Algoritmo é a lógica necessária para o desenvolvimento de um programa. Apesar do nome estranho, os algoritmos são muito comuns no nosso cotidiano, como por exemplo, em uma receita de bolo. Nela estão escritos os ingredientes necessários e a sequências de passos ou ações a serem cumpridos para que se consiga fazer um determinado tipo de bolo. Em um modo geral, um algoritmo segue um determinado padrão de comportamento, com objetivo de alcançar a solução de um problema. Padrão de comportamento: imagine a sequência de números: 1, 6, 11, 16, 21, 26,..., para determinar qual será o sétimo elemento dessa série, necessitamos descobrir qual é a sua regra de formação, isto é, qual é o seu padrão de comportamento. Como a sequência segue certa constância, facilmente determinada, somos capazes de determinar qual seria o sétimo termo ou outro termo qualquer. Descrevemos então uma atividade bem cotidiana: trocar uma lâmpada. Apesar de parecer óbvia demais, muitas vezes fazemos este tipo de atividade inconscientemente, sem percebermos os pequenos detalhes. Programação Computacional – Notas de Aula – Capítulo 01 – 3 Vejamos como seria descrevê-la passo a passo: 1. Pegar uma escada; 2. Posicionar a escada embaixo da lâmpada; 3. Buscar uma lâmpada nova; 4. Subir na escada; 5. Retirar a lâmpada velha; 6. Colocar a lâmpada nova. Para se trocar a lâmpada, é seguida uma determinada sequência de ações, representadas através desse algoritmo. Como isso pode ser seguido por qualquer pessoa, estabelece-se aí um padrão de comportamento. A sequenciação tem por objetivo reger o fluxo de execução, determinando qual ação vem a seguir. O algoritmo anterior tem um objetivo bem específico: trocar uma lâmpada. E se a lâmpada não estiver queimada? O algoritmo faz com ela seja trocada do mesmo modo, não prevendo essa situação. Para solucionar este problema, podemos efetuar um teste condicional, verificando se a lâmpada está ou não queimada: 1. Pegar uma escada; 2. Posicionar embaixo da lâmpada; 3. Buscar uma lâmpada nova; 4. Ligar o interruptor; 5. Se a lâmpada não acender, então: 1. Subir na escada; 2. Retirar a lâmpada velha; 3. Colocar a lâmpada nova. Dessa forma, algumas ações estão ligadas à condição (lâmpada não acender). No caso da lâmpada acender, as três linhas: 1. Subir na escada; 2. Retirar a lâmpada velha; 3. Colocar a lâmpada nova. Programação Computacional – Notas de Aula – Capítulo 01 – 4 3.1 Fatores a serem levados em consideração na construção de um algoritmo 3.1.1 Complexidade Percebeu-se, na medida em que colocávamos situações novas no problema a ser resolvido, ia aumentando a complexidade do algoritmo. Esse certamente é o maior problema envolvido na construção de algoritmos. A complexidade pode ser vista como um sinônimo de variedade (quantidade de situações diferentes que um problema pode apresentar), as quais devem ser previstas na sua solução. Já que conviver com a complexidade é um mal necessário, é saudável fazer o possível para diminuí-la ao máximo, a fim de controlar o problema e encontrar sua solução. Deve-se diferenciar O QUE de COMO. Muitos programadores aumentam a complexidade de um devido problema desnecessariamente. A forma errada de interpretação de um problema pode levar a respostas irrelevantes à solução almejada ou até mesmo a nenhuma solução, gerando algoritmos mais complexos do que o necessário. Exemplo: digamos que se pergunte a um leigo a respeito de um relógio: 1. Como é um relógio? = É um instrumento com três ponteiros concêntricos. 2. Como a descrição não é relevante, poderíamos indagar: Um relógio com 2 ponteiros é possível? = É... pode ser! 3. Poderíamos ainda indagar: E um relógio com apenas 1 ponteiro não poderia ser uma possibilidade? = Bem... Pode ser com 3, 2 ou 1 ponteiro. 4. E sem ponteiro pode? Programação Computacional – Notas de Aula – Capítulo 01 – 5 = Ah!, Sim! Pode ser digital 5. Já a pergunta: “O que é um relógio?”, poderia resultar na resposta: = É um instrumento cuja finalidade é marcar o decorrer do tempo. Ou seja, algumas variáveis podem aumentar ou diminuir a complexidade de um sistema quando forem bem ou mal utilizadas. 3.1.2 Legibilidade Mede a capacidade de compreensão de um algoritmo por qualquer observador (que não o construiu), ou seja, a clareza com que sua lógica está exposta. Quanto mais legível for um algoritmo, menor será sua complexidade. 3.1.3 Portabilidade Devido a quantidade enorme de linguagens de programação existentes, não será adotada nenhuma linguagem específica para trabalhar os algoritmos (Ex.: C, pascal, Java etc.). Isso porque a solução do problema fica ligada a características e recursos da linguagem na qual ela foi concebida. Utilizaremos um pseudocódigo ou portugol (linguagem fictícia) que visa a permitir a representação dos algoritmos através da língua portuguesa (português estruturado). Esses algoritmos poderão ser convertidos facilmente para qualquer linguagem de programação usual (C, C++, pascal, Java etc.). 3.1.4 Técnica de resolução por método cartesiano A famosa frase de Descartes “Dividir para conquistar” é muito importante dentro da programação. É um método que ataca um problema grande, de difícil solução, dividindo-o em problemas menores, de solução mais fácil. Se necessário, pode-se dividir novamente as partes não compreendidas. Esse método pode ser esquematizado em passos: 1. Dividir o problema em partes; 2. Analisar a divisão e garantir a coerência entre as partes; Programação Computacional – Notas de Aula – Capítulo 01 – 6 3. Reaplicar o método, se necessário; ou 4. Planejamento reverso: Consiste em, a partir do resultado final, determinar quais são os componentes básicos. Ou seja, a partir da saída desejada, devemos poder determinar, reversamente, quais são os componentes da entrada de dados necessários. 3.2 Método para construir um algoritmo Utilizando os conceitos já desenvolvidos, esquematizaremos um método para construir um algoritmo logicamente correto: 3.2.1 Ler atentamente o enunciado Deve-se reler o enunciado de um exercício quantas vezes forem necessárias, até compreendê-lo completamente. A maior parte da resolução de um exercício consiste na compreensão completa do enunciado. 3.2.2 Retirar a relação das entradas de dados do enunciado Através do enunciado, descobrimos quais são os dados que devem ser fornecidos ao programa, via teclado, a partir dos quais são desenvolvidos os cálculos. Obs.: Pode haver algum algoritmo que não necessite da entrada de dados (pouco comum). 3.2.3 Retirar do enunciado, a relação das saídas de informação Através do enunciado podemos descobrir quais são as informações que devem ser mostradas para compor o resultado final, objetivo do algoritmo. 3.2.4 Determinar o que deve ser feito para transformar as entradas nas saídas especificadas Nessa fase é que teremos a construção do Algoritmo propriamente dito. Devemos determinar qual sequência de passos ou ações é capaz de transformar um conjunto de dados nas informações de resultado. Para isso, utilizamos os fatores descritos Programação Computacional – Notas de Aula – Capítulo 01 – 7 anteriormente, tais como legibilidade, portabilidade, método cartesiano e planejamento reverso, e finalmente podemos construir o algoritmo. 3.3 Exercício de Fixação 1. Elabore um algoritmo que mova 3 discos de uma torre de Hanói, que consiste em 3 hastes (A-B-C), uma das quais serve de suporte para os três discos de tamanhos diferentes (1-2-3), os menores sobre os maiores. Siga as seguintes regras: i. Deve-se mover um disco de cada vez para qualquer haste; ii. Nunca deve ser colocado um disco maior sobre um menor. iii. O objetivo é transferir os três discos da haste A para haste C. • Solução: 7 movimentos 1. Mova disco 1 da haste A para haste C 2. Mova disco 2 da haste A para haste B 3. Mova disco 1 da haste C para haste B 4. Mova disco 3 da haste A para haste C 5. Mova disco 1 da haste B para haste A 6. Mova disco 2 da haste B para haste C 7. Mova disco 1 da haste A para haste C • Explicações: Alguns pontos são necessários ressalta quanto a solução deste problema da torre de Hanói: Existe uma formula matemática para descobrirmos o número ideal de movimentos que devemos realizar. Esta formula é representada por: T(n) = 2n − 1, na qual T(n) representa o menor número de movimentos em função de n que representa o número de discos do problema. Programação Computacional – Notas de Aula – Capítulo 01 – 8 Outro ponto a se observado, é para qual haste devemos movimentar o primeiro disco. Este primeiro movimento e muito importante, pois se ele for feito para a haste errada, resultará em um numero maior de movimentos. A regra é a seguinte, se possuirmos um número impar de discos, deve-se movimentar o primeiro disco para a haste onde se deseja ao final manter todos os discos. No exercício acima o primeiro movimento deverá ser, portanto para a haste C, caso o disco seja movimentado para a haste B tem-se dois ou mais movimentos desnecessários, conforme mostrado abaixo: 1. 2. 3. 4. 5. 6. 7. 8. 9. 9 Movimentos Mova disco 1 da haste A para haste B Mova disco 2 da haste A para haste C Mova disco 1 da haste B para haste A Mova disco 2 da haste C para haste B Mova disco 1 da haste A para haste B Mova disco 3 da haste A para haste C Mova disco 1 da haste B para haste A Mova disco 2 da haste B para haste C Mova disco 1 da haste A para haste C 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 11 Movimentos Mova disco 1 da haste A para haste B Mova disco 2 da haste A para haste C Mova disco 1 da haste B para haste C Mova disco 3 da haste A para haste B Mova disco 1 da haste C para haste B Mova disco 2 da haste C para haste A Mova disco 1 da haste B para haste A Mova disco 3 da haste B para haste C Mova disco 1 da haste A para haste B Mova disco 2 da haste A para haste C Mova disco 1 da haste B para haste C Com dois discos observe solução abaixo, que comprovará a explicação dada acima para o primeiro movimento quanto temos um numero par de discos e quando temos um numero impar de discos: 1. 2. 3. 3 Movimentos Mova disco 1 da haste A para haste B Mova disco 2 da haste A para haste C Mova disco 1 da haste B para haste C 1. 2. 3. 4. 5. 5 Movimentos Mova disco 1 da haste A para haste C Mova disco 2 da haste A para haste B Mova disco 1 da haste C para haste A Mova disco 2 da haste B para haste C Mova disco 1 da haste A para haste C Outro ponto importante a ser observado e que sempre o número de movimentos deverá ser impar, caso a solução seja um número par de trocas estará incorreta. Programação Computacional – Notas de Aula – Capítulo 01 – 9 2. Um homem viajava transportando junto a si um balde de leite, um cão e um gato. Em certo momento da viagem precisou atravessar um rio. Ali encontrava a sua disposição um barco que possui capacidade de transporte apenas dele mesmo e mais uma de suas três cargas. O que o homem deve fazer para conseguir atravessar o rio sem perder suas cargas? Nota: O gato bebe o leite; O cão come o gato; Informações: homem, cão, gato, leite e barco. Ação: transportar homem mais carga com segurança. Resultado: Atravessar as cargas para a outra margem do rio. • Solução: 1. Atravessar o homem e o gato; 2. Voltar o homem; 3. Atravessar o homem e o leite; 4. Voltar o homem e o gato; 5. Atravessar o homem e o cão; 6. Voltar o homem; 7. Atravessar o homem e o gato. 3. Três Jesuítas e três canibais precisam atravessar um rio, para tal, dispõe de um barco com capacidade para duas pessoas. Por medidas de segurança não se permite que em alguma margem a quantidade de jesuítas seja inferior a de canibais. Qual a sequência de passos que permitirá atravessar com os jesuítas e canibais com segurança? Informações: Três jesuítas, Três canibais, Um barco com capacidade para duas pessoas. Ação: Atravessar para a outra margem do rio. Resultado: Chegar com segurança. • Solução: 1. Atravessar um Jesuíta e um Canibal 2. Voltar um Canibal 3. Atravessar dois Canibais 4. Voltar um Canibal 5. Atravessar um Jesuíta e um Canibal 6. Voltar um Canibal 7. Atravessar dois Canibais 8. Voltar um Canibal 9. Atravessar um Jesuíta e um Canibal. Programação Computacional – Notas de Aula – Capítulo 01 – 10 4 PSEUDOCÓDIGOS E FLUXOGRAMAS 4.1 Pseudocódigos O pseudocódigo é um código simplório, ou seja, não é um código real, mas um código imaginário que lembra o código de programação. O pseudocódigo é muito utilizado para apresentar a lógica algorítmica de forma mais simples, sem ter que se preocupar muito com o aspecto técnico das linguagens reais. O pseudocódigo não segue um padrão definido, portanto, qualquer um pode escrever seu pseudocódigo da forma que bem entender desde que ele transmita a ideia central da lógica da programação. Por exemplo, abaixo estão dois pseudocódigos que descrevem o mesmo algoritmo: Exemplo 1: INICIO entrada de dado : grava em VAR1 verificar VAR1 : letra ? verdade : imprimir dado -> "Você digitou uma letra" falso : imprimir dado -> "Você digitou um número" FIM Exemplo 2: INICIO: procedimento VARIÁVEIS var1 var1 <- entrada de dados:TECLADO se (var1 É letra) então imprimir dado:MONITOR -> "Você digitou uma letra" caso contrário imprimir dado:MONITOR -> "Você digitou um número" FIM: procedimento 4.2 Fluxogramas Fluxogramas têm o mesmo objetivo dos pseudocódigos, a única diferença é que os fluxogramas são representações gráficas. Programação Computacional – Notas de Aula – Capítulo 01 – 11 A vantagem principal dos fluxogramas é que, diferentemente dos pseudocódigos, eles são padronizados. Ou seja, cada símbolo representa uma ação específica e sempre representará. Um fluxograma usa linhas para ligar seus elementos, criando assim, um caminho que deve ser seguido. Abaixo está uma tabela com as representações do fluxograma e o que são. Inicio e Fim Sentença e Comandos Conectores de fluxo Estrutura de Seleção Estrutura de Repetição Processo Alternativo Programação Computacional – Notas de Aula – Capítulo 01 – 12 Programação Computacional – Notas de Aula – Capítulo 01 – 13 Fluxogramas Fluxograma para testar A<B<C; Programação Computacional – Notas de Aula – Capítulo 01 – 14 5 EXERCÍCIOS PROPOSTOS: 1. Dada a série de números: 1, 1, 2, 3, 5, 8, 13, qual é o próximo? 2. Um pai preocupado com a saúde de seus filhos quer que eles comam maçãs, mas não sabe fazer a distribuição. Se der 5 maçãs para cada filho, vão lhe sobrar quatro, se der 6, vai faltar uma. Quantos filhos e quantas maçãs ele tem? 3. Daniela é mais jovem do que Adriano. Carlos é mais velho do que Daniela. Qual dessas conclusões é verdadeira? a. Adriano é mais velho do que Carlos. b. Carlos é mais velho do que Adriano. c. Daniela é a mais jovem dos três. 4. Distribua os números de 1 a 9 nos círculos abaixo, de modo que a soma das linhas seja sempre 10. 5. Em uma folha de papel traçam-se duas retas, formando um ângulo de 15°. Ao utilizar uma lente que aumenta três vezes, quantos graus passará a ter o ângulo? 6. Oito pessoas de uma só família estão sentadas em volta de uma mesa redonda. Seu Daniel é o chefe da família, é casado com dona Marina, ótima cozinheira, principalmente aos domingos, quando toda a família vem almoçar. Eles têm 3 filhos: Claudinho, que é casado com Doroti; Luísa, que é solteira e estuda nos Estados Unidos; e Júlio, que é viúvo. A filha de Claudinho e Doroti chama-se Sônia e sempre se senta entre os dois. Os filhos de Júlio chamam-se Pedro e Paulo, sempre estão brigando, e a avó não permite que se sentem juntos. Júlio Programação Computacional – Notas de Aula – Capítulo 01 – 15 sempre coloca os cotovelos na mesa e isso irrita Doroti, que sempre fica longe dele. Júlio prefere sentar-se no lado esquerdo do pai. Dona Marina tem um carinho especial pelo neto Pedro e está sentada ao lado dele, enquanto conversa animadamente com sua nora, que está à sua esquerda. Paulo sempre chega depois que o almoço foi servido e nunca fica contente com o lugar que sobrou para ele. Em que lugares estão sentadas todas as pessoas em volta da mesa? 7. Descreva a sequência de passos necessária para: a. Fritar um ovo b. Trocar um pneu furado c. Colocar um carro em movimento d. Atravessar a rua e. Fazer as malas f. Fazer uma prova g. Jogar o jogo da forca h. Jogar o jogo da velha