4  Controle de fluxo e iteração

4.1 Controle de Fluxo

Na programação, é muito comum precisarmos que determinados blocos de código sejam executados apenas quando uma condição específica for atendida. Para lidar com esse tipo de situação, o Python oferece mecanismos para controlar o fluxo de execução do programa, chamados de estruturas condicionais. As principais estruturas condicionais em Python são: if, elif, else.

Essas estruturas permitem que o programa tome decisões durante a execução, escolhendo diferentes caminhos dependendo das condições definidas. Podemos imaginar isso como uma encruzilhada: dependendo da condição avaliada, o programa pode seguir por um caminho ou por outro. Para que essas decisões sejam possíveis, o Python utiliza expressões booleanas e operadores de comparação e lógicos, que permitem verificar relações entre valores.

4.1.1 Expressões Booleanas

Uma expressão booleana é qualquer expressão que resulta em um dos dois valores lógicos possíveis: True (verdadeiro) ou False (falso). Essas expressões são essenciais para o controle de fluxo, pois determinam quais blocos de código serão executados.

Expressões booleanas geralmente envolvem operadores de comparação (como ==, !=, >, <) ou operadores lógicos (and, or, not). Essas expressões podem ser usadas diretamente em estruturas condicionais para tomar decisões no programa.

4.1.1.1 Operadores de Comparação

Os operadores de comparação são usados para comparar dois valores. Esses operadores comparam um valor do lado esquerdo com um valor do lado direito, retornando True ou False.

Operador Nome Função
== Igual a Verifica se um valor é igual ao outro
!= Diferente de Verifica se um valor é diferente ao outro
> Maior que Verifica se um valor é maior que outro
>= Maior ou igual Verifica se um valor é maior ou igual ao outro
< Menor que Verifica se um valor é menor que outro
<= Menor ou igual Verifica se um valor é menor ou igual ao outro

4.1.1.2 Operadores Lógicos

Além dos operadores de comparação, Python também possui operadores lógicos que permitem combinar múltiplas condições. Os principais são:

Operador Definição
and Retorna True se ambas as afirmações forem verdadeiras
or Retorna True se uma das afirmações for verdadeira
not retorna False se o resultado for verdadeiro

4.1.1.3 Operadores de Identidade

Esses operadores são utilizados para comparar objetos, verificando se duas variáveis referenciam o mesmo objeto na memória.

Operador Definição
is Retorna True se ambas as variáveis são o mesmo objeto
is not Retorna True se ambas as variáveis não forem o mesmo objeto

4.1.1.4 Operadores de Associação

Esses operadores verificam se um elemento pertence ou não a uma estrutura de dados, como listas, strings ou tuplas.

Operador Definição
in Retorna True caso o valor seja encontrado na sequência
not in Retorna True caso o valor não seja encontrado na sequência

4.1.1.5 Operadores de Atribuição

Esses operadores são utilizados para atribuir valores a variáveis e também para atualizar valores existentes.

Operador Equivalente a
= x = 1
+= x = x + 1
-= x = x - 1
*= x = x * 1
/= x = x / 1
%= x = x % 1

4.1.2 Instruções if

Para escrever programas úteis, quase sempre precisamos da capacidade de verificar condições e alterar o comportamento do programa de acordo com elas. As instruções condicionais nos dão essa capacidade. A forma mais simples é a instrução if. Vamos começar com o seguinte exemplo:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x < y:
    print("x é menor que y")

Observe como o programa recebe a entrada do usuário para x e y, convertendo esses valores para inteiros e armazenando-os nas respectivas variáveis x e y. Em seguida, a instrução if compara x e y. Se a condição x < y for satisfeita, o comando print é executado.

As instruções if utilizam valores booleanos (True ou False) para decidir se um bloco de código deve ou não ser executado. Se a comparação x < y for True, o interpretador executa o bloco de código indentado.

4.1.3 Controle de Fluxo: elif e else

