Com uma quantidade investida de 250000 reais, um rendimento de 1% sobre o valor na conta e considerando a distribuição do gasto mensal como sendo normal com média 3000 e variância 1000, o cálculo básico realizado tanto na programação utilizando o while, quanto na programação utilizando recursão é
\[AP_{i}= AP_{i-1}+R_{i}-G_{i},\]
ou seja, a aplicação no final do mês atual é igual a aplicação no final do mês anterior mais o rendimento menos o gasto do mês atual. Este cálculo deve ser realizado até que o valor da aplicação no mês anterior seja menor do que o gasto no mês atual.
Para simplificar a programação, no caso do While, decidi fazer o loop enquanto a aplicação fosse maior ou igual a 0, embora, ao encontrar a condição de parada, o algoritmo retorne o contador de meses com um mês contabilizado a mais. Devido a isso, deve ser subtraída uma unidade do contador de meses. A mesma ideia vale para a versão recursiva, de modo que o caso base da recursão é definido quando a aplicação assumir valores negativos.
A seguir tem-se as implementações de ambos os algoritmos.
import numpy as np
from scipy.stats import norm as rnorm
import matplotlib.pyplot as plt
from collections import Counter
#Versão com While.
def tempo_sem_precisar_trabalhar(aplicacao,variancia):
meses=0
while(aplicacao>=0):
rendimento=0.01*aplicacao
gastos_mensais= float(rnorm.rvs(loc=3000, scale=np.sqrt(variancia), size=1))
aplicacao=aplicacao+rendimento-gastos_mensais
meses+=1
aplicacao=aplicacao+gastos_mensais
meses=meses-1
return {"resto_da_aplicação":aplicacao,"meses_sem_trabalhar":meses}
#Versão recursiva.
def func_recursiva(aplic,variancia,meses=0):
if(aplic<0):return meses-1
else:
rendimento=0.01*aplic
gastos_mensais=float(rnorm.rvs(loc=3000, scale=np.sqrt(variancia), size=1))
aplic=aplic+rendimento-gastos_mensais
meses=meses+1
return func_recursiva(aplic,variancia,meses)
O algoritmo com While, além de retornar os meses, também retorna o valor que sobrou na conta de aplicação. Para o algoritmo recursivo, decidi deixá-lo de tal forma que ele retornasse apenas o que é de real interesse, ou seja, o contador de meses.
Para conseguir saber por quantos meses em média Marcelo consegue viver sem trabalhar é preciso replicar este cenário uma quantidade razoavelmente grande de vezes.
A seguir tem-se o código utilizado para replicar este cenário, calcular a quantidade média de meses sem trabalhar e gerar os gráficos de interesse.
mc=[]
for i in range(1,50000+1):
valores=tempo_sem_precisar_trabalhar(250000,1000)
mc.append(valores)
mst=[]
resto=[]
for i in mc:
mst.append(i.get("meses_sem_trabalhar"))
resto.append(i.get("resto_da_aplicação"))
np.mean(mst)
y=Counter(mst)
x=[str(i) for i in np.unique(mst)]
plt.bar(x,y.values(), color="red")
plt.xlabel('meses sem trabalhar')
plt.ylabel('Freq')
A quantidade média de meses sem trabalhar, considerando uma distribuição de gastos com variância igual a 1000 é 179.5747 meses. O gráfico abaixo mostra a distribuição do número de meses que ele pode ficar sem trabalhar.

Se considerarmos que a variância de sua distribuição de gastos aumenta para 2000, a média de meses sem trabalhar passa a ser 179.5727 meses e a distribuição do número de meses que ele pode ficar sem trabalhar passa a ter uma dispersão maior, o que já era de se esperar. Em outras palavras, se a variância do gasto muda de 1000 para 2000, a média do número de meses sem trabalhar não muda, mas tem-se uma incerteza maior com relação ao valor informado pela estimativa da média.

Como a média é uma métrica que sofre uma grande influência de valores extremos, uma métrica robusta seria a mediana.
Quanto à quantidade restante na aplicação, percebeu-se que essa quantidade possui uma distribuição que é muito próxima a de uma uniforme quando a variância do gasto mensal é alta, já para valores da variância do gasto baixos (por exemplo var=10) essa similaridade com a uniforme não é observada.
O gráfico abaixo mostra a densidade da quantidade restante na aplicação quando a variância do gasto mensal é igual a 1000.

A seguir tem-se a implementação em R do problema. Apesar das rotinas serem muito semelhantes em Python e em R, o tempo de processamento foi infinitas vezes menor no R. Não cheguei a calcular os tempos, mas ao rodar o Monte Carlo para n=50000, demorou mais de 30 minutos para retornar o resultado no Python, enquanto que no R demorou menos de 3 minutos. Como sou iniciante no Python, posso ter implementado de um certo modo ineficiente, mas se alguém souber algum outro motivo para essa diferença de tempo de processamento, ficarei muito grato em saber.
tempo_sem_precisar_trabalhar=function(aplicacao,variancia){
meses=0
while(aplicacao>=0){
rendimento=0.01*aplicacao
gastos_mensais=rnorm(1,mean = 3000,sd = sqrt(variancia))
aplicacao=aplicacao+rendimento-gastos_mensais
meses=meses+1
}
aplicacao=aplicacao+gastos_mensais
meses=meses-1
return(list(resto_da_aplicacao=aplicacao,meses_sem_trabalhar=meses))
}
mc=list()
for(i in 1:50000) mc[[i]]=tempo_sem_precisar_trabalhar(250000,1000)
resto_da_aplicacao_mc=as.numeric(lapply(mc,function(x)x=x[[1]]))
meses_sem_trabalhar_mc=as.numeric(lapply(mc,function(x)x=x[[2]]))
X11();plot(hist(resto_da_aplicacao_mc))
X11();plot(as.factor(meses_sem_trabalhar_mc))
func_recursiva=function(aplic,variancia,meses=0){
if(aplic<0) return(meses-1)
else
rendimento=0.01*aplic
gastos_mensais=rnorm(1,mean = 3000,sd = sqrt(variancia))
aplic=aplic+rendimento-gastos_mensais
meses=meses+1
return(func_recursiva(aplic,variancia,meses))
}
x=0
for(i in 1:50000)x[i]=func_recursiva(250000,1000)
x11();plot(as.factor(x))