A forma como obter um mesmo resultado utilizando um programa só está limitada pela imaginação do programador.
Contudo, embora o resultado numérico seja o mesmo, o tempo de execução pode ser muito diferente.
Esta diferença pode ser importante e deste modo limitadora da excutabilidade de determiada ideia ou processo.
Vamos utilizar como exemplo o cálculo de um
O valor médio de uma função (<f>) no intervalo a, b é dado por $$ <f> = \frac{1}{b-a}\int_a^bf(x)dx$$ Por outro lado um valor médio pode ser calculado como $$<f>=\frac{1}{N}\sum_if(x_i)$$ Então temos que um integral pode ser aproximado a $$(b-a)\frac{1}{N}\sum_if(x_i)\simeq\int_a^bf(x)dx$$ Os valores de x devem ser uniformemente distribuídos entre a e b. O seu número (N) e o seu valor vão influenciar o resultado do integral.
Deste modo, vamos calcular muitas vezes (M) o integral e ver qual o valor mais frequente fazendo um histograma para representar a distribuição de valores.
Para distinguirmos a velocidade de cada metodologia utilizada para calcular um integral, é preciso o tempo de execução, que vamos optar, total. Assim, este notebook serve também de exemplo a
Para isso o python dispõe da função time.
# Vamos precisar dos seguintes módulos/ funções
#
from scipy import random
import numpy as np
import matplotlib.pyplot as plt
from termcolor import colored
# Método escrito sem preocupações de tempo de execução
#
import time
start_time = time.time()
a = 0
b = np.pi
N = 1000 # Número de áreas a calcular
M = 1000 # Número de pontos para calcular o integral
def func(x):
'''Função integranda'''
return np.sin(x)
areas = np.zeros(M)
for j in range(M):
xrand=np.zeros(N)
for i in range(N):
xrand[i] = random.uniform(a,b)
integral = 0.0
for i in range(N):
integral += func(xrand[i])
answer = (b-a)/N*integral
areas[j]=answer
plt.title("Distribuição de frequências (histograma)")
plt.hist(areas,bins = 30, ec = 'black')
plt.xlabel("Áreas")
plt.ylabel("Frequência")
plt.show()
print("%i integrais, calculados com %i pontos cada um, demoram "%(M,N)+
colored('%.3f s', 'red')%((time.time() - start_time)))
# Método escrito com algumas preocupações de tempo de execução
#
import time
start_time = time.time()
a = 0
b = np.pi
M = 1000
N = 1000
areas = []
for j in range(M):
xrand = random.uniform(a,b,N)
integral=sum(np.sin(xrand))
answer = (b-a)/N*integral
areas.append(answer)
plt.title("Distribuição de frequências (histograma)")
plt.hist(areas,bins = 30, ec = 'black')
plt.xlabel("Áreas")
plt.ylabel("Frequência")
plt.show()
print("%i integrais, calculados com %i pontos cada um, demoram "%(M,N)+
colored('%.3f s', 'red')%((time.time() - start_time)))
A principal razão de se conseguir diminuir o tempo de execução em mais de 10 X's foi principalmente substituir N chamadas da função func por uma facilidade existente no Numpy.
Essa facilidade do Numpy é a sua capacidade de executar funções sobre matrizes, neste caso o cálculo do sin.
Tal aceleração deve-se ao facto do python ser semi-interpretado, isto é, as funções são compiladas, o código que as chama é que é interpretado.
Outra aceleração obtida, mas menos importante, foi a substituição de array's do Numpy por listas do python.
Consegue fazer mais rápido ainda? Para melhorar a precisão pode-se aumentar N ou M. Como aumenta a diferença entre os tempos de execução? Qual o melhor N? ou M?
Para ver a importância deste assunto, tempo de execução e optimização de código, responda aos desafios sugeridos com ambos os programas. Verá que para N = 10000 e M = 10000 será dificil de esperar pelo primeiro programa.