Uma instrução if pode ter uma segunda parte, chamada de cláusula else. A sintaxe é a seguinte:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x < y:
    print("x é menor que y")
if x > y:
    print("x é maior que y")
if x == y:
    print("x é igual a y")

Observe que você está fornecendo uma série de instruções if. Primeiro, a primeira instrução if é avaliada. Em seguida, a segunda instrução if realiza sua avaliação. Por fim, a última instrução if também é avaliada. Esse fluxo de decisões é chamado de controle de fluxo.

Nosso código pode ser representado da seguinte forma:

Controle de fluxo

Este programa pode ser melhorado evitando fazer três verificações consecutivas. Afinal, nem todas as três condições podem ser verdadeiras ao mesmo tempo!

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x < y:
    print("x é menor que y")
elif x > y:
    print("x é maior que y")
elif x == y:
    print("x é igual a y")

A palavra-chave elif é uma abreviação para else if. Observe como o uso de elif permite que o programa faça menos verificações. Primeiro, a instrução if é avaliada. Se essa condição for verdadeira, todas as instruções elif não serão executadas.

No entanto, se a instrução if for avaliada e considerada falsa, o primeiro elif será avaliado. Se essa condição for verdadeira, a verificação final não será executada. Nosso código pode ser representado da seguinte forma:

Controle de fluxo

Embora o seu computador talvez não perceba diferença de velocidade entre o primeiro programa e essa versão revisada, considere que um servidor online executando bilhões ou trilhões desse tipo de cálculo todos os dias pode ser impactado até mesmo por uma pequena decisão de programação.

Há ainda uma melhoria final que podemos fazer no programa. Observe que, logicamente, a verificação elif x == y não é necessária. Afinal, se x não é menor que y e x não é maior que y, então x necessariamente deve ser igual a y. Portanto, não precisamos executar elif x == y. Em vez disso, podemos criar um resultado padrão, que será executado caso nenhuma das condições anteriores seja verdadeira, utilizando a instrução else. Podemos revisar o código da seguinte forma:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x < y:
    print("x é menor que y")
elif x > y:
    print("x é maior que y")
else:
    print("x é igual a y")

Observe como a complexidade relativa deste programa diminuiu após nossa revisão. Nosso código pode ser representado da seguinte forma:

Controle de fluxo

4.1.4 Operador or

O operador or permite que o programa tome uma decisão entre uma ou mais alternativas. Por exemplo, podemos modificar ainda mais o nosso programa da seguinte forma:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x < y or x > y:
    print("x não é igual a y")
else:
    print("x é igual a y")

Observe que o resultado do programa permanece o mesmo, mas a complexidade foi reduzida. Com isso, a eficiência do código aumenta. Neste ponto, nosso código já está bastante bom. No entanto, será que o design do programa ainda pode ser melhorado? Podemos modificar nosso código da seguinte forma:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x != y:
    print("x não é igual a y")
else:
    print("x é igual a y")

Observe que removemos completamente o operador or e simplesmente perguntamos: “x é diferente de y?”. Assim, fazemos apenas uma única verificação, o que torna o código mais eficiente. Para fins de ilustração, também poderíamos modificar nosso código da seguinte forma:

x = int(input("Qual o valor de x? "))
y = int(input("Qual o valor de y? "))

if x == y:
    print("x é igual a y")
else:
    print("x não é igual a y")

Observe que o operador == verifica se os valores à esquerda e à direita são iguais entre si. O uso de dois sinais de igual é muito importante. Se você usar apenas um sinal de igual (=), o interpretador provavelmente retornará um erro. Nosso código pode ser ilustrado da seguinte forma:

Controle de fluxo

4.1.5 Operador and

Assim como o operador or, o operador and também pode ser utilizado em instruções condicionais. Comece seu novo programa da seguinte forma:

nota = int(input("Nota: "))

if nota >= 90 and nota <= 100:
    print("Conceito: A")
elif nota >=80 and nota < 90:
    print("Conceito: B")
elif nota >=70 and nota < 80:
    print("Conceito: C")
