Programação de Computadores I UFOP DECOM 2013 2 Aula prática 6 Comandos de repetição while Resumo Nesta aula vamos trabalhar com problemas cuja solução envolve realizar um cálculo ou tarefa repetidas vezes, enquanto uma determinada condição é satisfeita. Em outras palavras, a implementação da solução de tais problemas requer o uso de um comando de repetição, tal como o comando while. Sumário 1 Comandos de repetição - while A solução de diversos problemas, em computação, envolve a repetição de uma sequência de tarefas, ou comandos, enquanto uma determinada condição é satisfeita. Esse processo de repetição, ou loop, é implementado por meio do comando while, que tem a seguinte sintaxe: while condição bloco de comandos while A condição deve ser uma expressão booleana, isto é, cujo valor é verdadeiro (%t) ou é falso (%f). O bloco de comandos é qualquer sequência de comandos, incluindo, possivelmente, comandos de atribuição, de entrada e saída, de desvio ou outros comandos de repetição. A execução de um comando while é feita do seguinte modo: 1. a condição do while é avaliada; 2. se a condição avalia para %t (verdadeira), o bloco de comandos while é executado, e volta-se ao passo 1; 3. caso contrário, isto é, se a condição avalia para %f (falso), comando while termina. A execução do programa prossegue a partir do comando imediatamente subsequente ao do comando while. Note que, se a condição for inicialmente falsa, isto é, se o resultado for %f na primeira vez em que a condição é avaliada, o bloco de comandos while não é executado nenhuma vez. Por outro lado, se o valor da condição permanece sempre verdadeiro, em cada iteração do comando while, a execução desse comando prossegue indefinidamente. 2 Exemplos 2.1 Exemplo 1 Suponha que queremos determinar a menor sequência 1,2,..., k, tal que a soma 1+2+ +k é maior ou igual a um dado valor n. Uma possível maneira de resolver este problema, seria calcular a soma 1 + 2 + + k repetidamente, para valores crescentes de k, até que essa soma seja maior ou igual a n. Isso pode ser implementado, em Scilab, do seguinte modo: 1
// Menor sequência 1, 2,..., k cujo somatório é maior ou igual a n n = input("digite um número inteiro positivo: ") nums = [] // sequência de números considerados soma = 0 // soma de todos os valores da sequência nums next = 1 // próximo inteiro a ser incluído na sequência while soma < n nums(1,next) = next // inclui o o valor de next no vetor nums soma = soma + next // adiciona o valor de next à soma da sequência next next = next + 1 // próximo número a ser considerado na sequência printf("a menor sequência 1, 2,..., k cujo somatório é >= %g é:", n) disp(nums) Digite um número inteiro positivo: 10 A menor sequência 1, 2,..., k cujo somatório é >= 10 é:" 1. 2. 3. 4. Observações: 1. No corpo do comando while do programa acima, a execução do comando nums(1,next) = next atribui ao elemento da linha 1, coluna next, do vetor nums, o valor contido na variável next. Por exemplo, se o valor de next for 5, após execução deste comando, o valor de nums(1,5) será 5. Note que, no programa acima, isso significa que nums é estido, colocando-se mais um valor no final desse vetor. Em Scilab, pode também ser usada a seguinte forma alternativa, que pode ser entida como "esta o vetor nums, incluindo o valor next no final deste vetor". nums = [nums next] 2. A função disp pode ser usada para exibir um valor na tela, de maneira semelhante à função printf. A função disp recebe como argumento apenas os valores a serem exibidos na tela, não permitindo especificar como esses valores devem ser formatados, o que significa que os valores são exibidos em uma formatação padrão adotada pelo Scilab. 2.2 Exemplo 2 - Validando um dado de entrada Uma tarefa comum em programas consiste em testar se um dado fornecido pelo usuário é válido, isto é, se corresponde a um valor esperado. Quando um usuário digita um valor inválido, em geral queremos permitir que ele possa corrigir seu erro, digitando um novo valor. Para isso, devemos solicitar ao usuário uma nova entrada, repetidas vezes, até que o valor digitado seja válido. O exemplo a seguir ilustra a implementação desse tipo de tarefa, onde se considera a entrada válida somente se ela for um número inteiro positivo: // Verifica se o valor digitado é um inteiro positivo n = input("digite um valor inteiro positivo: "); while n <> int(n) n <= 0 printf("valor inválido.") n = input("digite um valor inteiro positivo: "); O corpo do comando while será repetido enquanto o usuário insistir em digitar um número que não seja inteiro, ou que seja negativo ou nulo. Portanto, garante-se que, ao final do while, 2
o valor de n será um número inteiro positivo. Note que, se o usuário digitar um valor válido inicialmente, o corpo do comando while não será executado nenhuma vez. 2.3 Exemplo 3 - Controle de Qualidade Uma indústria de tubos de aço possui, em sua linha de produção, uma máquina para cortar tubos. A máquina de corte é controlada por um programa, que verifica se os comprimentos dos tubos cortados estão dentro de uma determinada margem de erro, em relação ao comprimento desejado. Os tubos com comprimento inadequado são rejeitados. No início da operação da máquina, são especificados o comprimento desejado para os tubos, a margem de erro aceitável e a quantidade de tubos necessária. A operação da máquina deve parar quando tiver sido obtido o número de tubos desejados, com comprimentos dentro da margem de erro especificada. Ao final da operação da máquina, o programa imprime o total de tubos cortados e o número de tubos rejeitados, tal como mostrado no exemplo de execução do programa, a seguir. Controle de Qualidade de Corte de Tubos Comprimento de corte dos tubos: 10 Erro aceitável:.2 Número de tubos desejados: 4 Comprimento do tubo cortado: 10.1 Comprimento do tubo cortado: 9.7 Comprimento do tubo cortado: 9.9 Comprimento do tubo cortado: 10.3 Comprimento do tubo cortado: 10 Comprimento do tubo cortado: 10.2 Desligue a máquina. Total de tubos cortados = 6 Total de tubos rejeitados = 2 Como poderia ser implementado esse programa? A idéia é usar um loop, que deverá terminar quando for obtido o número de tubos desejados. No corpo do loop, devemos contar o número de tubos com comprimento válido, isto é, com comprimento dentro da margem de erro aceitável, em relação ao comprimento desejado. Essa idéia é implementada pelo programa a seguir. 3
clear; clc; // Controle de Qualidade de corte de tubos printf("controle de Qualidade de Corte de Tubos\n") printf("\n") lenp = input("comprimento de corte dos tubos: ") errp = input("erro aceitável: ") ntubos = input("número de tubos desejados: ") printf("\n") tubosok = 0 // quantidade de tubos com comprimento aceitável tottubos = 0 // quantidade total de tubos cortados while tubosok < ntubos len = input("comprimento do tubo cortado: ") err = abs(len - lenp) if err <= errp then tubosok = tubosok + 1 tottubos = tottubos + 1 printf("\n") printf("desligue a máquina.\n") printf("total de tubos cortados = %g\n", tottubos) printf("total de tubos rejeitados = %g\n", tottubos-ntubos) 4
3 Resolvo problemas Tarefa 1: Adivinhe! - 1 Qual é o menor número inteiro par que é divisível por 7 e cujo cubo é maior do que 4.000? Escreva um programa para calcular e imprimir esse número. O menor número par que é múltiplo de 7 e cujo cubo é maior do que 4.000 é 28 Dica: A idéia é usar um loop, que seja repetido enquanto o número procurado n não for múltiplo de 2, ou não for múltiplo de 7, ou seu cubo for menor que 4.000. Em cada passo, ou iteração do loop, o valor de n deve ser incrementado. É claro que, antes iniciar o loop, deve ser atribuído a n um valor apropriado. Existem quatro possíveis alternativas para implementar a solução: 1. Atribuir inicialmente a n o valor 1, e incrementar n de 1 em cada passo do loop. Neste caso, qual deve ser a condição de teste do loop? 2. Atribuir inicialmente a n o valor 2, e incrementar n de 2 emcada passo do loop. Neste caso, qual deve ser a condição de teste do loop? Note que essa solução é mais eficiente do que a soluçãoanterior, pois o número de iterações do loop é a metade que na solução anterior. Além disso, a condição de teste do loop envolve um teste a menos. 3. Atribuir inicialmente a n o valor 7, e incrementar n de 7 em cada passo do loop. Neste caso, qual deve ser a condição de teste do loop? Essa solução é mais eficiente do que as duas anteriores. Porque? 4. Existe uma alternativa ainda mais eficiente do que as 3 anteriores. Qual é ela? Qual deve ser o valor inicial de n? De quanto n deve ser incrementado, em cada passo do loop? Qual deve sera condição de teste? Quão mais eficiente é esta solução, em relação à primeira? Veja que mesmo um programa bastante simples como este admite diversas soluções alternativas, com diferentes tempos de execução. É claro sempre almejamos a solução mais eficiente possível. Portanto, pense bem sobre a solução de cada problema, antes de implementá-lo. Tarefa 2: Adivinhe! - 2 Vamos agora generalizar o programa da tarefa anterior. Escreva um programa que leia 4 valores inteiros positivos a, b, c e k e encontre o menor número inteiro n que seja divisível por a e por b e tal que n k > c. Cálculo do menor n tal que n a, n b e n^k > c ------ a = 2 b = 7 c = 4000 k = 3 n = 28 5
Dica: Qual seria o valor apropriado para inicialização de n, antes do loop? De quanto n deve ser incrementado em cada passo do loop? Lembre-se que o máximo divisor comum de dois números inteiros a e b mdc(a, b) pode ser calculado pelo algoritmo de Euclides. O mínimo múltiplo comum de a e b mmc(a, b) pode ser calculado como: mmc(a, b) = aḃ mdc(a, b) Muitas vezes, um problema específico, tal como o do exemplo??, pode ser generalizado. A solução do problema mais geral nos permite obter resposta para vários problemas particulares, que são instâncias do problema mais geral. Essa capacidade de generalização, ou abstração, é um dos conceitos mais fundamentais em computação e na ciência em geral. Então, ao resolver um problema, pense sempre se ele pode ser generalizado, pois a solução do problema mais geral possivelmente poderá ser útil como parte da solução de outros problemas. 6
Tarefa 3: Collatz A conjectura de Collatz, também conhecida como conjectura 3n+1, foi proposta pelo matemático Lothar Collatz, em 1937. Para explicar essa conjectura, considere o seguinte processo que descreve como obter a Sequência de Collatz para um número inteiro n > 0: Se n for par, divida n por 2, obto n/2; se n for ímpar, multiplique n por 3 e some 1, obto 3n + 1. Repita esse processo para o valor obtido, e assim sucessivamente, até que o valor obtido seja 1. Exemplos: n sequência de Collatz para n 5 5, 16, 8, 4, 2, 1 11 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 12 12, 6, 3, 10, 5, 16, 8, 4, 2, 1 A conjectura é que esse processo de cálculo sempre termina: sempre se obtém, eventualmente, o valor 1, para qualquer inteiro n > 0 dado inicialmente. Tal conjectura nunca foi provada, mas também nunca se encontrou um exemplo em contrário. Escreva um programa que leia um valor inteiro n > 0 e imprima a sequência de Collatz para n, assim como o comprimento dessa sequência. O programa deve verificar se o valor de entrada é válido, solicitando um novo valor caso não o seja. Sequência de Collatz --------------------- Digite um número inteiro > 0: 11 Collatz: 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 Comprimento da sequência = 15 Dicas: 1. Uma maneira de implementar o programa é usar um vetor para armazenar a sequência de Collatz. Esse vetor deve conter, inicialmente, apenas o número digitado. Cada novo número da sequência é incluído nesse vetor, tal como mostrado no exemplo??. 2. Para imprimir a sequência no formato acima, use os comandos a seguir, onde seq é o vetor que contém a sequência gerada e i é o número de elementos dessa sequência: printf("\ncollatz: ") printf(strcat(repmat("%g ",1,i)),seq) O significado das funções repmat e strcat é explicado a seguir (veja mais detalhes no Help do Scilab): função descrição exemplo repmat( A,m,n) retorna uma matriz que consiste de m n cópias da matriz A repmat([1 2], 2, 3) = [1 2 1 2 1 2; 1 2 1 2 1 2] strcat(s) concatena todos os strings na matriz de strings S strcat(["ab" "cd"] = "abcd" strcat(["ab" "cd"; "ef" "gh"]) = "abefcdgh" 7
Tarefa 4: Controle de Qualidade Sua tarefa agora é modificar o programa apresentado no exemplo?? de modo que, ao final do programa, sejam também impressos os seguintes dados: 1. o comprimento de cada um dos tubos aceitos, 2. o erro médio de corte de todos os tubos, e 3. o maior erro de corte dos tubos. Controle de Qualidade de Corte de Tubos Comprimento de corte dos tubos: 10 Erro aceitável:.2 Número de tubos desejados: 4 Comprimento do tubo cortado: 10.1 Comprimento do tubo cortado: 9.7 Comprimento do tubo cortado: 10.3 Comprimento do tubo cortado: 10 Comprimento do tubo cortado: 10.2 Comprimento do tubo cortado: 9.9 Desligue a máquina. Total de tubos cortados = 6 Total de tubos rejeitados = 2 Tubos aceitos: 10.1 10 10.2 9.9 Erro médio = 0.166667 Maior erro = 0.3 8