Um dos problemas de lógica de programação que mais quebram a cabeça de quem está começando são os problemas com cálculo de porcentagens ou juros.

Começa assim: Calcule 10% de aumento de um salário de R$2.500,00

Dependendo da base matemática do aluno, porcentagem se aprende na quarta/quinta série… alguns conceitos tem que ser relembrados. Como diz o nome, a 10% significa que a cada 100 do valor, você deve retirar 10. O cálculo é bem simples, pode ser realizado com uma multiplicação. Por exemplo: 10/100 * 2500, que resulta em 250. Até aí tudo bem, mas alguns alunos perguntam por que as vezes fazemos 0.1 * 2500. Bem, é apenas a forma de representar que muda, pois 10/100 é equivalente a 0.1, alias 100/1000 também e por aí vai. Mas está é a parte fácil e depois de acertar a notação, as coisas voltam a andar novamente.

Quando chegamos em repetições, problemas como cálculo da poupança começam a aparecer. Eles tem o seguinte formato: imagine que alguém deposita R$1.000,00 para poupança e que esta rende 1% ao mês (bons tempos). Depois de n meses, quanto a pessoa terá como saldo? O problema combina porcentagem com repetição. Vejamos como fica para 6 meeses:

saldo = 1000  # Valor inicial
n = 6         # Número de meses
juros = 0.01  # Juros mensal 1% = 1/100 = 0.01
for mês in range(1, n + 1):
   saldo *= 1 + juros
   print(f"Mês ({mês}): {saldo:7.2f}")

que resulta em:

Mês (1): 1010.00
Mês (2): 1020.10
Mês (3): 1030.30
Mês (4): 1040.60
Mês (5): 1051.01
Mês (6): 1061.52

Várias perguntas surgem.

Por que o *=?

É que o juros é composto, ou seja, aplicado ao saldo precedente. Não se calcula 1% do saldo inicial e se multiplica pelo número de meses. Vai ficar mais claro depois com a fórmula.

Por que se soma 1 ao juros?

Como estamos multiplicando o saldo, precisamos ajustar o novo saldo para que seja igual ao anterior mais o juros do mês. Em formato mais longo seria saldo = saldo + juros * saldo. Este cálculo pode ser simplificado, agrupando a variável saldo de forma que saldo = saldo * (1 + juros). Como em Python podemos escrever saldo = saldo * como saldo *=, a expressão fica resumida a saldo *= (1 + juros), veja que retirei os parênteses pois não são mais necessários.

Precisa de repetição para realizar este cálculo?

Não, você pode ter uma solução analítica, aplicando a fórmula: `saldo = saldo * (1 + juros) ** mês). Exemplo:

>>> print(f"{1000 * (1 + 0.01) ** 6:7.2f}")
1061.52

E de onde vem essa exponenciação?

Se voltarmos ao exemplo com repetição, veremos que:

No mês 1, o saldo é saldo * 1.01.

O saldo no mês 2 é saldo * 1.01 * 1.01

No mês 3, saldo * 1.01 * 1.01 * 1.01

E assim por diante, começamos a ver um padrão onde o 1.01 é multiplicado por ele mesmo o número de mês que estamos calculando. Podemos então escrever de forma genérica que saldo *= 1.01 ** mês, que é justamente a definição da exponenciação: a ** n = a * a * a ... (n vezes)

Por que usamos a repetição?

Porque o curso é de lógica de programação e o professor quer que você tenha uma motivo para usar o for ou while.

Uma questão que apareceu hoje no StackOverflow em Português

A questão foi votada para baixo e logo depois fechada :-(

Duas fabricantes de calçado disputam o mercado no Brasil. A empresa A tem produção de 10.000 pares/mês e um crescimento mensal de 15%. A empresa B, de 8.000 pares/mês e tem um crescimento mensal de 20%. Determinar o número de meses necessários para que a empresa B supere o número de pares produzidos pela empresa A.

Vamos ver como ficaria isso na matemática:

Produção da empresa A: 10000 x 1.15m

Produção da empresa B: 8000 x 1.20m

Onde m é número de meses.

O que você procura é o valor de m quando:

8000 x 1.20m > 10000 x 1.15m

Você pode resolver isso usando logaritmos, mas num curso de lógica de programação, o professor provavelmente espera que você varie o valor de m de 1 em 1.

Então, tente calcular a produção quando m = 1. Compare a produção das duas fábricas (use as fórmulas acima). Se o valor de B não ultrapassar o de A, continue incrementando m de 1. Pare quando a produção de B for maior que A (a resposta é o valor de m quando isso acontecer).

m = 0
while True:
   prodA = 10000 * 1.15 ** m
   prodB = 8000 * 1.2 ** m
   print(f"Prod A: {prodA:8.2f} Prod B: {prodB:8.2f} - mês: {m}")
   if prodB > prodA:
      break
   m += 1
print(f"Meses para que a produção de B ultrapasse a produção de A: {m}")

que resulta em:

Prod A: 10000.00 Prod B:  8000.00 - mês: 0
Prod A: 11500.00 Prod B:  9600.00 - mês: 1
Prod A: 13225.00 Prod B: 11520.00 - mês: 2
Prod A: 15208.75 Prod B: 13824.00 - mês: 3
Prod A: 17490.06 Prod B: 16588.80 - mês: 4
Prod A: 20113.57 Prod B: 19906.56 - mês: 5
Prod A: 23130.61 Prod B: 23887.87 - mês: 6
Meses para que a produção de B ultrapasse a produção de A: 6

E a solução analítica? Como falei o curso é de lógica de programação :-D

Mas você pode resolver a desigualdade usando logaritmos:

8000 x 1.20m > 10000 x 1.15m

ln(8000) + m * ln(1.20) > ln(10000) + m * ln(1.15)

m * ln(1.20) - m * ln(1.15) > ln(10000) - ln(8000)

m * (ln(1.20) - ln(1.15)) > ln(10000) - ln(8000)

m = (ln(10000) - ln(8000))/(ln(1.20) - ln(1.15))

que em Python fica:

from math import log
m = (log(10000) - log(8000))/(log(1.20) - log(1.15))
print(m)

que resulta em:

5.243082071149164

Que podemos arredondar para 6, caso não consideremos frações de um mês. Agora conhecemos o método analítico e o iterativo. Fica fácil de entender porque o segundo é mais usado em cursos de lógica de programação.