elif nota >=60 and nota < 70:
    print("Conceito: D")
else:
    print("Conceito: F")

Observe que, ao executar o programa, você poderá inserir uma nota e receber um conceito. No entanto, perceba que ainda existe potencial para erros (bugs). Normalmente, não devemos confiar que os usuários sempre irão inserir informações corretas. Podemos melhorar nosso código da seguinte forma:

nota = int(input("Nota: "))

if 90 <= nota <= 100:
    print("Conceito: A")
elif 80 <= nota < 90:
    print("Conceito: B")
elif 70 <= nota < 80:
    print("Conceito: C")
elif 60 <= nota < 70:
    print("Conceito: D")
else:
    print("Conceito: F")

Observe como o Python permite encadear operadores e condições de uma maneira pouco comum em muitas outras linguagens de programação. Ainda assim, podemos melhorar ainda mais o nosso programa:

nota = int(input("Nota: "))

if nota >= 90:
    print("Conceito: A")
elif nota >= 80:
    print("Conceito: B")
elif nota >= 70:
    print("Conceito: C")
elif nota >= 60:
    print("Conceito: D")
else:
    print("Conceito: F")

Observe como o programa foi melhorado ao fazer menos verificações. Isso torna o código mais fácil de ler e muito mais fácil de manter no futuro. Você pode aprender mais na documentação oficial do Python sobre controle de fluxo.

4.1.6 Instrução match

Assim como as instruções if, elif e else, as instruções match também podem ser usadas para executar código condicionalmente, dependendo de determinados valores. Considere o seguinte programa:

curso = input("Qual é o seu curso? ")

if curso == "Economia":
    print("FEA")
elif curso == "Administração":
    print("FEA")
elif curso == "Contabilidade":
    print("FEA")
elif curso == "Matemática":
    print("IME")
else:
    print("Curso não encontrado")

Observe que as três primeiras instruções condicionais exibem a mesma resposta. Podemos melhorar um pouco esse código utilizando a palavra-chave or:

curso = input("Qual é o seu curso? ")

if curso == "Economia" or curso == "Administração" or curso == "Contabilidade":
    print("FEA")
elif curso == "Matemática":
    print("IME")
else:
    print("Curso não encontrado")

Observe que o número de instruções elif diminuiu, melhorando a legibilidade do nosso código. Como alternativa, podemos usar instruções match para associar os cursos às unidades da USP. Considere o seguinte código:

curso = input("Qual é o seu curso? ")

match curso:
    case "Economia":
        print("FEA")
    case "Administração":
        print("FEA")
    case "Contabilidade":
        print("FEA")
    case "Matemática":
        print("IME")
    case _:
        print("Curso não encontrado")

Observe o uso do símbolo _ no último case. Ele corresponde a qualquer entrada, resultando em um comportamento semelhante ao de uma instrução else. Uma instrução match compara o valor que aparece após a palavra-chave match com cada um dos valores definidos após as palavras-chave case. Quando uma correspondência é encontrada, o bloco de código indentado correspondente é executado e o programa interrompe as demais verificações. Podemos melhorar o código da seguinte forma:

curso = input("Qual é o seu curso? ")

match curso:
    case "Economia" | "Administração" | "Contabilidade":
        print("FEA")
    case "Matemática":
        print("IME")
    case _:
        print("Curso não encontrado")

Observe o uso da barra vertical simples (|). Assim como a palavra-chave or, ela permite verificar múltiplos valores dentro da mesma instrução case.

4.2 Estruturas de Repetição

Estruturas de repetição permitem executar um mesmo bloco de código múltiplas vezes, tornando o processo mais eficiente e menos sujeito a erros do que repetir comandos manualmente. São fundamentais para automatizar tarefas, processar grandes volumes de dados e implementar algoritmos que dependem de iteração.

Neste capítulo, vamos explorar dois tipos principais de laços (loops):

  • Laços em que não sabemos previamente o número de repetições: while() (executam enquanto uma condição for verdadeira)
  • Laços em que sabemos previamente o número de repetições: for() (executam um número definido de vezes)

