{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Diversos" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Formas de declaração de dicionários\n", "\n", "Existem múltiplas formas de declarar dicionários. Já tínhamos visto a sintaxe com o uso de `{}`:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{'primeiro': 23, 'segundo': 45}" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "{'primeiro': 23, 'segundo': 45}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Também é possível usar a função construtora `dict()`, que coleta todos os parâmetros passados por `chave=valor` em um dicionário (veja os detalhes sobre coleta de valores em funções na aula passada):" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{'primeiro': 23, 'segundo': 45}" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "dict(primeiro=23, segundo=45)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Uma terceira opção é usar um `zip`. Essa função é útil quando as chaves e os valores são construídos pelo seu programa separadamente:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{'primeiro': 23, 'segundo': 45}" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "chaves=['primeiro', 'segundo']\n", "valores=[23, 45]\n", "dict(zip(chaves, valores))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Expressões condicionais\n", "\n", "Geramente, quando um comando condicional espera uma expressão, essa expressão deve retornar um booleano:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "menor\n" ] } ], "source": [ "a = 4\n", "if a < 5:\n", " print('menor')" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a < 5" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "False" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "a > 5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Mas, como em C, condicionais também funcionam com não booleanos.\n", "\n", "Por exemplo, para inteiros, 0 é considerado falso, qualquer valor diferente de zero é considerado verdadeiro." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = 0\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "verdadeiro\n" ] } ], "source": [ "a = 10\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('false')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`None` é considerado falso:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = None\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Listas, dicionários, conjuntos ou cadeias vazias são também considerados falsos. Se tiverem algum elemento, são considerados verdadeiros." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = []\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = {}\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = set()\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "falso\n" ] } ], "source": [ "a = ''\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "verdadeiro\n" ] } ], "source": [ "a = [0]\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "verdadeiro\n" ] } ], "source": [ "a = '0'\n", "if a:\n", " print('verdadeiro')\n", "else:\n", " print('falso')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Iteração em dicionários\n", "\n", "Quando iteragimos pelos elementos de um dicionário (por exemplo, em um `for`), a iteração ocorre pelas chaves, na ordem que o Python considerar conveniente." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": true }, "outputs": [], "source": [ "d = dict(zip(['a','b','c','d'], [1,2,3,4]))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{'a': 1, 'b': 2, 'c': 3, 'd': 4}" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "d" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b\n", "a\n", "d\n", "c\n" ] } ], "source": [ "for x in d:\n", " print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para acessar o valor, indexamos o dicionário." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b 2\n", "a 1\n", "d 4\n", "c 3\n" ] } ], "source": [ "for x in d:\n", " print(x, d[x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Outra opção é usar o método `items` de dicionários, que retorna tuplas chave/valor:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('b', 2)\n", "('a', 1)\n", "('d', 4)\n", "('c', 3)\n" ] } ], "source": [ "for x in d.items():\n", " print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ele é normalmente usado em conjunto com desempacotamento, para já acessar a chave e o correspondente valor em variáveis distintas." ] }, { "cell_type": "code", "execution_count": 21, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "b 2\n", "a 1\n", "d 4\n", "c 3\n" ] } ], "source": [ "for chave, valor in d.items():\n", " print(chave, valor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Se quisermos que as chaves sejam percorridas em uma ordem específica, devemos requisitar:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "a 1\n", "b 2\n", "c 3\n", "d 4\n" ] } ], "source": [ "for x in sorted(d):\n", " print(x, d[x])" ] }, { "cell_type": "code", "execution_count": 23, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d 4\n", "c 3\n", "b 2\n", "a 1\n" ] } ], "source": [ "for x in sorted(d, reverse=True):\n", " print(x, d[x])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ou então:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "d 4\n", "c 3\n", "b 2\n", "a 1\n" ] } ], "source": [ "for chave, valor in sorted(d.items(), reverse=True):\n", " print(chave, valor)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Compreensões versus map ou filter\n", "\n", "Em diversas situações, o uso de `map` ou `filter` é equivalente ao uso de *list comprehension*, sendo a escolha feita de acordo com conveniência no código, ou por questões de eficiência (no caso de haver muitos elementos).\n", "\n", "Veja os exemplos abaixo." ] }, { "cell_type": "code", "execution_count": 25, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 25, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(map(lambda i: i * i, range(10)))" ] }, { "cell_type": "code", "execution_count": 26, "metadata": { "collapsed": true }, "outputs": [], "source": [ "l1 = []\n", "for i in range(10):\n", " l1.append(i * i)" ] }, { "cell_type": "code", "execution_count": 27, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 27, "metadata": {}, "output_type": "execute_result" } ], "source": [ "l1" ] }, { "cell_type": "code", "execution_count": 28, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[i * i for i in range(10)]" ] }, { "cell_type": "code", "execution_count": 29, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 6.10 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 5.14 µs per loop\n" ] } ], "source": [ "%timeit list(map(lambda i: i * i, range(10)))" ] }, { "cell_type": "code", "execution_count": 30, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 28.25 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 2.72 µs per loop\n" ] } ], "source": [ "%timeit [i * i for i in range(10)]" ] }, { "cell_type": "code", "execution_count": 31, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 5.93 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 3.62 µs per loop\n" ] } ], "source": [ "def teste():\n", " l1 = []\n", " for i in range(10):\n", " l1.append(i * i)\n", "\n", "%timeit teste()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]" ] }, "execution_count": 32, "metadata": {}, "output_type": "execute_result" } ], "source": [ "list(filter(lambda i: i % 3 == 0, range(30)))" ] }, { "cell_type": "code", "execution_count": 33, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "[x for x in range(30) if x % 3 == 0]" ] }, { "cell_type": "code", "execution_count": 34, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10000 loops, best of 3: 14.7 µs per loop\n" ] } ], "source": [ "%timeit list(filter(lambda i: i % 3 == 0, range(30)))" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 4.19 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 7.35 µs per loop\n" ] } ], "source": [ "%timeit [x for x in range(30) if x % 3 == 0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exemplo: Raizes de funções" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Frequentemente desejamos encontra os zeros de uma dada função. Veremos duas formas de realizar isso numericamente." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Método da bissecção" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "\n", "Uma forma de fazer isso é o chamado *método de bissecção*. Se temos dois pontos `a` e `b` onde a função tem sinais contrários então (supondo que a função é contínua) ela deve ter uma raiz entre esses dois valores.\n", "\n", "Dividimos então, iterativamente, o intervalo em 2 e verificamos em qual das metades continua existindo uma troca de sinal no valor da função. Mantemos então esse novo intervalo.\n", "\n", "Na função abaixo, `f` é a função da qual queremos a raiz, `a` e `b` são dois pontos para os quais `f(a)` e `f(b)` devem ter sinais opostos, `precisao` é o erro máximo aceitável no valor da raiz calculado." ] }, { "cell_type": "code", "execution_count": 36, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def bisseccao(f, a, b, precisao):\n", " fa = f(a)\n", " fb = f(b)\n", " if fa * fb >= 0: # Se têm mesmo sinal, é um erro\n", " raise Exception('Intervalo inválido')\n", " x = (a + b) / 2 # Calcula o ponto médio e sua função\n", " fx = f(x)\n", " # Repetitivamente, terminando quando acha raiz ou intervalo é pequeno\n", " while fx != 0.0 and abs(b - a) >= precisao:\n", " # Substitui o limite com mesmo sinal que f(x) por x\n", " if fx * fa > 0:\n", " a = x\n", " fa = fx\n", " else:\n", " b = x\n", " # Recalcula ponto médio\n", " x = (a + b) / 2\n", " fx = f(x)\n", " return x" ] }, { "cell_type": "code", "execution_count": 37, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import math" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Vamos primeiro plotar a função para termos uma idéia de onde temos uma raiz.\n", "\n", "Usaremos a função $\\cos x - 1/2$ e estamos interessados no intervalo $[0,3]$." ] }, { "cell_type": "code", "execution_count": 38, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "x = np.arange(0, 3, 0.1)\n", "y = np.cos(x) - 0.5\n", "plt.axhline(0, color='gray')\n", "plt.grid(True)\n", "plt.plot(x, y);" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true }, "source": [ "Vemos que nesse intervalo existe uma raiz um pouco acima de 1. Usamos agora o método da bissecção para encontrar essa raiz. Criamos uma função lambda para a expressão da função $f(x) = \\cos x - 1/2$." ] }, { "cell_type": "code", "execution_count": 39, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0471975514665246\n" ] } ], "source": [ "raiz = bisseccao(lambda x: math.cos(x) - 0.5, 0, 3, 1e-8)\n", "print(raiz)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O valor da função nesse ponto deve ser próximo de zero." ] }, { "cell_type": "code", "execution_count": 40, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "-2.337635085503109e-10" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "math.cos(raiz) - 0.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Método da secante" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Um outro método, frequentemente mais eficiente, consiste em, ao invés de dividir o intervalo em duas partes iguais, fazer uma divisão baseada numa aproximação linear entre os dois pontos do intervalo atual. Isto é, ao invés de $x=(a+b)/2$ usamos $$x=\\frac{a f(b) - b f(a)}{f(b) - f(a)}$$" ] }, { "cell_type": "code", "execution_count": 41, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def secante(f, a, b, precisao):\n", " fa = f(a)\n", " fb = f(b)\n", " if fa * fb >= 0:\n", " raise Exception('Invalid interval')\n", " x = (a * fb - b * fa) / (fb - fa)\n", " fx = f(x)\n", " while fx != 0.0 and abs(b - a) >= precisao:\n", " if fx * fa > 0:\n", " a = x\n", " fa = fx\n", " else:\n", " b = x\n", " fb = fx\n", " x = (a * fb - b * fa) / (fb - fa)\n", " fx = f(x)\n", " return x" ] }, { "cell_type": "code", "execution_count": 42, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1.0471975511965976\n" ] } ], "source": [ "raiz = secante(lambda x: math.cos(x) - 0.5, 0, 3, 1e-8)\n", "print(raiz)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "1.1102230246251565e-16" ] }, "execution_count": 43, "metadata": {}, "output_type": "execute_result" } ], "source": [ "math.cos(raiz) - 0.5" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Comparando os tempos" ] }, { "cell_type": "code", "execution_count": 44, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "10000 loops, best of 3: 41.9 µs per loop\n" ] } ], "source": [ "%timeit bisseccao(lambda x: math.cos(x)-0.5, 0, 3, 1e-8)" ] }, { "cell_type": "code", "execution_count": 45, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The slowest run took 5.08 times longer than the fastest. This could mean that an intermediate result is being cached.\n", "100000 loops, best of 3: 14.5 µs per loop\n" ] } ], "source": [ "%timeit secante(lambda x: math.cos(x)-0.5, 0, 3, 1e-8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exemplo: Integração numérica\n", "\n", "Outra operação útil é a integração numérica: Queremos calcular uma aproximação para a integral definida de uma função num intervalo especificado. Existem diversas formas de realizá-la. Uma forma simple é a chamada *regra do trapézio*, onde avaliamos o valor da função para um número de pontos igualmente espaçados no intervalo e em cada subintervalo aproximamos a área sob a função pelo trapézio formado pelos pontos $(x_i, 0), (x_i, f(x_i)), (x_{i+1}, f(x_{i+1})), (x_{i+1}, 0)$.\n", "\n", "Se $a$ e $b$ são os limites do intervalo de interesse, dividimos esse intervalo em $N$ subintervalos, cada um de tamanho $h=(b-a)/N$. Então avaliamos a função nos pontos $f(x_0=a), f(x_1=a+h), f(x_2=a+2h), \\ldots f(x_{N-1}=a+(N-1)h), f(b=a+Nh)$.\n", "\n", "A integral será então aproximada por:\n", "$$\n", "\\left[\\frac{1}{2}f(a) + f(a+h) + f(a+2h) + \\cdots + f(a+(N-1)h) + \\frac{1}{2}f(b)\\right]h\n", "$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def trapezio(f, a, b, N):\n", " h = (b - a) / N\n", " s = (f(a) + f(b)) / 2\n", " for i in range(1, N):\n", " s += f(a + i * h)\n", " return s * h" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Agora usaremos o método do trapézio para calcular uma aproximação para\n", "$$ \\int_0^{\\pi / 2} \\sin x\\, dx.$$" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "trapezio(math.sin, 0, math.pi/2, 1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A desvantagem desse método é que não sabemos de antemão o número de intervalos para atingirmos uma resposta com precisão apropriada. (Experimente diversos valores de $N$ na chamada acima.)\n", "\n", "Podemos resolver esse problema aumentando o número de intervalos até atingir a precisão que queremos. A idéia é considerar que, após a integral estar suficientemente próxima da resposta, se duas iterações sucessivas com números crescentes de intervalos não fornecerem diferenças significativas no valor da integral calculada, então podemos considerar que já houve convergência.\n", "\n", "No codigo abaixo, começamos com 1 intervalo, e a cada vez vamos dobrando o número de intervalos (e correspondentemente dividindo $h$ por 2). Desta forma, a cada iteração precisamos apenas calcular os valores da função nos novos pontos, e podemos aproveitar os cálculos feitos para os pontos anteriores, lembrando de reescalar o resultado anterior para o novo (menor) valor de $h$." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def trapezio_precisao(f, a, b, epsilon):\n", " # curriter: iteração atual. Começa com 0\n", " # N: número de intervalos na iteração atual, começa com 1\n", " # h: tamanho do intervalo na iteração atual, começa com b - a\n", " curriter, N, h = 0, 1, (b - a)\n", " # Guarda os valores das pontas em s\n", " s = (f(a) + f(b)) * h / 2\n", " # olds vai guardar s da iteração anterior\n", " # Começa com valor suficientemente diferente de s\n", " olds = s + 10*epsilon\n", " # Repete a iteração pelo menos 4 vezes e até chegar na precisão\n", " while abs(s - olds) >= epsilon or curriter < 4:\n", " # Ajusta para próxima iteração\n", " curriter, N, h = curriter + 1, N * 2, h / 2\n", " # Se teve muitas iterações e não convergiu, há problema.\n", " if curriter > 25:\n", " raise Exception(\"Falta de convergência em trapezio_precisao\")\n", " olds = s # Guarda aproximação anterior\n", " # Soma novos pontos em s\n", " s = 0\n", " # Os i ímpares são os novos pontos. Pares já incluídos em olds\n", " for i in range(1, N, 2):\n", " s += f(a + i * h)\n", " # Junta contribuição dos novos pontos com anterior reescalado\n", " # para novo valor de h\n", " s = s * h + olds / 2\n", " return s" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Testando agora com a mesma integral anterior:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "trapezio_precisao(math.sin, 0, math.pi/2, 1e-8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Enquanto isso, no mundo real" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como eu já comentei, implementar suas próprias rotinas numéricas (a não ser para estudo) é arriscado e inútil: melhor usar rotinas implementadas por especialistas. No caso do Python, as rotinas para achar raízes e para integração numérica estão disponíveis no pacote SciPy.\n", "\n", "As funções para raízes estão no módulo de otimização:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import scipy.optimize" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O método da bissecção está implementado em `scipy.optimize.bisect`, que tem parâmetros similares à desenvolvida acima, mas a precisão é especificada pelo parâmetro chave/valor `xtol` (existe opcionalmente o parâmetro `rtol` para especificar precisão relativa)." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "scipy.optimize.bisect(lambda x: math.cos(x) - 0.5, 0, 3, xtol=1e-8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "O método da secante é chamado pela função `scipy.optimize.newton` se não for fornecida a derivada da função (se fornecemos a derivada, o chamado método de Newton é usado). Ao invés de aceitar um intervalor para a raiz, esta função aceita um \"chute\" inicial do valor." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "scipy.optimize.newton(lambda x: math.cos(x) - 0.5, 1.5, tol=1e-8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Chegou a hora de comparar os tempos:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit bisseccao(lambda x: math.cos(x)-0.5, 0, 3, 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit scipy.optimize.bisect(lambda x: math.cos(x) - 0.5, 0, 3, xtol=1e-8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit secante(lambda x: math.cos(x)-0.5, 0, 3, 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit scipy.optimize.newton(lambda x: math.cos(x) - 0.5, 1.5, tol=1e-8)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para integração, uma função de fácil uso é a função `quad` do módulo `scipy.integrate`:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import scipy.integrate" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "scipy.integrate.quad(math.sin, 0, math.pi/2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Como você vê, a função retorna dois valores. O primeiro é o valor calculado para a integral, o segundo é uma estimativa do erro. No caso, a função estima que o erro deve ser menor que $1.11 \\cdot 10^{-14}$, o que podemos verificar neste caso." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Para finalizar, a temporização:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit trapezio_precisao(math.sin, 0, math.pi/2, 1e-8)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit scipy.integrate.quad(math.sin, 0, math.pi/2)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python [default]", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.2" } }, "nbformat": 4, "nbformat_minor": 0 }