Baixe o app para aproveitar ainda mais
Prévia do material em texto
Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 1 Introdução a Algoritmos e Programação com Python Uma abordagem dirigida por testes Raul Sidnei Wazlawick Elsevier, 2017 Respostas aos Exercícios Existem virtualmente infinitas formas de escrever programas para resolver cada problema. Neste documento apresentamos a forma que nos parece mais simples, mais correta e mais adequada ao estilo Python de programar. Aconselhamos, porém, que você só recorra a esta lista depois de ter feito cada programa proposto e visto que ele funciona. Então você poderá verificar se a estratégia que usou era realmente a mais simples. Caso você tenha feito um programa melhor do que as minhas sugestões, entre em contato e me avise! Haverá um agradecimento especial na próxima edição do livro. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 2 2 Alô Mundo 2.1 print('Lista de compras:\n\nMamão\nPão\nCafé') 2.2 O seguinte programa vai mostrar que o resultado é -6: print(2+2-2*2+(2-2*(2+2))) Confirma a evolução das operações na tabela abaixo. Primeiramente é executada a expressão 2+2 dos parênteses mais internos (Passo 1), restando avaliar: 2+2-2*2+(2-2*4). Em seguida, é avaliada a multiplicação 2*4 (Passo 2) dentro do que agora se constitui nos parênteses mais internos, restando avaliar: 2+2-2*2+(2-8). Em seguida é avaliada a expressão 2-8 dentro dos parênteses (Passo 3), restando avaliar depois: 2+2-2*2-6; observe que como o resultado da expressão 2-8 é negativo, a soma 2+(2-8) é convertida na subtração 2-6. Agora que não há mais parênteses na expressão, avalia-se a única multiplicação que restou, 2*2 (Passo 5), e em seguida as somas e subtração da expressão: 2+2-4-6 (Passos 6 a 8). Nesta última expressão, todos os operadores têm a mesma precedência e, portanto, são avaliados da esquerda para a direita, restando sucessivamente as seguintes expressões: 4-4-6, depois 0-6 e finalmente -6, o resultado final da expressão. Passo Expressão 1 2+2-2*2+(2-2*(2+2)) 2 2+2-2*2+(2-2*4) 3 2+2-2*2+(2-8) 4 2+2-2*2-6 5 2+2-4-6 6 4-4-6 7 0-6 8 -6 2.3 Supondo que seu peso seja 83 kg e sua altura 1,79m, o programa a seguir vai informar que seu índice de massa corporal (IMC) é igual a 25.90431010268094: print(83/(1.79*1.79)) Erros comuns que você pode ter cometido: 1. Esquecer que a divisão e multiplicação têm ambas a mesma precedência e que são executadas da esquerda para a direita. Assim, se você esquecer os parênteses e Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 3 escrever print(83/1.79*1.79) vai obter como resultado 83, ou seja, o peso original, já que ele será primeiro dividido pela altura e depois multiplicado pela altura novamente. 2. Trocar o ponto decimal por vírgula, como em print(83/(1,79*1,79)) ou print(83/1,79*1,79). No primeiro caso, o programa dará erro pois as vírgulas estão dentro dos parênteses da expressão numérica e não separando argumentos. No segundo caso, o resultado seria 83.0 79 79, ou seja, 83/1, 79*1 e 79. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 4 3 Variáveis 3.1 • Elefante – o interpretador aceita como identificador, mas não deve ser usado como nome da variável por questão de estilo: variáveis em Python devem iniciar por letra minúscula. Assim, o correto seria "elefante". Mais adiante veremos casos em que identificadores devem iniciar com letra maiúscula. • madeira – identificador válido para variáveis. • casaDePedra – o interpretador aceita como identificador, mas não deve ser usado por questão de estilo, pois identificadores de variáveis com nomes compostos devem usar o estilo snake_case. Assim, o correto seria "casa_de_pedra". • tábua_de_passar – identificador válido para variáveis, desde que o sistema permita letras acentuadas. • linha42 – identificador válido para variáveis. • 3a_dimensão – inválido, pois inicia com algarismos numéricos. Opções válidas seriam "terceira_dimensão" ou ainda "dimensão3". • validação&verificação – inválido pois contém um caractere especial "&". O correto seria "validação_e_verificação". • 14bis – inválido pois inicia em número. Opções válidas: "quatorze_bis" ou "modelo_14_bis". • continue – inválido pois é palavra reservada de Python. Confira a lista apresentada um pouco antes do enunciado do exercício. 3.2 x = 45 # aqui x=45 y = 14 # aqui x=45 e y=14 z = x // y # aqui x=45, y=14 e z=3 print(z) # aqui imprime 'z': 3 z = y % z # aqui x=45, y=14 e z=2, 'z' mudou print(x) # aqui imprime x: 45 y = 10 # aqui x=45, y=10 e z=2, 'y' mudou print(z) # aqui imprime z: 2 Erros comuns: • Achar que no final "z" vale 0 no final porque "y" passou a valer 10 e 10 % 2 é igual a 0. Isso não acontece porque os comandos são executados em sequência e quando o valor de "z" foi atualizado pela última vez, "y" ainda valia 14. Mesmo que "y" seja alterado depois disso, o valor de "z" permanece sendo o que era em sua última atribuição. • Achar que o programa vai imprimir 3, 2 e 10, que são os últimos valores calculados antes do comando "print". Isso não acontece porque o comando "print" imprime a variável que é passada como argumento, e não o valor da última variável atualizada no programa. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 5 Com este exemplo podemos introduzir uma importante técnica de verificação de programas chamada "trace table" ou "tabela de rastreamento", que por um grosseiro erro de tradução de décadas atrás acabou sendo conhecido em português como "teste de mesa" ("table" tanto pode ser traduzido como "tabela" quanto como "mesa" e em algum momento nos anos 1970 algum autor optou pela tradução menos adequada e assim "trace table" acabou virando "teste de mesa" para muitos programadores brasileiros). Uma tabela de rastreamento terá as variáveis do programa indicadas nas colunas e cada linha representará os valores de cada variável após a execução de cada linha do programa. Para não ter que escrever todos os comandos dentro da tabela usualmente se trabalha com os números das linhas dos comandos executados, como mostrado abaixo: 1 x = 45 2 y = 14 3 z = x // y 4 print(z) 5 z = y % z 6 print(x) 7 y = 10 8 print(z) Assim, a tabela de rastreamento para o programa deste exercício poderia ser representada da seguinte forma: Linha x y z 1 45 - - 2 45 14 - 3 45 14 3 4 45 14 3 5 45 14 2 6 45 14 2 7 45 10 2 8 45 10 2 Para simplificar essa tabela podemos representar nela apenas os valores que foram modificados em cada linha (valores em negrito na tabela anterior). Dessa forma, o valor de uma variável em uma linha será aquele que for encontrado na própria linha ou na linha anterior mais próxima da atual e acima dela onde esse valor seja definido: Linha x y z 1 45 - - 2 14 - 3 3 4 5 2 6 7 10 8 Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 6 Assim, observando a tabela, o valor das variáveis na linha 6 por exemplo será 45 para "x", porque "x" recebeu esse valor na linha 1 e não mudou mais até a linha 6, 14 para "y" porque "y" recebeu esse valor na linha 2 e não mudou mais até a linha 6, e 2 para a variável "z", porque "z" recebeu esse valor na linha 5 e não mudou na linha 6. 3.3 print('{0} e {1} são {2} e {3}'.format(x, y, a, b)) print('Total: {0}'.format(x+y)) print('{0} {1} {2} são números inteiros'.format(a, b, c)) print('{0} + 0= {0}'.format(z)) Aqui o exercício era apenas para escrever as expressões. O programa não irá rodar porque os valores das variáveis não foram definidos. 3.4 print('{0}/{1}/{2}'.format(dia, mês, ano)) Da mesma forma, aqui o programa não deve ser rodado, apenas a linha escrita para fim de resolução do exercício. 3.5 print('Este programa calcula a média de três valores') a = int(input('Primeiro valor:')) b = int(input('Segundo valor:')) c = int(input('Terceiro valor:')) print('Média:', (a+b+c)/3) Erros comuns: • Esquecer o "int". Neste caso, o programa dá erro assim que você digitar o primeiro número. • Usar "//" ao invés de "/". Ninguém falou que era para calcular a média "inteira". Observe que os números digitados como entrada precisam ser necessariamente inteiros, por causa da função "int". Mas o resultado é apresentado em ponto flutuante. • Esquecer os parênteses na expressão (a+b+c)/3, o que daria os resultados 12.333333333333332 36.0 0.0 para os testes, equivalendo à expressão a+b+c/3, na qual a divisão c/3é executada antes das somas. 3.6 print('Este programa eleva um número ao seu cubo') número = int(input('Número: ')) print('{0} elevado ao cubo é {1}'.format(número, número**3)) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 7 3.7 print('Este programa calcula a raiz cúbica de um número') x = int(input('Número: ')) print('A raiz cúbica de {0} é: {1}'.format(x, x**(1/3))) Erros comuns: • Esquecer os parênteses em x**(1/3). Se você usar a expressão x**1/3 terá como resultado uma expressão equivalente a (x**1)/3. Isso acontece porque a precedência da potenciação (**) é maior do que a da divisão. • Ao invés de 1/3 usar o valor simplificado de 0.3 ou 0.33, que incorpora um erro de precisão importante ao resultado. • Ao entrar com os números negativos, escrever "- 40" em vez de "-40". Não se pode usar espaço entre o sinal de negativo e o número em si. Se você fizer isso o programa vai dar erro após a entrada de dados. 3.8 print('Este programa calcula a raiz n de um número x') n = int(input('n: ')) x = int(input('x: ')) print('A raiz {0} de {1} é {2}.'.format(n, x, x**(1/n))) 3.9 print('Este programa calcula áreas e volume de uma sala') altura = int(input('Altura: ')) comprimento = int(input('Comprimento: ')) largura = int(input('Largura: ')) print('Área do piso:', largura*comprimento) print('Volume da sala:', largura*comprimento*altura) print('Área das paredes:', 2*(largura*altura + comprimento*altura)) 3.10 print('Quantos segundos se passaram desde a meia-noite!') print('Que horas são?') horas = int(input('Horas (0-23): ')) minutos = int(input('Minutos (0-59): ')) segundos = int(input('Segundos (0-59): ')) print('Já se passaram {0} segundos desde a meia noite' .format(3600*horas + 60*minutos + segundos)) Obs.: Como a linha final ficou bastante longa, optamos por colocar o envio da mensagem "format" na linha abaixo do "print". Existem regras de estilo para quebra de linha deste tipo. No caso, o ponto ideal para fazer a quebra de linha é exatamente antes do ponto de envio da mensagem e o recuo na linha seguinte deve coincidir com o início da string, conforme mostrado acima. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 8 3.11 print('Raizes de um polinômio ax**2+bx+c') a = int(input('a: ')) b = int(input('b: ')) c = int(input('c: ')) delta = (b**2 - 4*a*c)**(1/2) # delta é o resultado da raiz quadrada x1 = (-b+delta)/2*a x2 = (-b-delta)/2*a print('Primeira raiz:', x1) print('Segunda raiz:', x2) Erros comuns: • Escrever "2a" ao invés de "2*a". O interpretador não entende que "2a" deva ser uma multiplicação; você deve ser sempre explícito e usar o operador "*" para indicar multiplicações. • Não usar a variável "delta" para conter o resultado da raiz quadrada não é necessariamente um erro, mas o programa resultante terá a expressão (b**2- 4*a*c)**(1/2) repetida duas vezes, se você não armazenar esse valor em uma variável intermediária como "delta" ou "discriminante". Embora não cause erro, repetir código é considerado uma prática ruim entre programadores. 3.12 print('Conversão de segundos em horário') segundos_totais = int(input('Qual a quantidade de segundos? ')) horas = segundos_totais //3600 segundos_restantes = (segundos_totais - horas*3600) minutos = segundos_restantes//60 segundos = segundos_restantes % 60 print('{0}h{1}m{2}s'.format(horas, minutos, segundos)) Obs.: O maior desafio neste exercício está em perceber que para obter o valor em minutos você não pode dividir o total de segundos por 60, o que seria talvez sua primeira tentativa. Você deve dividir por 60 aquilo que sobra da quantidade total de segundos, depois que você subtrai dela a quantidade de segundos contidos nas horas inteiras que já foram calculadas, ou seja, segundos_totais - horas*3600, que, para não repetir duas vezes a expressão, armazenamos na variável "segundos_restantes". Daí para encontrar a quantidade de segundos é só ver quanto sobrou desse valor dividido por 60. 3.13 G = 9.80665 h = 56 tempo = (2*h/G)**(1/2) print('Tempo para Torre de Pisa:', tempo) h = 333 tempo = (2*h/G)**(1/2) print('Tempo para Torre de Tóquio:', tempo) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 9 Resultados: • Tempo para Torre de Pisa: 3.3794706072627405 • Tempo para Torre de Tóquio: 8.240940467223995 Aqui neste exemplo não tivemos como evitar de escrever a expressão (2*h/G)**(1/2) duas vezes. Mais adiante, com outras estruturas de programação, porém, poderemos evitar esse tipo de redundância nos programas. 3.14 print('Este programa simula prestações de uma compra') valor = float(input('Valor da compra: ')) print('A vista:', round(valor*0.9, 2)) print('Prestação em 5x:', round(valor/5, 2)) print('Prestação em 10x:', round(valor*1.2/10, 2)) 3.15 5.13532181e-07 5.9012210122e10 Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 10 4 Repetição Fixa 4.1 print('Canção dos programadores') for i in range(99, 251): print(i, 'bugs no software, pegue um deles e conserte...') Erro comum: usar range(99, 250). Neste caso, a canção vai parar em 249. Lembre-se que o limite superior de um "range" não pertence à lista de números gerada por ele. 4.2 print('Canção dos programadores v.2') for i in range(99, 251): print(i, 'bugs no software, pegue um deles e conserte...') print('Tecle "Ctrl-F5"') print('Vamos fazer mais um café!') 4.3 for i in range(2004, 2100, 4): print(i) Observação: o segundo argumento da função "range" poderia ser qualquer número entre 2097 e 2100 inclusive. 4.4 print('Canção dos programadores v.3') for i in range(99, 251, 7): print(i, 'bugs no software, pegue um deles e conserte...') print('Tecle "Ctrl-F5"') print('Vamos fazer mais um café!') O valor de parada (segundo argumento da função "range") deve ser 251 porque ela deve parar "em ou antes" de 250. Se o valor fosse 250, a repetição só pararia antes. 4.5 Se o usuário digitar "0", vai visualizar apenas "0" e "Fogo!", pois o valor final da repetição é -1. Assim, o "0" está incluído na sequência. Já para qualquer valor negativo digitado pelo usuário, ele vai visualizar apenas a palavra "Fogo!", pois a repetição não será executada nenhuma vez. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 11 4.6 print('Canção dos bons programadores') for i in range(99, 0, -11): print(i, 'bugs no software, pegue onze deles e conserte...') print('Tecle "Ctrl-F5"')print('Sem erros no software! Está estabilizado!') 4.7 print('Programa que simula prestações em compras sem juros') valor = int(input('Valor da compra:')) for i in range(1, 21): print('{0}x de R${1}'.format(i, valor//i)) Erros comuns: • Se você iniciar o "range" em 0, vai dar um erro no programa porque ele vai tentar dividir o valor por zero. • Se você terminar o "range" em 20, ele vai parar no valor 19. Lembre-se que o range nunca inclui o valor final. • Se você usar "/" em vez de "//" vai obter valores com casas decimais, e o enunciado do exercício pedia para mostrar apenas o valor inteiro. 4.8 print('Cálculo do somatório dos inteiros entre dois limites') inferior = int(input('Entre com o limite inferior:')) superior = int(input('Entre com o limite superior:')) somatório = 0 for i in range(inferior, superior+1): somatório += i print(somatório) 4.9 print('Cálculo do somatório dos quadrados') n = int(input('Entre com o limite superior:')) somatório = 0 for i in range(1, n+1): somatório += i**2 print(somatório) 4.10 print('Cálculo do somatório dos termos de um polinômio') a = int(input('Entre com o valor de a:')) b = int(input('Entre com o valor de b:')) n = int(input('Entre com o valor de n:')) somatório = 0 for i in range(1, n+1): somatório += a - b*i + i**2 print(somatório) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 12 4.11 QUANTIDADE = 10 print('Somatório e média de {0} números'.format(QUANTIDADE)) somatório = 0 for i in range(QUANTIDADE): número = int(input('Entre com um valor:')) somatório += número print('Somatório:', somatório) print('Média:', somatório/QUANTIDADE) 4.12 print('Média de n números') quantidade = int(input('Quantos números?')) somatório = 0 for i in range(quantidade): número = int(input('Entre com um valor:')) somatório += número print('Média:', somatório/quantidade) 4.13 print('Seu número da sorte') data_nascimento = int(input(Data de nascimento no formato ddmmaaaa:')) número_da_sorte = 0 for i in range(8): número_da_sorte += data_nascimento % 10 data_nascimento //= 10 print('Seu número da sorte é: ', número_da_sorte) 4.14 print('Pagamento pelo jogo de xadrez') grãos = 0 for i in range(64): grãos += 2**i print('Grãos de arroz:', grãos) quilos = grãos//170000 print('Quilos:', quilos) quilômetros_quadrados = quilos//550000 print('Quilômetros quadrados:', quilômetros_quadrados) brasis = quilômetros_quadrados//8514876 print('Brasis:', brasis) Obs.: Os resultados são: • Grãos de arroz: 18446744073709551615 • Quilos: 108510259257115 • Quilômetros quadrados: 197291380 • Brasis: 23 Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 13 4.15 print('Este programa calcula o enésimo número de Fibonacci') ordem = int(input('Qual Fibonacci você quer saber? ')) atual = 1 último = 1 for i in range (2, ordem): penúltimo = último último = atual atual = último+penúltimo print('Resposta:', atual) O segredo aqui está em controlar qual o último e o penúltimo números de Fibonacci para calcular o atual. Dentro do "for" atualizamos, nesta estrita ordem, "penúltimo" recebendo o valor de "último", "último" recebendo o valor de "atual" e "atual" recebendo a soma de "último" e "penúltimo". Como no início da repetição "último" e "atual" valem 1, devemos começar o "range" em 2, porque já estamos calculando o segundo número de Fibonacci. Para ter uma visão mais clara sobre o funcionamento deste programa, sugerimos que utilize a tabela de rastreamento, como mostrada no capítulo anterior. Segue exemplo que mostra a tabela para o cálculo do quarto número de Fibonacci: 1 print('Este programa calcula o enésimo número de Fibonacci') 2 ordem = int(input('Qual Fibonacci você quer saber? ')) 3 atual = 1 4 último = 1 5 for i in range (2, ordem): 6 penúltimo = último 7 último = atual 8 atual = último+penúltimo 9 print('Resposta:', atual) Linha ordem atual último i penúltimo 1 2 4 3 1 4 1 5 2 6 1 7 1 8 2 5 3 6 1 7 2 8 3 5 4 9 Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 14 4.16 print('Este programa calcula o enésimo número de Tribonacci') ordem = int(input('Qual Tribonacci você quer saber? ')) atual = 1 último = 1 penúltimo = 0 for i in range(2, ordem): antepenúltimo = penúltimo penúltimo = último último = atual atual = último+penúltimo+antepenúltimo print('Resposta:', atual) Obs.: O exercício foi resolvido simplesmente adicionando-se uma nova variável para conter o antepenúltimo número da sequência o qual é necessário para calcular o Tribonatti. 4.17 fatorial = 1 for i in range(1, n+1): fatorial *= i print('{0}!={1}'.format(i, fatorial)) Obs.: Na versão anterior do programa não havia necessidade de iniciar a repetição com 1, porque o resultado era impresso no final. Mas agora, como os resultados são impressos dentro do "for" fazemos a repetição ocorrer uma vez, mesmo que para multiplicar 1 por 1, mas também para imprimir o resultado na tela que, de outra forma, não seria visto. 4.18 print('Calendário de 2017 a 2020') for ano in range(2017, 2021): for mês in range(1, 13): for dia in range(1, 31): print('{0}/{1}/{2}'.format(dia, mês, ano)) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 15 5 Seleção 5.1 for i in range(256): print(i, chr(i)) Teste depois o programa substituindo 256 por 55296 e veja o resultado. 5.2 print('Digite dois números diferentes e direi qual o maior') x = int(input('Primeiro número: ')) y = int(input('Segundo número: ')) if x > y: print('O maior é', x) else: print('O maior é', y) 5.3 print('Cálculo da nota geral de um edital') qualidade = int(input('Nota da qualidade: ')) preço = int(input('Nota do preço: ')) prazo = int(input('Nota do prazo: ')) if qualidade <7: nota_geral = 0 else: if preço >= 7: nota_geral = (qualidade+preço+prazo)/3 else: nota_geral = (qualidade+2*preço+prazo)/4 print('Nota geral:', nota_geral) Obs.: Não é necessário explicitar a última condição (condição 3 do enunciado) porque caso a primeira e a segunda não sejam verdadeiras, a terceira, por eliminação, tem que ser verdadeira. Assim, a estrutura do programa pode terminar com um "else" para a terceira condição, não sendo necessário escrever if preço < 7. 5.4 print('Digite a idade de 10 pessoas e direi quais são maiores') maiores = 0 for i in range(1, 11): idade = int(input('[{0}]:'.format(i))) if idade >= 18: maiores += 1 print(maiores, 'pessoas são maiores de idade') Obs.: aqui não é necessário dar tratamento especial ao primeiro elemento como fizemos no caso de encontrar o maior elemento de uma sequência de números. Como se trata de contar Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 16 quantos elementos em uma lista tem determinada propriedade (ser maior de idade), basta inicializar a variável que vai conter essa quantidade com zero e acrescentar 1 cada vez que um elemento com a propriedade for encontrado. 5.5 print('Digite 10 números e eu direi a média dos pares e dos ímpares') quantos_pares = 0 quantos_ímpares = 0 soma_pares = 0 soma_ímpares = 0 for i in range(1,11): número = int(input('[{0}]:'.format(i))) if número % 2 == 0: quantos_pares += 1 soma_pares += número else: quantos_ímpares += 1 soma_ímpares += número print('Média dos pares:', soma_pares/quantos_pares) print('Média dos ímpares:', soma_ímpares/quantos_ímpares)5.6 print('Digite 10 números e eu direi a média dos pares e dos ímpares') quantos_pares = 0 quantos_ímpares = 0 soma_pares = 0 soma_ímpares = 0 for i in range(1,11): número = int(input('[{0}]:'.format(i))) if número % 2 == 0: quantos_pares += 1 soma_pares += número else: quantos_ímpares += 1 soma_ímpares += número if quantos_pares >0: print('Média dos pares:', soma_pares/quantos_pares) else: print('Nenhum número par') if quantos_ímpares >0: print('Média dos ímpares:', soma_ímpares/quantos_ímpares) else: print('Nenhum número ímpar') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 17 5.7 print('Digite a idade e altura de 5 pessoas') quantos_abaixo_de_170 = 0 quantos_acima_dos_20 = 0 soma_idade_dos_abaixo_de_170 = 0 soma_altura_dos_acima_de_20 = 0 for i in range(1, 6): idade = int(input('Idade[{0}]:'.format(i))) altura = int(input('Altura[{0}]:'.format(i))) if altura <170: quantos_abaixo_de_170 += 1 soma_idade_dos_abaixo_de_170 += idade if idade >20: quantos_acima_dos_20 += 1 soma_altura_dos_acima_de_20 += altura if quantos_abaixo_de_170 >0: print('Idade média dos abaixo de 170 cm:', soma_idade_dos_abaixo_de_170/quantos_abaixo_de_170) else: print('Ninguém abaixo de 170 cm') if quantos_acima_dos_20 >0: print('Altura média dos acima de 20 anos:', soma_altura_dos_acima_de_20/quantos_acima_dos_20) else: print('Ninguém acima dos 20 anos') Obs.: como as linhas de dois "print" ficaram muito longas, adicionamos uma quebra logo após a vírgula endentando o argumento seguinte da função na mesma coluna do argumento que aparece na linha do "print". 5.8 número = int(input('Digite um número e direi se é perfeito: ')) soma_divisores = 0 for i in range(1, número//2+1): if número % i == 0: soma_divisores += i if soma_divisores == número: print('O número é PERFEITO!') else: print('O número não é perfeito...') Obs.: não é necessário verificar todos os números menores do que n para saber quais são os divisores de n. Basta verificar de 1 até n//2, porque nenhum número maior do que n//2 pode ser divisor de n. Por exemplo, no caso de 28, nenhum número maior do que 14 pode ser divisor de 28. Por isso é que o "range" vai de 1 a número//2+1. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 18 5.9 print('Número perfeitos entre 2 e dez mil:') for número in range(2, 10001): soma_divisores = 0 for i in range(1, número//2+1): if número % i == 0: soma_divisores += i if soma_divisores == número: print(número) Obs.: Se o programa estiver correto você vai visualizar a lista: 6, 28, 496 e 8128. 5.10 número = int(input('Digite um número e direi se é primo: ')) é_primo = True for possível_divisor in range(2, número//2+1): if número % possível_divisor == 0: é_primo = False print('Divisor:', possível_divisor) # linha adicionada if é_primo: print('é primo') else: print('não é primo') 5.11 número = int(input('Digite um número e direi se é triangular: ')) é_triangular = False for i in range(1, int(número**(1/3)+1)): if número == i*(i+1)*(i+2): é_triangular = True primeiro_fator = i if é_triangular: print('É triangular porque {0}={1}x{2}x{3}' .format(número, primeiro_fator, primeiro_fator+1, primeiro_fator+2)) else: print('Não é triangular') Obs.: uma das decisões que afetam a eficiência deste programa é decidir até quando executar o "for". Optamos por simplesmente limitar o "for" à parte inteira da raiz cúbica do número. Se você escreveu for i in range(1, número) ou for i in range(1, número//2+1), o programa funcionará da mesma forma, mas fará muitas comparações desnecessárias após examinar a raiz cúbica do número. Isso ocorre porque se o primeiro dos três números consecutivos for maior do que a raiz cúbica do número, o produto deles será sempre maior do que o número em questão já que, por definição, se "x" é a raiz cúbica de "y", então "y" é igual a x*x*x. Além disso, como os três fatores são consecutivos, basta guardar o primeiro, caso sejam encontrados e os outros dois podem ser produzidos somando-se 1 e 2 ao primeiro. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 19 5.12 print('Digite os valores dos lados e direi se é triângulo') lado1 = int(input('Lado 1:')) lado2 = int(input('Lado 2:')) lado3 = int(input('Lado 3:')) if lado1 >= lado2+lado3 \ or lado2 >= lado1+lado3 \ or lado3 >= lado1+lado2: print('Não é triângulo') else: print('É triângulo') Obs.: você certamente não precisou disso ao escrever seu programa, mas para mostrar o programa mais claramente no tamanho de texto adotado neste documento, tivemos que dividir a linha do "if" em três partes. O símbolo "\" colocado ao final da linha do "if" e da linha seguinte, indica que a condição continua na linha seguinte. Se tivéssemos simplesmente escrito as três linhas como abaixo, haveria erro de compilação, porque o interpretador não saberia que a segunda linha é continuação da primeira e a terceira continuação da segunda. if lado1 >= lado2+lado3 or lado2 >= lado1+lado3 or lado3 >= lado1+lado2: Em Python, o uso da barra invertida não é necessário quando você divide a linha após abrir, mas não fechar parênteses. Assim, por exemplo, uma lista de argumentos para uma função, que vai entre parênteses, pode ser escrita assim: função(arg1, arg2, arg3) Mas em outras situações, a barra invertida deve ser usada para indicar que a linha continua abaixo. Por exemplo: if a > b \ and (b > c or c > d): Observe que, neste exemplo, tivemos que usar a barra ao final da primeira linha, mas não usamos ao final da segunda linha, pois na segunda linha havia parênteses aberto. 5.13 print('Digite um ano e direi se é bissexto') ano = int(input('Ano:')) if ano % 4 == 0 and not ano % 100 == 0 or ano % 400 == 0: print('é bissexto') else: print('não é bissexto') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 20 Obs.: Pelas regras de precedência não é necessário usar parênteses na condição porque o "not" será executado primeiro, depois o "and" e finalmente o "or. 5.14 print('Digite um número e direi o mês correspondente') mês = int(input('Número do mês:')) if mês == 1: print('janeiro') elif mês == 2: print('fevereiro') elif mês == 3: print('março') elif mês == 4: print('abril') elif mês == 5: print('maio') elif mês == 6: print('junho') elif mês == 7: print('julho') elif mês == 8: print('agosto') elif mês == 9: print('setembro') elif mês == 10: print('outubro') elif mês == 11: print('novembro') elif mês == 12: print('dezembro') else: print('mês inválido') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 21 5.15 print('Eleições') abel = 0 beatriz = 0 caio = 0 dirce = 0 eustáquio = 0 brancos = 0 nulos = 0 for i in range(1, 11): print('Vote 11 Abel / 15 Beatriz / 21 Caio / ' '25 Dirce / 46 Eustáquio') voto = int(input('Voto[{0}]: '.format(i))) if voto == 11: abel += 1 elif voto == 15: beatriz += 1 elif voto == 21: caio += 1 elif voto == 25: dirce += 1 elif voto == 46: eustáquio += 1 elif voto == 0: brancos += 1 else: nulos += 1 print('Resultados:') print('Abel:', abel) print('Beatriz:', beatriz) print('Caio:', caio) print('Dirce:', dirce) print('Eustáquio:', eustáquio) print('Brancos:', brancos) print('Nulos:', nulos) Adiante no livro veremos a estrutura de "dicionário", quepermite resolver esse tipo de problema de forma bem mais elegante e simples. Observe também que no "print" subordinado ao "for" dividimos a literal em duas linhas, quando uma linha termina com aspas e a linha seguinte inicia com aspas isso equivale a concatenar as literais das duas linhas. Não há necessidade de usar a barra invertida aqui para indicar quebra de linha, nem se deve usar vírgula neste caso. Assim, por exemplo, o comando seguinte vai imprimir "abc": print('a' 'b' 'c') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 22 5.16 print('Classificação de nadadores') idade = int(input('Qual a idade do participante? ')) if idade <5: print('Classificação inválida') elif idade <= 7: print('Infantil A') elif idade <= 10: print('Infantil B') elif idade <= 14: print('Juvenil A') elif idade <= 17: print('Juvenil B') else: print('Adulto') Obs.: o uso do "elif" nos desobriga de verificar o limite inferior de cada intervalo. Assim, o programa testou no primeiro "if" se a idade era menor do que 5; então a condição do "elif" seguinte pode ser idade <= 7, porque verificar se ela é maior ou igual 5 seria redundante já que o teste anterior que verificava se era menor do que 5 já falhou e, portanto, já sabemos que a idade é maior ou igual a 5. 5.17 nota = float(input('Entre com a nota: ')) if nota <0: print('nota inválida') elif nota <7: print('Conceito E') elif nota <8: print('Conceito C') elif nota <9: print('Conceito B') elif nota <= 10: print('Conceito A') else: print('nota inválida') 5.18 print('Índice de Massa Corporal') peso = float(input('Entre com o peso (kg): ')) altura = float(input('Entre com a altura (m): ')) imc = peso/altura**2 print('IMC:', imc) if imc <18.5: print('Adulto com baixo peso') elif imc <25: print('Adulto com peso adequado') elif imc <30: print('Adulto com sobrepeso') else: print('Adulto com obesidade') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 23 5.19 print('Conversão de moedas') moeda = input('Qual a moeda? (e, d, m, a, l): ') reais = float(input('Quantos reais? ')) if reais <0: print('Quantidade inválida') elif moeda == 'e': print(reais*0.31, 'Euros') elif moeda == 'd': print(reais*0.42, 'Dólares') elif moeda == 'm': print(reais*5.55, 'Pesos Mexicanos') elif moeda == 'm': print(reais*2.84, 'Pesos Argentinos') elif reais == 'l': print(moeda*0,26, 'Libras') else: print('Moeda inválida') 5.20 Para resolver este problema, vamos incrementar o número de dias um por um verificando sempre se chegamos a um final de mês, situação na qual o dia retorna a 1 e o mês é incrementado, ou a um final de ano, situação na qual o dia e o mês retornam a 1 e o ano é incrementado. A primeira versão usa uma estrutura "if-elif-else": print('Cálculo de data futura') dia = int(input('Dia: ')) mês = int(input('Mês: ')) ano = int(input('Ano: ')) avanço = int(input('Quantos dias no futuro? ')) for i in range(avanço): if dia == 31: fim_do_mês = True elif dia == 30 and (mês == 4 or mês == 6 or mês == 9 or mês == 1): fim_do_mês = True elif dia == 29 and mês == 2 \ and (ano % 4 == 0 and not ano % 100 == 0 or ano % 400 == 0): fim_do_mês = True elif dia == 28 and mês == 2: fim_do_mês = True else: fim_do_mês = False if fim_do_mês and mês == 12: fim_do_ano = True else: fim_do_ano = False if fim_do_ano: ano += 1 mês = 1 dia = 1 elif fim_do_mês: mês += 1 dia = 1 else: dia += 1 print('{0}/{1}/{2}'.format(dia, mês, ano)) A segunda versão atribui a expressão lógica diretamente às variáveis booleanas sem usar "if- elif-else": Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 24 print('Cálculo de data futura') dia = int(input('Dia: ')) mês = int(input('Mês: ')) ano = int(input('Ano: ')) avanço = int(input('Quantos dias no futuro? ')) for i in range(avanço): fim_do_mês = dia == 31 \ or (dia == 30 and (mês == 4 or mês == 6 or mês == 9 or mês == 1)) \ or (dia == 29 and mês == 2 and (ano % 4 == 0 and not ano % 100 == 0 or ano % 400 == 0)) \ or (dia == 28 and mês == 2) fim_do_ano = fim_do_mês and mês == 12 if fim_do_ano: ano += 1 mês = 1 dia = 1 elif fim_do_mês: mês += 1 dia = 1 else: dia += 1 print('{0}/{1}/{2}'.format(dia, mês, ano)) Obs.: na expressão complexa que atribui valor booleano a "fim_do_mês", foi usada barra invertida apenas nos finais de linha onde não havia parêntese aberto. Nos casos em que a linha termina com parêntese aberto, mas não fechado, o uso da barra invertida é opcional e recomenda-se não usá-la. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 25 6 Repetição Condicionada 6.1 CARGA_MÁXIMA = 500 print('Simulador de elevador') peso_total = 0 while peso_total <= CARGA_MÁXIMA: peso_pessoa = int(input('Peso do passageiro: ')) peso_total += peso_pessoa print('Peso excedido') Obs.: 1. Neste exercício, ao contrário de muitos outros, não é necessário ler o peso do primeiro passageiro antes do "while", já que apenas o peso total é usado na condição do "while" e esse peso já foi inicializado com 0. Se a primeira pessoa, inclusive, pesasse mais de 500 kg, ela seria impedida de entrar no elevador. Por este motivo também a condição é testada na própria estrutura "while" e não em um "if" subordinado. 2. A constante "CARGA_MÁXIMA" foi usada aqui mesmo ela aparecendo uma única vez no programa. Considera-se uma boa prática de programação evitar usar aquilo que se conhece como "números mágicos" dentro de um programa. Neste caso até podemos saber que peso_total<= 500 estaria se referindo à carga máxima do elevador. Mas uma pessoa que fosse ler este código, sem ter sido seu programador, poderia ficar em dúvida sobre o real significado do valor 500 nesta expressão. Assim, a substituição do número mágico 500 pela constante "CARGA_MÁXIMA" deixa o código mais fácil de ler e entender. 6.2 print('Programa que inverte números') número = int(input('Digite um número: ')) invertido = 0 while número != 0: último_dígito = número % 10 número //= 10 invertido *= 10 invertido += último_dígito print('Número invertido:', invertido) Erro comum: • Durante o "while" você deve primeiro multiplicar o número invertido por 10 para só depois somar o último dígito a ele. Se você inverter a ordem dessas operações não vai obter o resultado desejado, mas um número 10 vezes maior do que o esperado. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 26 6.3 Primeira versão ainda incorreta: TAXA_MORTES = 0.06 TAXA_NASCIMENTOS = 0.01 POPULAÇÃO_MÍNIMA = 100000 print('Evolução da população de dodôs.') ano = 1600 população = 1000000 while população >= POPULAÇÃO_MÍNIMA: mortos = round(população*TAXA_MORTES) nascidos = round(população*TAXA_NASCIMENTOS) print('Ano: {0}. População: {1}. Nascidos: {2}. Mortos: {3}.' .format(ano, população, nascidos, mortos)) população += nascidos-mortos ano += 1 Erros comuns: • Ao invés de escrever população >= POPULAÇÃO_MÍNIMA ou mesmo população >= 100000, você poderia ter talvez escrito população >= população*0.1, pensando que assim estaria comparando a população atual de dodôs com 10% da população inicial. Mas note que você está comparando a variável "população" (o valor atual e não o inicial) com 10% dela própria; essa expressão nunca será falsa (qual número é maior que 10% dele próprio?) e seuprograma entrará em repetição infinita (sobre isso, veja Seção 6.3). Você tem que comparar a população atual, armazenada na variável "população" com 10% da população inicial, ou seja, 10% de um milhão, que é cem mil. • Dentre os comandos subordinados ao "while", a escolha da posição onde colocar o "print" é fundamental para os resultados saírem certos. Se tivesse colocado, por exemplo, mais no início da lista de comandos, os valores de "mortos" e "nascidos" estariam indefinidos quando o "print" fosse executado pela primeira vez. Assim o programa daria erro. Por outro lado, se tivesse colocado o "print" depois da atualização do ano e da população, o que seria impresso em cada linha referente a ano e população estaria errado, pois já estariam sendo mostrados dados do ano subsequente e não do ano atual. • O enunciado do problema exigia que se trabalhasse com os números de nascidos e mortos como inteiros. Se não houvesse a função "round" antes da atribuição dos valores dessas variáveis dentro do "while", o programa geraria resultados ligeiramente diferentes por causa de arredondamentos não feitos, mesmo que você usasse o "round" na hora de imprimir no final. • Finalmente, talvez a maior "pegadinha" do exercício é que o enunciado exige que sejam mostrados os resultados até que o tamanho da população seja menor do que 10% da original. Acontece que a população é atualizada no final da lista de comandos subordinados ao "while" e quando o programa retorna à linha do "while" para testar o tamanho da população, e ela está menor, a execução para e, dessa forma, não são calculados os mortos e nascidos nem impressa a última linha que deveria ser aquela com o ano em que a população se tornou menor do que 10% da original. Observe que se você fez um programa semelhante a esse, ele parou no ano de 1644 quando a Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 27 população ainda era superior a 100.000 dodôs. Isso nos leva a uma segunda versão deste programa na qual usamos uma estrutura while True com um "break" para parar a repetição logo depois de imprimir a população, caso ela tenha se tornado inferior ao mínimo estabelecido: Segunda versão: TAXA_MORTES = 0.06 TAXA_NASCIMENTOS = 0.01 POPULAÇÃO_MÍNIMA = 100000 print('Evolução da população de dodôs.') ano = 1600 população = 1000000 while True: mortos = round(população*TAXA_MORTES) nascidos = round(população*TAXA_NASCIMENTOS) print('Ano: {0}. População: {1}. Nascidos: {2}. Mortos: {3}.' .format(ano, população, nascidos, mortos)) if população < POPULAÇÃO_MÍNIMA: break população += nascidos-mortos ano += 1 Se você fez o exercício corretamente, a última linha que vai visualizar será: "Ano: 1645. População: 99441. Nascidos: 994. Mortos: 5966.". 6.4 VEL_TARTARUGA = 1 VEL_LEBRE = 10 print('A tartaruga e a lebre') tartaruga = 500 lebre = 0 minuto = 0 while tartaruga > lebre: tartaruga += VEL_TARTARUGA lebre += VEL_LEBRE minuto += 1 print('A lebre vai alcançar a tartaruga em {0} minutos' .format(minuto)) Observação: aqui, como em outros casos, abreviamos parte do nome das constantes. Por exemplo, usamos "VEL_TARTARUGA" ao invés de 'VELOCIDADE_DA_TARTARUGA". Ocorre que quando identificadores ficam muito longos eles começam a prejudicar, e não a ajudar, a legibilidade dos programas. Assim, quando o significado de uma sigla for absolutamente claro, recomenda-se usar abreviação para evitar que o identificador fique muito longo. Porém, deve- se evitar abreviações cujo significado não seja tão claro, como por exemplo "VT" e "VL" no lugar de "VEL_TARTARUGA" e "VEL_LEBRE". Se você fez tudo certo, deve ter visto o resultado "56". Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 28 6.5 TAXA_CARRO = 1.004 TAXA_APLICAÇÃO = 1.007 print('Compra de um carro') aplicação = 10000 preço_carro = 12000 meses = 0 while preço_carro > aplicação: meses += 1 aplicação *= TAXA_APLICAÇÃO preço_carro *= TAXA_CARRO print('Vai levar {0} meses para você poder comprar o carro' .format(meses)) Se fez tudo certo, descobriu que o resultado é "62". 6.6 print('Cartão x Salário') salário = 2000 dívida = 100 mês = 10 ano = 2016 while salário > dívida: mês += 1 if mês == 13: mês = 1 ano += 1 elif mês == 3: salário *= 1.05 dívida *= 1.15 print('A dívida será superior ao salário em {0}/{1}' .format(mês, ano)) Se você fez tudo certo, deve ter visualizado como resultado o mês de setembro de 2018. 6.7 print('A tartaruga e a lebre') tartaruga = int(input('Quantos metros de vantagem tem a tartaruga? ')) lebre = 0 velocidade_tartaruga = int(input('Velocidade da tartaruga: ')) velocidade_lebre = int(input('Velocidade da lebre: ')) minuto = 0 if velocidade_tartaruga >= velocidade_lebre and tartaruga > lebre: print('A lebre nunca vai alcançar a tartaruga') else: while tartaruga > lebre: tartaruga += velocidade_tartaruga lebre += velocidade_lebre minuto += 1 print('A lebre vai alcançar a tartaruga em {0} minutos' .format(minuto)) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 29 Erro comum: • Se você apenas comparar as velocidades sem verificar se a tartaruga realmente tem uma vantagem positiva sobre a lebre, o programa terá um comportamento estranho, pois se ambas saírem juntas (vantagem zero), a resposta deveria ser "0 minutos", pois elas já estão juntas; mas neste caso, se a velocidade da tartaruga for maior do que a da lebre, o programa estranhamente vai dizer que a tartaruga nunca vai alcançar a lebre, ou seja, o programa iria falhar no quarto teste proposto. 6.8 print('Divisão pelo método das subtrações sucessivas') dividendo = int(input('Dividendo: ')) divisor = int(input('Divisor: ')) contador = 0 if divisor == 0: print('Erro de divisão por zero') else: while dividendo >= divisor: dividendo -= divisor contador += 1 print('Divisão: ', contador) print('Resto:', dividendo) Erro comum: • Caso você tenha esquecido de testar se o divisor é zero, quando executar o último teste proposto seu programa vai entrar em repetição infinita. Como você pode ver, esse é o motivo de a divisão por zero ser indefinida. 6.9 soma = 0 quantidade = 0 while True: entrada = input('Nota: ') if entrada == '': break nota = float(entrada) if nota % 0.5 != 0: print('Nota inválida! Deve ser múltiplo de .5') elif nota <0 or nota >10: print('Nota inválida! Deve estar entre 0 e 10') else: soma += nota quantidade += 1 if quantidade == 0: print('Nenhuma nota foi digitada') else: média = round(2*soma//quantidade)/2 print('Média:', média) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 30 6.10 x = float(input('Valor de x:')) if -1 < x <1: série_anterior = x série_atual = x+x**3 i = 2 while série_anterior != série_atual: i += 2 série_anterior = série_atual termo = x**(2*i+1) série_atual += termo print('Somatório:', série_atual) else: print('Valor inválido. "x" deve estar entre -1 e 1') 6.11 from math import factorial PRECISÃO = 10 x = float(input('Seno de:')) série_anterior = 0 série_atual = x i = 1 sinal = 1 while round(série_anterior, PRECISÃO) != round(série_atual, PRECISÃO): i += 2 sinal *= -1 série_anterior = série_atual termo = sinal*(x**i / factorial(i)) série_atual += termo print('Seno de', x, ':', round(série_atual, PRECISÃO)) 6.12 from math import factorial x = float(input('Cosseno de:')) série_anterior = 1 série_atual = 1 - x**2/2 i = 2 sinal = -1 while série_anterior != série_atual:i += 2 sinal *= -1 série_anterior = série_atual termo = sinal*(x**i / factorial(i)) série_atual += termo print('Cosseno de', x, ':', série_atual) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 31 7 Funções 7.1 from random import randint print('Mega sena. Números sorteados:') for i in range(6): print(randint(1, 50)) Erros comuns: • Esquecer de importar "randint". • Confundir randint com randrange. Para gerar os números de 1 a 50, pode-se tanto usar randint(1, 50) como randrange(1, 51). 7.2 from random import randint print('Programa General') while True: quantos = int(input('Quantos dados devem ser jogados? ')) if quantos == 0: break elif 1 <= quantos <= 5: for i in range(quantos): print(randint(1, 6)) else: print('Quantidade inválida') print('Obrigado por jogar!') Obs.: Neste caso, se não usássemos a estrutura while True com break teríamos que ler a quantidade de dados tanto antes quando dentro do "while". 7.3 def fatorial(n): fat = 1 for i in range(2, n+1): fat *= i return fat print('Cálculo de combinações C(n, p)') n = int(input('Quantos elementos no total (n)?')) p = int(input('Quantos elementos em cada combinação (p)?')) c = fatorial(n)//(fatorial(p)*fatorial(n-p)) print('Combinações possíveis:', c) Erro comum: • Esquecer o uso de parênteses após o sinal de divisão na expressão fatorial(n)//(fatorial(p)*fatorial(n-p)). Como as multiplicações e divisões são Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 32 executadas da esquerda para a direita, a divisão seria feita antes da multiplicação e assim em vez de calcular 𝑛𝑛! 𝑝𝑝!(𝑛𝑛−𝑝𝑝) , o programa estaria calculando 𝑛𝑛! 𝑝𝑝! (𝑛𝑛 − 𝑝𝑝)!, e o resultado seria diferente do esperado. 7.4 def média_final(p1, p2, p3, p4, rec=-1): média_das_4 = (3*p1+4*p2+5*p3+6*p4)/18 if rec == -1: return round(média_das_4, 1) else: return round((média_das_4+rec)/2, 1) print(média_final(3, 2, 7, 6, 8)) print(média_final(6, 4, 9, 8)) print(média_final(8, 2, 1, 2, 7)) Obs.: O desafio deste exercício está em perceber que a nota da recuperação não tem, de fato, um valor default como 0 ou 10, porque isso afetaria o cálculo. Se ela não estiver presente na chamada da função, ela não deve ser usada. Assim, definimos como valor padrão da recuperação uma nota impossível, como -1. Dessa forma, a função consegue detectar que se o valor de "rec" for -1, esse valor não deve ser usado no cálculo da média, caso contrário, isso significa que a função foi chamada com um valor definido para "rec" e, portanto, ele deve ser usado. Também poderíamos usar o valor "None" no lugar de –1. "None" é um valor especial que em Python significa "nenhum" ou "nada": def média_final(p1, p2, p3, p4, rec=None): média_das_4 = (3*p1 + 4*p2 + 5*p3 + 6*p4)/18 if rec == None: return round(média_das_4, 1) else: return round((média_das_4+rec)/2, 1) 7.5 def tribonacci(n): if n == 1 or n == 2: return 1 elif n ==3: return 2 else: return tribonacci(n-1)+tribonacci(n-2)+tribonacci(n-3) def mdc(a, b): if b == 0: return a else: return mdc(b, a % b) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 33 7.6 def mdc(x, y): if y == 0: return x else: return mdc(y, x % y) 7.7 def triangulo_invertido(n): if n == 1: print('$') else: print('$'*n) triangulo_invertido(n-1) 7.8 def triângulo_rec(n, original): if n == 1: print('$'*original) else: print('$'*(original-n+1)) triângulo_rec(n-1, original) def triângulo(n): triângulo_rec(n, n) Obs.: Esta função é um pouco mais difícil de conceber porque ela deve guardar tanto o valor original da primeira chamada quanto o valor que está sendo diminuído a cada chamada recursiva. Assim, uma segunda função, chamada "triângulo_rec" é definida com dois parâmetros. A cada chamada recursiva, o primeiro parâmetro é decrementado (subtraído 1) e o segundo é passado exatamente como recebido. Assim, se você chamar triângulo(5), vai ativar as seguintes chamadas recursivas: triângulo_rec(5, 5), triângulo_rec(4, 5),triângulo_rec(3, 5),triângulo_rec(2, 5) etriângulo_rec(1, 5). 7.9 def fatorial(n): fat = 1 for i in range(2, n+1): fat *= i return fat def arranjo(n, p): a = 1 for i in range(n-p+1, n+1): a *= i return a def combinação (n, p): return arranjo(n,p) // fatorial(p) print('Cálculo de combinações C(n, p)') n = int(input('Quantos elementos no total (n)?')) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 34 p = int(input('Quantos elementos em cada combinação (p)?')) print('Combinações possíveis:', combinação(n,p)) 7.10 def inverte(n): invertido = 0 while n != 0: último_dígito = n % 10 n //= 10 invertido *= 10 invertido += último_dígito return invertido Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 35 8 Desenvolvimento Dirigido por Teste 8.1 def divisão(x, y): try: return x/y except ZeroDivisionError: return None print(divisão(12, 10)) print(divisão(3, 1)) print(divisão(14, 0)) 8.2 A resposta que já consta na continuação do texto é: def mmc(x, y): candidato = x while not(candidato % x == 0 and candidato % y == 0): candidato += 1 return candidato Existe, porém, um algoritmo mais eficiente para calcular o "mmc". Só que ele usa a função "mdc" (máximo divisor comum), que já vimos no capítulo anterior. A ideia é a seguinte, se "y" for zero, o "mmc" de "x" e "y" é "x"; senão é x*y/mdc(x, y): def mmc(x, y): if y == 0: return x else: return x*y/mdc(x, y) 8.3 def analisa(idade): assert isinstance(idade, int) assert 0 <= idade <= 120 if idade <18: return 'menor' elif idade <= 64: return 'adulto' else: return 'idoso' 8.4 Aqui, a análise de valor limítrofe aplica-se entre a classe dos números negativos, para os quais o fatorial não é definido, e dos não negativos. Assim, devemos fazer um teste de falha com -1 e um teste válido com 0. Fora isso, podemos testar valores dentro das duas classes como -25 e 5. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 36 Devemos fazer também testes de falha para números de ponto flutuante e valores não numéricos. # Valores não inteiros try: fatorial(0.35) raise Exception except AssertionError: pass try: fatorial('abc') raise Exception except AssertionError: pass # Negativos try: fatorial(-25) raise Exception except AssertionError: pass try: fatorial(-1) # valor limítrofe raise Exception except AssertionError: pass # Válidos assert(fatorial(0) == 1) #valor limítrofe assert(fatorial(5) == 120) print('Todos os testes finalizados com sucesso') O programa original vai falhar já no primeiro teste. Para corrigi-lo basta adicionar uma linha com o comando "assert" como mostrado abaixo: def fatorial(n): assert isinstance(n, int) and n >= 0 fat = 1 for i in range(2, n+1): fat *= i return fat 8.5 Neste exemplo, as classes de equivalência não se aplicam diretamente ao valor do argumento, mas aos resultados que a função produz. Assim, devemos ver como o programa se comporta quando zero notas são informadas – um caso limite – ou quando resultados são produzidos com apenas uma nota de alguns tipos ou com notas repetidas. O teste com o valor 186 (uma nota de cada valor: 100+50+20+10+5+1) também é importante porque ele exercita todas as linhas do programa, já que com estevalor todas as condições do "if-elif-else" são executadas. Este teste garante assim a cobertura do código. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 37 def quantas_notas(reais): assert isinstance(reais, int) and reais >= 0 print(reais) quantidade = 0 while reais != 0: if reais >= 100: reais -= 100 quantidade += 1 elif reais >= 50: reais -= 50 quantidade += 1 elif reais >= 20: reais -= 20 quantidade += 1 elif reais >= 10: reais -= 10 quantidade += 1 elif reais >= 5: reais -= 5 quantidade += 1 else: reais -= 1 quantidade += 1 return quantidade # Válidos assert quantas_notas(0) == 0 # zero notas - valor limítrofe assert quantas_notas(100) == 1 # uma nota apenas assert quantas_notas(51) == 2 # duas notas não repetidas assert quantas_notas(44) == 6 # notas repetidas assert quantas_notas(186) == 6 # uma nota de cada # negativo try: quantas_notas(-1) # valor limítrofe raise Exception except AssertionError: pass # não inteiro try: quantas_notas(1.5) raise Exception except AssertionError: pass # não numérico try: quantas_notas('abc') raise Exception except AssertionError: pass print('Todos os testes finalizados com sucesso') Interessante observar que eu mesmo desenvolvia solução para este exercício, conforme proposto, usando TDD. A primeira versão da função que implementei falhou nos testes porque usei ">" em vez de ">=", um erro MUITO comum. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 38 8.6 from math import sqrt def area(a, b, c): assert (isinstance(a, int) or isinstance(a, float)) and a >0 assert (isinstance(b, int) or isinstance(b, float)) and b >0 assert (isinstance(c, int) or isinstance(c, float)) and c >0 assert a < b+c and b < a+c and c < a+b p = (a+b+c)/2 return sqrt(p*(p-a)*(p-b)*(p-c)) # Válidos CASAS_DECIMAIS = 3 assert round(area(5, 5, 5), CASAS_DECIMAIS) == 10.825 # equilátero assert round(area(5, 5, 2), CASAS_DECIMAIS) == 4.899 # isósceles acutângulo assert round(area(5, 5, 8.5), CASAS_DECIMAIS) == 11.194 # isósceles obtusângulo assert round(area(5, 3, 2.1), CASAS_DECIMAIS) == 1.236 # escaleno acutângulo assert round(area(5.1, 3.5, 8), CASAS_DECIMAIS) == 6.184 # escaleno obtusângulo assert round(area(3, 4, 5), CASAS_DECIMAIS) == 6 # reto # valores negativos try: area(-1, -3, -4) raise Exception except AssertionError: pass # não numérico try: area('a', 'b', 'c') raise Exception except AssertionError: pass # não forma triângulo - valor limítrofe onde c = a+b try: area(1, 2, 3) raise Exception except AssertionError: pass # não forma triângulo try: area(10, 2, 5) raise Exception except AssertionError: pass print('Todos os testes finalizados com sucesso') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 39 8.7 def converte(temperatura, escala): assert isinstance(temperatura, int) or isinstance(temperatura, float) assert escala == 'C' or escala == 'F' if escala == 'C': assert temperatura >= -273.15 return 1.8*temperatura + 32 if escala == 'F': assert temperatura >= -459.67 return (temperatura-32)/1.8 # Válidos CASAS_DECIMAIS = 2 assert round(converte(-273.15, 'C'), CASAS_DECIMAIS) == -459.67 #valor limítrofe assert round(converte(-459.67, 'F'), CASAS_DECIMAIS) == -273.15 #valor limítrofe assert round(converte(0, 'C'), CASAS_DECIMAIS) == 32 assert round(converte(0, 'F'), CASAS_DECIMAIS) == -17.78 assert round(converte(15, 'C'), CASAS_DECIMAIS) == 59 assert round(converte(38, 'F'), CASAS_DECIMAIS) == 3.33 # abaixo do zero absoluto - valor limítrofe try: converte(-273.16, 'C') raise Exception except AssertionError: pass try: converte(-459.68, 'F') raise Exception except AssertionError: pass # temperatura não numérica try: converte('C', 'F') raise Exception except AssertionError: pass # escala inválida try: converte(10, 'K') raise Exception except AssertionError: pass print('Todos os testes finalizados com sucesso') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 40 9 Estruturas de Dados Primitivas 9.1 from string import ascii_lowercase def letras_disponíveis(usadas): assert isinstance(usadas, list) for elemento in usadas: assert type(elemento) == str \ and len(elemento) == 1 \ and 'a' <= elemento <= 'z' restantes = list(ascii_lowercase) for letra in usadas: restantes.remove(letra) return restantes # classes válidas assert letras_disponíveis([]) == list(ascii_lowercase) assert letras_disponíveis(list(ascii_lowercase)) == [] assert letras_disponíveis(['a', 'g', 'b', 'k', 'y']) == \ ['c', 'd', 'e', 'f', 'h', 'i', 'j', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'z'] # classes inválidas try: letras_disponíveis(1) # não é lista raise Exception except AssertionError: pass try: letras_disponíveis([1, 2]) # elementos que não são letras raise Exception except AssertionError: pass try: letras_disponíveis(['$']) # string que não é letra raise Exception except AssertionError: pass try: letras_disponíveis(['asd']) # string com mais de 1 caractere raise Exception except AssertionError: pass print('Todos testes concluídos com sucesso!') 9.2 Minha primeira versão deste programa falhou no teste em que todas as letras da palavra são informadas porque usei um if letra in restantes em vez de while letra in restantes. Neste caso, como a palavra tem a letra "a" repetida 3 vezes, o programa só removeu uma ocorrência do "a" e assim "restantes" ficou com ['a', 'a']. Quando percebi isso, simplesmente troquei o "if" por "while" e o programa funcionou sem erros. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 41 def adivinhou_palavra(letras, palavra): assert isinstance(letras, list) for elemento in letras: assert isinstance(elemento, str) \ and len(elemento) == 1 \ and 'a'<= elemento <= 'z' assert isinstance(palavra, str) for simbolo in palavra: assert 'a'<= simbolo <= 'z' restantes = list(palavra) for letra in letras: while letra in restantes: restantes.remove(letra) return len(restantes) == 0 # classes válidas assert adivinhou_palavra([], 'palavra') == False # nenhuma letra no palpite assert adivinhou_palavra(['a'], 'palavra') == False # uma letra no palpite assert adivinhou_palavra(['a', 'p', 'l', 'v', 'r'], 'palavra') == True assert adivinhou_palavra(['a', 'f', 'p', 'l', 'v', 'r', 'q'], 'palavra') == True # classes inválidas try: adivinhou_palavra(1, 'palavra') # primeiro argumento não é lista raise Exception except AssertionError: pass try: adivinhou_palavra([1, 2], 'palavra') # lista com elementos que não são letras raise Exception except AssertionError: pass try: adivinhou_palavra(['$'], 'palavra') # string que não é letra raise Exception except AssertionError: pass try: adivinhou_palavra(['asd'], 'palavra') # string com mais de 1 caracter raise Exception except AssertionError: pass try: adivinhou_palavra(['a'], 'palavra1') # palavra com símbolo não alfabético raise Exception except AssertionError: pass try: adivinhou_palavra(['a'], 5) # palavra não é string raise Exception except AssertionError: pass print('Todos testes concluídos com sucesso!') 9.3 Aqui, usamos uma abordagem nova para percorrer a lista que produz um algoritmo mais simples do que outros que seriam possíveis. Inicializamoso "elemento_anterior", que dentro do "for" será comparado com o elemento da iteração com "None", que literalmente significa "nenhum". Assim, quando o primeiro "if" subordinado ao "for" verificar se o elemento atual é igual ao anterior, a comparação dará "False" e essa é exatamente a intenção, já que naquele momento não existe elemento anterior. Se o "elemento_anterior" não tivesse sido inicializado dessa forma, teríamos que primeiro tratar a situação especial do primeiro elemento, antes do "for", e em seguida fazer o "for" variar a partir do segundo elemento da lista. Isso causaria a repetição de código que está dentro do "for" e teria que aparecer também antes dele. Mas atenção: o "None" não pode ser comparado a números com os operadores ">", "<", "<=" e ">=", mas apenas com "==" e "!=". Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 42 def maior_sequencia(lista): assert isinstance(lista, list) for elemento in lista: assert isinstance(elemento, int) elemento_anterior = None contador = 1 maior_contador = 0 for elemento in lista: if elemento == elemento_anterior: contador += 1 else: contador = 1 if contador > maior_contador: maior_contador = contador elemento_anterior = elemento return maior_contador # válidos assert maior_sequencia([]) == 0 assert maior_sequencia([5]) == 1 assert maior_sequencia([5, 6, 7]) == 1 assert maior_sequencia([5, 5, 6]) == 2 assert maior_sequencia([5, 6, 5]) == 1 assert maior_sequencia([3, 4, 1, 1, 1, 1, 1, 3, 3, 3, 3]) == 5 #inválidos try: maior_sequencia(5) raise Exception except AssertionError: pass try: maior_sequencia([3, 4, 'abc']) raise Exception except AssertionError: pass print('Todos os testes concluídos com sucesso!') 9.4 Aqui, a técnica usada para saber se uma lista é permutação da outra foi criar uma cópia da segunda lista, para evitar efeitos colaterais e fazer o seguinte: para cada elemento da primeira lista, se ele estiver na segunda lista, removê-lo da segunda lista; se ele não estiver, retornar "False". Se, no final, a segunda lista estiver vazia, retornar "True"; senão, retornar "False". Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 43 def é_permutação(l1, l2_original): assert isinstance(l1, list) and isinstance(l2_original, list) l2 = l2_original.copy() for elemento in l1: if elemento in l2: l2.remove(elemento) else: return False return l2 == [] assert é_permutação([1, 2, 3, 3, 8, 9, 0, 2], [0, 1, 2, 2, 3, 3, 8, 9]) assert not é_permutação([0, 1, 2, 2, 3, 3, 3, 8], [0, 1, 2, 2, 3, 3, 8, 9]) assert not é_permutação([0, 1, 2, 2, 3, 3, 8, 9], [0, 1, 2, 3, 8, 9]) assert not é_permutação([], [0, 1, 2, 3, 8, 9]) assert not é_permutação([0, 1, 2, 2, 3, 3, 8, 9], []) assert é_permutação([], []) try: é_permutação(3, [0, 1, 2, 2, 3, 3, 8, 9]) raise Exception except AssertionError: pass try: é_permutação([0, 1, 2, 2, 3, 3, 8, 9], 4) raise Exception except AssertionError: pass print('Todos testes executados com sucesso') 9.5 Este programa é apenas uma variação da Mega-Sena, na qual a quantidade de números e o intervalo de geração são alterados. from random import randint print('Loteria') sorteados = [] while len(sorteados) <25: sorteado = randint(1, 40) for posição in range(len(sorteados)): número = sorteados[posição] if número == sorteado: break if número > sorteado: sorteados.insert(posição, sorteado) break else: sorteados.append(sorteado) print(sorteados) Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 44 9.6 Os testes a serem executados para testar esta função são mostrados abaixo. Em seguida, duas estratégias de resolução do problema são mostradas e discutidas: assert união([3,7,1,9,5,6,4], [1,7,5,8,2,0,3]) \ == [0,1,2,3,4,5,6,7,8,9] assert união([1, 1, 1, 1, 1], [2, 0, 2]) == [0, 1, 2] assert união([1, 2, 3], [4]) == [1, 2, 3, 4] assert união([1, 2, 3], []) == [1, 2, 3] assert união([1], [1]) == [1] assert união([], []) == [] try: união(1, [1]) # argumento 1 não é lista raise Exception except AssertionError: pass try: união([1], 'abc') # argumento 2 não é lista raise Exception except AssertionError: pass try: união([1, 'abc'], [1]) # lista com valor não numérico raise Exception except AssertionError: pass try: união([1], ['abc', 2]) # lista com valor não numérico raise Exception except AssertionError: pass print('Todos testes executados com sucesso') A primeira estratégia, a mais óbvia, consiste em usar uma abordagem semelhante à que usamos no problema da Mega-Sena. Vamos criar uma nova lista para ser a ordenada. Depois percorrer cada lista original e, para cada elemento, procuramos na lista ordenada se ele já está lá. Se estiver, ignoramos o elemento e vamos para o próximo; se encontrarmos um elemento maior do que ele, nós o inserimos antes deste elemento e vamos para o próximo; senão nós o inserimos no final da lista: Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 45 def união(l1, l2): assert isinstance(l1, list) assert isinstance(l2, list) for elemento in l1: assert type(elemento) in [float, int] for elemento in l2: assert type(elemento) in [float, int] nova_lista = [] for elemento in l1: for posição in range(len(nova_lista)): if nova_lista[posição] == elemento: break elif nova_lista[posição] > elemento: nova_lista.insert(posição, elemento) break else: nova_lista.append(elemento) for elemento in l2: for posição in range(len(nova_lista)): if nova_lista[posição] == elemento: break elif nova_lista[posição] > elemento: nova_lista.insert(posição, elemento) break else: nova_lista.append(elemento) return nova_lista O problema com este algoritmo é que o código subordinado a for elemento in l1 e for elemento in l2 é idêntico, aparecendo assim duas vezes repetido na função. Isso não é uma boa prática de programação. Código duplicado é normalmente fonte de dor de cabeça. Assim, nossa segunda abordagem consiste em, antes de executar este código uma única vez, produzir uma lista que seja a união das duas listas originais, fazendo união = l1+l2. Inclusive, podemos fazer isso já durante as precondições (linhas com "assert"), logo depois de nos certificarmos que os dois parâmetros são listas, para simplificar o código ali também: def união(l1, l2): assert isinstance(l1, list) assert isinstance(l2, list) união = l1+l2 for elemento in união: assert type(elemento) in [float, int] nova_lista = [] for elemento in união: for posição in range(len(nova_lista)): if nova_lista[posição] == elemento: break elif nova_lista[posição] > elemento: nova_lista.insert(posição, elemento) break else: nova_lista.append(elemento) return nova_lista Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 46 9.7 def soma(t, u): assert isinstance(t, tuple) and len(t) == 2 assert isinstance(u, tuple) and len(u) == 2 assert isinstance(t[0], int) or isinstance(t[0], float) assert isinstance(u[0], int) or isinstance(u[0], float) assert t[1] == 'C' or t[1] == 'F' assert u[1] == 'C' or u[1] == 'F' if t[1] == u[1]: return (t[0]+u[0], t[1]) else: if t[1] == 'F': t_convertido = (t[0]-32)/1.8 else: t_convertido = 1.8*t[0]+32 return(t_convertido+u[0], u[1]) # válidos assert soma((12, 'C'), (20, 'C')) == (32, 'C') assert soma((-12, 'F'), (20, 'F')) == (8, 'F') assert soma((12, 'C'), (20, 'F')) == (73.6, 'F') assert soma((32, 'F'), (-20, 'C')) == (-20, 'C') # inválidos try: soma(5, (1, 'F')) raise Exception except AssertionError: pass try: soma((1, 'F'), 'C') raise Exception except AssertionError: pass try: soma(('C', 'C'), (1, 'F')) raise Exception except AssertionError: pass try: soma((5, 'C'), ('1', 'F')) raise Exception except AssertionError: pass try: soma((5, 'K'), ('1', 'F')) raise Exception except AssertionError: pass try: soma((5, 'C'), ('1', 'K')) raise Exception except AssertionError: pass print('Todos os testes executados com sucesso!') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 47 9.8 def menos_provável(partículas): assert isinstance(partículas, dict) for chave in partículas: valor = partículas[chave] assert type(valor) in [int, float] assert 1 == sum(list(partículas.values())) chave_menor = [] valor_menor = None for chave, valor in partículas.items(): if valor_menor == None: valor_menor = valor chave_menor.append(chave) elif valor == valor_menor: chave_menor.append(chave) elif valor < valor_menor: chave_menor = [chave] valor_menor = valor return chave_menor # classes válidas dic = {'neutron': 0.55, 'proton': 0.21, 'meson': 0.03, 'muon': 0.07, 'neutrino': 0.14} assert menos_provável(dic) == ['meson'] dic = {'neutron': 0.55, 'proton': 0.21, 'meson': 0.03, 'muon': 0.03, 'neutrino': 0.18} resultado = menos_provável(dic) assert resultado == ['meson', 'muon'] \ or resultado == ['muon', 'meson'] dic = {'neutron': 0.55, 'proton': 0.03, 'meson': 0.21, 'muon': 0.03, 'neutrino': 0.18} resultado = menos_provável(dic) assert resultado == ['proton', 'muon'] \ or resultado == ['muon', 'proton'] dic = {'neutron': 0.55, 'proton': 0.21, 'meson': 0.21, 'muon': 0.03, 'neutrino': 0.0} resultado = menos_provável(dic) assert resultado == ['neutrino'] \ or resultado == ['neutrino'] # classes inválidas try: menos_provável([5, 3]) # não é dicionário raise Exception except AssertionError: pass try: menos_provável({'meson': 'abc'}) # valor não numérico raise Exception except AssertionError: pass try: menos_provável({'meson': 0.5, 'proton': 0.4}) # prob. total < 1 raise Exception except AssertionError: pass try: menos_provável({'meson': 0.5, 'proton': 0.6}) # prob. total > 1 raise Exception except AssertionError: pass print('Todos testes executados com sucesso') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 48 10 Estruturas de Dados Derivadas 10.1 def analisa(expressão): assert isinstance(expressão, str) pilha = [] caracteres = list(expressão) for símbolo in caracteres: if símbolo == '(': pilha.append(símbolo) elif símbolo == ')': if pilha and pilha[-1] == '(': pilha.pop() else: return False return pilha == [] assert analisa('a+b') assert not analisa('(a+b') assert analisa('(a+(b+c-(a)*e+(a)))') assert not analisa('(a+(b+c-a)*e+(a)))') assert not analisa(')+a+(') try: analisa(5) raise Exception except AssertionError: pass print('Sucesso!') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 49 10.2 def é_palíndromo(expressão): assert isinstance(expressão, str) tamanho = len(expressão) assert tamanho >= 1 pilha = [] caracteres = list(expressão) posição = tamanho//2 for i in range(posição): pilha.append(caracteres[i]) if tamanho % 2 != 0: # se a expressão tem tamanho ímpar posição += 1 # despreza o caractere central for i in range(posição, tamanho): if caracteres[i] != pilha.pop(): return False return True assert é_palíndromo('x') assert é_palíndromo('ana') assert é_palíndromo('osso') assert é_palíndromo('abcdcba') assert not é_palíndromo('casa') assert not é_palíndromo('quadros') try: é_palíndromo(5) raise Exception except AssertionError: pass try: é_palíndromo('') raise Exception except AssertionError: pass print('Sucesso!') 10.3 N = 4 a = [] for i in range(N, 0, -1): a.append(i) b = [] c = [] print(a, b, c) for i in range(1, 2**N): if i % 3 == 1: origem = a destino = b elif i % 3 == 2: origem = a destino = c else: origem = b destino = c if not origem or (destino and destino[-1] < origem[-1]): origem, destino = destino, origem Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 50 destino.append(origem.pop()) print(i, a, b, c) 10.4 fila = [] prioridades = [] while True: comando = input('Digite (c)hegada (a)tender (p)arar: ') if comando == 'c': nome = input('Digite o nome do paciente: ') if input('Prioridade (s/n)? ') == 's': prioridades.append(nome) else: fila.append(nome) elif comando == 'a': if prioridades: nome = prioridades.pop(0) print(nome) elif fila: nome = fila.pop(0) print(nome) else: print('Não há ninguém na fila') elif comando == 'p': if not fila: break else: print('A fila ainda contém os seguintes nomes:') for nome in fila: print(nome) opção = input('Parar? (s)im (n)ão: ') if opção == 's': break else: print('Comando desconhecido!') Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 51 10.5 from random import random TEMPO_SIMULAÇÃO = 1000 PROBABILIDADE_CARRO = 0.05 TEMPO_SEMÁFORO = 50 via1 = [] via2 = [] carro = 1 carros_parados = 0 semáforo = True for i in range(1, TEMPO_SIMULAÇÃO+1): if random() < PROBABILIDADE_CARRO: via1.append(carro) carro += 1 if random() < PROBABILIDADE_CARRO: via2.append(carro) carro += 1 print(i, via1, via2) if i % TEMPO_SEMÁFORO == 0: semáforo = not semáforo print('Semáforo mudou') if semáforo: if via1: via1.pop(0) else: if via2: via2.pop(0) carros_parados += len(via1) + len(via2) assert TEMPO_SIMULAÇÃO != 0 print('Número médio de carros parados:', carros_parados/TEMPO_SIMULAÇÃO) Ao executar o programa algumas vezes, obtivemos resultados variando entre 0.9 e 1.6. Executando novamente com probabilidades de 70%, os resultados variaram entre 190 e 290 automóveis parados em média, ou seja, um grande congestionamento. Introdução a Algoritmos e Programação com Python - Raul Sidnei Wazlawick ©Elsevier, 2017 52 10.6 def é_quadrado_mágico(matriz): # valor da linha 0 - padrão de comparação padrão = sum(matriz[0]) # verifica linhas for linha in matriz: if padrão != sum(linha): return False # verifica colunas for coluna in range(len(matriz)): valor = 0 for linha in range(len(matriz)): valor += matriz[linha][coluna] if valor != padrão: return False # verifica diagonais valor1 = 0 valor2 = 0 for linha in range(len(matriz)): valor1 += matriz[linha][linha] # diagonal principal valor2 += matriz[linha][len(matriz)-linha-1] # diag. secundária if valor1 != padrão or valor2 != padrão: return False return True quadrado_mágico = [[2, 7, 6], [9, 5, 1], [4,
Compartilhar