Compreender essas estruturas é essencial para resolver problemas de forma eficiente e elegante em programação.

4.2.1 Repetições com while()

O princípio de um loop while é que as instruções dentro do loop serão repetidas enquanto uma determinada condição for verdadeira. A ideia é que essa condição dependa de um ou mais objetos (variáveis) que sejam modificados durante as iterações; caso contrário, o loop se tornaria infinito. A sintaxe é a seguinte:

while condicao:
    instrucoes

Assim como nas instruções condicionais, as instruções devem ser escritas dentro de um bloco indentado. Vamos analisar um exemplo de um loop while:

i = 3

while i != 0:
    print("FEA-USP!")
    i = i - 1
FEA-USP!
FEA-USP!
FEA-USP!

Observe que o código executa corretamente, reduzindo o valor de n em 1 a cada “iteração” do loop. O termo iteração tem um significado especial em programação: refere-se a cada ciclo completo de execução do laço. A primeira iteração é chamada de iteração 0, a segunda é a iteração 1, e assim por diante.

Em programação, normalmente contamos a partir do zero: 0, 1, 2, etc. Compreender o conceito de iteração é fundamental para entender como os laços funcionam e para controlar corretamente o fluxo de repetição em seus programas. Podemos melhorar o nosso código da seguinte maneira:

i = 1

while i <= 3:
    print("FEA-USP!")
    i = i + 1
FEA-USP!
FEA-USP!
FEA-USP!

Note que, ao escrevermos i = i + 1, estamos atribuindo à variável i o valor da expressão à direita. No exemplo acima, começamos i em 1, como a maioria das pessoas costuma contar (1, 2, 3…). Se você executar o código acima, verá que ele imprime cinco vezes.

Entretanto, em programação, é considerado uma boa prática começar a contagem pelo zero. Podemos aprimorar nosso código para iniciar a contagem em zero:

i = 0

while i < 3:
    print("FEA-USP!")
    i += 1
FEA-USP!
FEA-USP!
FEA-USP!

Perceba que, ao alterar o operador para i < 3, nosso código passa a funcionar como esperado: começamos a contagem em 0 e o laço é executado três vezes, imprimindo a mensagem três vezes. - Além disso, repare que i += 1 é uma forma abreviada de escrever i = i + 1, facilitando a leitura e escrita do código. - Nosso código, até aqui, pode ser ilustrado da seguinte maneira:

Repetição com while()

Note que nosso loop conta o valor de i até 3, mas não inclui o 3.

4.2.2 Repetições com for()

Quando sabemos de antemão o número de repetições desejadas, o laço for() é a escolha ideal. Sua sintaxe é simples:

for objeto in valores_possiveis:
    instrucoes

Nesse contexto, objeto é uma variável local que, a cada iteração, assume um dos valores definidos em valores_possiveis. As instrucoes são os comandos executados em cada repetição do laço.

O laço for é especialmente útil para percorrer listas, sequências ou qualquer coleção de elementos. Por exemplo, podemos reescrever nosso código anterior da seguinte maneira:

for i in [0, 1, 2]:
    print("FEA-USP!")
FEA-USP!
FEA-USP!
FEA-USP!

Observe como esse código é mais limpo e fácil de entender em comparação ao exemplo anterior com o loop while. Aqui, a variável i começa em 0, imprime a mensagem, depois recebe 1, imprime novamente, depois recebe 2, imprime mais uma vez e então o laço termina automaticamente.

Essa abordagem é eficiente para listas pequenas, mas imagine se você precisasse repetir a operação para um milhão de vezes! O ideal é criar códigos que funcionem bem para qualquer quantidade de repetições, tornando-os escaláveis e fáceis de manter. Veja como podemos aprimorar nosso código para esses casos:

for i in range(0, 3):
    print("FEA-USP!")
FEA-USP!
FEA-USP!
FEA-USP!

A função range() é extremamente útil para criar sequências numéricas em laços de repetição, tornando o código mais limpo e eficiente. Seus principais argumentos são:

  • start: (opcional, padrão 0) valor inicial da sequência (incluído);
  • stop: valor final da sequência (não incluído);
  • step: (opcional, padrão 1) intervalo entre os valores.

Por exemplo, ao utilizar range(3), o Python gera automaticamente os valores 0, 1 e 2, permitindo que o laço execute exatamente três vezes sem a necessidade de criar listas manualmente ou controlar variáveis auxiliares.

Podemos tornar nosso código ainda mais limpo e idiomático. Note que, neste caso, nunca utilizamos a variável i dentro do corpo do laço, apenas a usamos para controlar o número de repetições. Ou seja, embora o Python precise de i para contar as iterações, não precisamos desse valor para nenhuma outra finalidade.

Em Python, quando a variável de iteração não é utilizada no corpo do laço, é uma boa prática representá-la por um sublinhado (_). Isso deixa claro para quem lê o código que o valor não será usado. Veja como fica:

for _ in range(3):
    print("FEA-USP!")
FEA-USP!
FEA-USP!
FEA-USP!

Note que trocar o i por _ não altera em nada o funcionamento do nosso programa. Na verdade, usar o sublinhado deixa explícito para quem lê o código que o valor da variável de controle não será utilizado, tornando o código mais claro e profissional.

Sempre que você precisar apenas repetir uma ação um certo número de vezes, sem se importar com o valor da variável de iteração, prefira usar _ no lugar de um nome qualquer. Isso é considerado uma boa prática em Python.

Outra alternativa interessante é utilizar a multiplicação de strings para repetir uma mensagem várias vezes de forma ainda mais concisa. Veja o exemplo:

print("FEA-USP!" * 3)
FEA-USP!FEA-USP!FEA-USP!

Note que, ao executar esse código, a mensagem será impressa três vezes, mas o resultado será FEA-USP!FEA-USP!FEA-USP! tudo na mesma linha. Como poderíamos adicionar uma quebra de linha ao final de cada mensagem?

print("FEA-USP!\n" * 3, end = "")
FEA-USP!
FEA-USP!
FEA-USP!

Note que esse código imprime a mensagem três vezes, cada uma em uma linha diferente. Ao adicionar end="" e o \n, informamos ao interpretador que deve inserir uma quebra de linha ao final de cada mensagem, mas sem adicionar uma linha em branco extra ao final do resultado.

4.2.3 Mais sobre Listas

Considere o exemplo a seguir:

alunos = ["Ana", "Pedro", "Marcos"]

print(alunos[0])
print(alunos[1])
print(alunos[2])
Ana
Pedro
Marcos

Observe que temos uma lista chamada alunos, contendo os nomes dos estudantes. Em seguida, imprimimos o aluno que está na posição 0, “Ana”. O mesmo é feito para os demais alunos, acessando cada posição da lista individualmente. No entanto, esse método não é prático se a lista for grande ou se não soubermos quantos elementos ela possui.

Assim como ilustramos anteriormente, podemos usar um laço para percorrer toda a lista de forma automática e eficiente. Para isso, podemos utilizar a função len() para descobrir o tamanho (quantidade de elementos) da lista alunos

alunos = ["Ana", "Pedro", "Marcos"]

for i in range(len(alunos)):
    print(alunos[i])
Ana
Pedro
Marcos

Outra forma ainda mais simples e legível de percorrer todos os elementos de uma lista é utilizando o próprio nome da variável na estrutura do laço. Veja:

alunos = ["Ana", "Pedro", "Marcos"]

for aluno in alunos:
    print(aluno)
Ana
Pedro
Marcos

Note que, para cada aluno na lista alunos, o código irá imprimir o nome do aluno como esperado. Você pode se perguntar por que não usamos o sublinhado (_), como discutido anteriormente. Optamos por não fazer isso porque a variável aluno é utilizada explicitamente no nosso código.

4.2.4 Interrompendo um Laço de Repetição

Ao trabalhar com laços controlados por condição, pode surgir a necessidade de interromper ou pular partes da execução antes que a condição principal deixe de ser satisfeita. Para esses casos, Python oferece mecanismos explícitos de controle de fluxo. As duas principais palavras-chave para modificar o comportamento de um laço são:

  • A instrução continue informa explicitamente ao Python para ir para a próxima iteração do laço.
  • Já a instrução break faz com que o Python “quebre” o laço antes de terminar todas as iterações previstas.

Essas ferramentas são úteis para tratar casos especiais, validar entradas ou evitar cálculos desnecessários dentro de um laço.

while True:
    n = int(input("Qual o valor de n? "))
    if n < 0:
        continue
    else:
        break

No exemplo anterior, continuamos para a próxima iteração do laço quando n é menor que 0 — o que faz com que o usuário seja novamente solicitado a informar “Qual o valor de n?”. Se, por outro lado, n for maior ou igual a 0, saímos do laço e o restante do programa é executado normalmente.

Note que a palavra-chave continue é redundante neste caso. Podemos melhorar nosso código da seguinte forma:

while True:
    n = int(input("Qual o valor de n? "))
    if n > 0:
        break

for _ in range(n):
    print("FEA-USP!")

Observe que esse laço while irá sempre executar até que n seja maior que 0. Quando n for maior que 0, o laço é interrompido.

4.2.5 Aplicação: Teorema Central do Limite

O Teorema Central do Limite (TCL) é um dos resultados mais importantes da estatística e da teoria das probabilidades. Em termos simples, ele afirma que, sob certas condições, a média de um grande número de variáveis aleatórias independentes e identicamente distribuídas tende a seguir uma distribuição normal, independentemente da distribuição original dessas variáveis.

DicaTeorema Central do Limite

Suponha que estejamos amostrando de uma variável aleatória \(X\) com média finita \(\mu\) e desvio padrão finito \(\sigma\). Independentemente da distribuição original de \(X\), a distribuição da média amostral aproxima-se de uma distribuição normal à medida que o tamanho da amostra aumenta. Nessa caso, a média e o desvio padrão da distribuição das médias amostrais são dados por:

\[\mu_{\bar{X}}=\mu\]

\[\sigma_{\bar{X}}=\frac{\sigma}{\sqrt{N}}\]

Vamos ilustrar esse resultado por meio de um experimento computacional simples:

  1. Gerar várias amostras aleatórias de uma distribuição (por exemplo, uniforme);
  2. Calcular a média de cada amostra;
  3. Repetir esse processo muitas vezes, armazenando as médias obtidas;
  4. Analisar o comportamento dessas médias.

Esse experimento permite observar, na prática, como a distribuição das médias amostrais se aproxima de uma normal, mesmo quando os dados originais não seguem esse formato.

Vamos começar sorteando uma única amostra com 5 observações de uma distribuição uniforme entre 0 e 1:

import numpy as np

amostra = np.random.uniform(0, 1, 5)

print(amostra)
print(np.mean(amostra))
[0.43208861 0.86517986 0.16973698 0.03472065 0.77964568]
0.45627435578926656

Se executarmos esse código novamente, obteremos valores diferentes. Isso acontece porque estamos lidando com números aleatórios. Para tornar os resultados reprodutíveis, utilizamos a função seed do NumPy, que fixa o ponto de partida do gerador de números aleatórios:

import numpy as np

np.random.seed(19)
amostra = np.random.uniform(0, 1, 5)

print(amostra)
print(np.mean(amostra))
[0.0975336  0.76124972 0.24693797 0.13813169 0.33144656]
0.3150599084675772

Note que a média da amostra gerada dificilmente será exatamente igual a 0,5, que é a média teórica da distribuição uniforme \(U(0,1)\). É aqui que entra o Teorema Central do Limite: embora uma única amostra possa apresentar variações, a média das médias amostrais tende a se aproximar da média populacional conforme aumentamos o número de amostras.

Vamos repetir o experimento várias vezes, gerando diversas amostras de tamanho 5 e calculando suas médias:

import numpy as np

np.random.seed(19)   # reprodutibilidade
n_amostras = 10000   # número de repetições
tamanho_amostra = 5  # tamanho de cada amostra

# Lista para armazenar as médias
medias = []

# Gerar as amostras e calcular as médias
for _ in range(n_amostras):
    amostra = np.random.uniform(0, 1, tamanho_amostra)
    medias.append(np.mean(amostra))

# Calcular a média das médias
media_das_medias = np.mean(medias)
print(f"Média das médias: {media_das_medias:.4f}")
Média das médias: 0.4989

Observe que o valor obtido é muito próximo de 0,5, evidenciando o resultado teórico.

Podemos aprofundar a análise variando o número de repetições do experimento:

import numpy as np

np.random.seed(19)                             # reprodutibilidade
n_amostras = [1, 10, 100, 1000, 10000, 100000] # número de repetições
tamanho_amostra = 5                            # tamanho de cada amostra

for n in n_amostras:
    medias = []

    for _ in range(n):
        amostra = np.random.uniform(0, 1, tamanho_amostra)
        medias.append(np.mean(amostra))

    print(f"Média das médias: {np.mean(medias):.4f}")
Média das médias: 0.3151
Média das médias: 0.5157
Média das médias: 0.4762
Média das médias: 0.5011
Média das médias: 0.4992
Média das médias: 0.5002

Note que, à medida que o número de amostras aumenta, a média das médias se estabiliza em torno de 0,5.

4.3 Exercícios

  1. Escreva um programa que receba a lista de rendas mensais abaixo e classifique cada observação nas seguintes categorias:
  • Dados: rendas = [1200, 3240, 3250, 7100, 12000, 5000, 7500, 1800, 4500]
  • “Baixa renda” caso a renda mensal seja menor do que 2 salários mínimos.
  • “Renda média” caso a renda mensal esteja entre 2 e 5 salários mínimos.
  • “Alta renda” caso contrário.

O programa deve retornar uma nova lista contendo as classificações na mesma ordem da lista original. Você deve utilizar as instruções if e for. Considere o salário mínimo de \(R\$ 1.621\) (valor de 2026)

  1. Utilize o loop while para construir um código que realize uma soma infinita de uma progressão geométrica em que o primeiro termo é igual a \(10\) e a razão é igual a \(0.9\).Lembre-se de definir um critério de parada para que o loop não rode para sempre.

  2. Escreva um programa que, a partir de uma lista de dados, percorra todos os elementos um a um ignorando valores negativos, interrompendo o loop caso encontre um número maior do que \(8\), e somando todos os valores processados pelo loop antes da interrupção. Aplique esse programa à lista numeros = [4,7,-2,9,1,2,-10,6,-5,3,7,-0.5,12,1,3,-2].

  3. Simule 10.000 lançamentos de um dado justo utilizando a função randint do numpy.random. Utilize um loop for e expressões condicionais para (i) calcular a frequência relativa de cada face e (ii) a média dos valores obtidos dentro da amostra de lançamentos.

  4. O último teorema de Fermat diz que não há nenhum número inteiro positivo a,b,c tal que \[ a^n + b^n = c^n \]

    para quaisquer valores de n maiores do que 2.

Use o que você aprendeu com condicionais, operações aritméticas e instruções print para testar se o teorema se mantém, dada a lista de números inteiros abaixo. Note que ao fim de cada iteração, o programa deve exibir “Holy smokes! Fermat was wrong” caso o teorema não valha e “Fermat was right” caso você não tenha refutado um gênio dos tempos modernos.

  • a = [1,2,3,4,5,6,7,8,9,10]
  • b = [1,2,3,4,5,6,7,8,9,10]
  • n = [3,4,5,6,7,8,9,10,37,52,89,100]