Pular para conteúdo

Lista 04 · Bisseção e falsa posição

Acompanha o capítulo 4. Todo problema aqui vira \(f(x) = 0\): a ressonância de um circuito, o atrito num cano de água, os juros de um financiamento. Comece sempre olhando o gráfico.

Abrir no Colab

Baixar o caderno

Como esta lista funciona

Abra o caderno no Colab, escreva a sua solução no lugar do # TODO e rode a célula de teste logo abaixo. Ela mesma diz o que passou e o que falhou — você não precisa esperar correção de ninguém.

O código de cada exercício também está aqui embaixo, caso você prefira colar num editor local. Os testes comparam números com tolerância.

Exercícios marcados com ✏️ são à mão, no papel, com 4 casas decimais — como na parte em papel das avaliações.

Exercício 01

✏️ À mão. Queremos \(\sqrt[3]{20}\), a raiz de \(f(x) = x^3 - 20\).

a) Mostre que existe uma raiz em \([2, 3]\).

b) Faça 4 iterações da bisseção, preenchendo a tabela (4 casas decimais):

it \(a\) \(b\) \(x_m\) \(f(x_m)\) novo intervalo
1 2 3
2
3
4

c) Quantas iterações seriam necessárias para garantir erro menor que \(0{,}001\), partindo de \([2, 3]\)?

d) Faça uma iteração da falsa posição em \([2, 3]\). Ela chega mais perto de \(\sqrt[3]{20} = 2{,}7144\) do que a primeira iteração da bisseção?

Exercício 02

Escreva bissecao(f, a, b, tol, max_iter), que aplica a bisseção a f no intervalo [a, b] e devolve o ponto médio da última iteração. Pare quando o erro relativo aproximado (em %) ficar abaixo de tol, ou depois de max_iter iterações.

def g(x):
    return x**2 - 2

bissecao(g, 1, 2, 0.0001, 100)    # -> 1.41421... (a raiz de 2)
def bissecao(f, a, b, tol, max_iter):
    """Raiz de f em [a, b] pela bisseção; para quando eps_a (%) < tol."""
    xm = a
    # TODO: laço até max_iter: guarda o anterior, ponto médio, teste de sinal
    # para escolher a metade, e break quando eps_a < tol
    return xm


# --- testes (não altere) ---
def g_ex02(x):
    return x**2 - 2


def h_ex02(x):
    return x**3 - 20


r = bissecao(g_ex02, 1, 2, 0.0001, 100)
assert abs(r - 1.41421356) < 1e-5, f"a raiz de x² - 2 é 1.41421... (veio {r})"
r = bissecao(h_ex02, 2, 3, 0.0001, 100)
assert abs(r - 2.71441762) < 1e-5, f"a raiz cúbica de 20 é 2.71442... (veio {r})"
r = bissecao(h_ex02, 2, 3, 0.0001, 1)
assert r == 2.5, "com max_iter = 1, só uma iteração: o ponto médio 2.5"
print("Exercício 02: todos os testes passaram!")

Exercício 03

Escreva iteracoes_necessarias(a, b, delta), que devolve o menor número de iterações \(n\) da bisseção que garante \(\dfrac{b - a}{2^n} < \delta\).

Use um while: comece com a largura \(b - a\) e vá dividindo por 2, contando.

iteracoes_necessarias(2, 3, 0.001)       # -> 10
iteracoes_necessarias(100, 200, 0.01)    # -> 14
def iteracoes_necessarias(a, b, delta):
    """Menor n com (b - a) / 2**n < delta."""
    n = 0
    largura = b - a
    # TODO: while a largura não ficar menor que delta: divide por 2 e conta
    return n


# --- testes (não altere) ---
assert iteracoes_necessarias(2, 3, 0.001) == 10
assert iteracoes_necessarias(100, 200, 0.01) == 14
assert iteracoes_necessarias(0, 1, 0.6) == 1, "1/2 = 0.5 já é menor que 0.6"
assert iteracoes_necessarias(0, 8, 1) == 4, "8/2**3 = 1 NÃO é menor que 1"
print("Exercício 03: todos os testes passaram!")

Exercício 04

Escreva falsa_posicao(f, a, b, tol, max_iter), igual à bisseção do ex02, mas com o ponto

\[ x_r = b - \frac{f(b)\,(a - b)}{f(a) - f(b)}. \]
falsa_posicao(g, 1, 2, 0.0001, 100)    # -> 1.41421...

Conte as iterações das duas em \(x^2 - 2\): qual precisou de menos?

def falsa_posicao(f, a, b, tol, max_iter):
    """Raiz de f em [a, b] pela falsa posição; para quando eps_a (%) < tol."""
    xr = a
    # TODO: o mesmo laço da bisseção, trocando o ponto médio por x_r
    return xr


# --- testes (não altere) ---
def g_ex04(x):
    return x**2 - 2


r = falsa_posicao(g_ex04, 1, 2, 0.0001, 100)
assert abs(r - 1.41421356) < 1e-5, f"veio {r}"
r = falsa_posicao(g_ex04, 1, 2, 0.0001, 1)
assert abs(r - 4 / 3) < 1e-12, "a primeira reta cruza o zero em 4/3"
print("Exercício 04: todos os testes passaram!")

Exercício 05

Eletrônica — ressonância. Num circuito com indutor \(L\) e capacitor \(C\) em série, a reatância (a "resistência" à corrente alternada) é

\[ X(f) = 2\pi f L - \frac{1}{2\pi f C}. \]

Na frequência de ressonância, \(X = 0\) e a corrente é máxima: é o princípio da sintonia de um rádio. Escreva frequencia_ressonancia(L, C), que acha essa raiz com a bissecao (já escrita) no intervalo de 100 Hz a 100 kHz, com tol = 1e-6 e max_iter = 200.

Confira com a fórmula \(f_0 = \dfrac{1}{2\pi\sqrt{LC}}\).

Dica: dentro da sua função, defina outra função reatancia(f) que usa o L e o C recebidos, e passe-a para a bisseção.

import numpy as np

def bissecao(f, a, b, tol, max_iter):
    """Bisseção. JÁ ESCRITA."""
    xm = a
    for it in range(max_iter):
        anterior = xm
        xm = (a + b) / 2
        if f(a) * f(xm) < 0:
            b = xm
        else:
            a = xm
        if abs((xm - anterior) / xm) * 100 < tol:
            break
    return xm


def frequencia_ressonancia(L, C):
    """Frequência (Hz) em que a reatância do circuito LC em série é zero."""
    # TODO: defina reatancia(f) aqui dentro e chame bissecao(reatancia, 100, 100000, 1e-6, 200)
    pass


# --- testes (não altere) ---
import numpy as np


f0 = frequencia_ressonancia(10e-3, 100e-9)
assert abs(f0 - 1 / (2 * np.pi * np.sqrt(10e-3 * 100e-9))) < 0.01, f"veio {f0}"
f0 = frequencia_ressonancia(1e-3, 4.7e-6)
assert abs(f0 - 1 / (2 * np.pi * np.sqrt(1e-3 * 4.7e-6))) < 0.01, "outro circuito"
print("Exercício 05: todos os testes passaram!")

Exercício 06

Hidráulica — o atrito num cano. O fator de atrito \(f\) de um cano (que decide a potência da bomba de uma adutora) obedece à equação de Colebrook:

\[ \frac{1}{\sqrt{f}} = -2\log_{10}\!\left(\frac{\varepsilon/D}{3{,}7} + \frac{2{,}51}{Re\,\sqrt{f}}\right), \]

em que \(\varepsilon/D\) é a rugosidade relativa do cano e \(Re\) é o número de Reynolds do escoamento. Não dá para isolar \(f\). A função colebrook(f, ...) (já escrita) calcula o lado esquerdo menos o direito: a raiz dela é o fator de atrito.

Escreva fator_de_atrito(rugosidade, reynolds), que acha essa raiz com a bissecao (já escrita) em \([0{,}008;\ 0{,}1]\).

fator_de_atrito(0.0001, 1e5)    # -> ≈ 0.01851
import numpy as np

def bissecao(f, a, b, tol, max_iter):
    """Bisseção. JÁ ESCRITA."""
    xm = a
    for it in range(max_iter):
        anterior = xm
        xm = (a + b) / 2
        if f(a) * f(xm) < 0:
            b = xm
        else:
            a = xm
        if abs((xm - anterior) / xm) * 100 < tol:
            break
    return xm


def colebrook(f, rugosidade, reynolds):
    """Lado esquerdo menos o direito da equação de Colebrook. JÁ ESCRITA."""
    log10 = np.log(rugosidade / 3.7 + 2.51 / (reynolds * np.sqrt(f))) / np.log(10)
    return 1 / np.sqrt(f) + 2 * log10


def fator_de_atrito(rugosidade, reynolds):
    """Fator de atrito de Darcy de um cano, pela equação de Colebrook."""
    # TODO: defina g(f) que chama colebrook(f, rugosidade, reynolds) e use a bissecao
    pass


# --- testes (não altere) ---
assert abs(fator_de_atrito(0.0001, 1e5) - 0.018513866104185588) < 1e-5
assert abs(fator_de_atrito(0.001, 1e6) - 0.019943465910851964) < 1e-5
print("Exercício 06: todos os testes passaram!")

Exercício 07

Finanças — os juros escondidos. Uma loja anuncia um carro de R$ 30 000 "em 36 parcelas de R$ 1 100". Numa prestação fixa (tabela Price), o valor à vista, a parcela \(P\), o número de parcelas \(n\) e a taxa mensal \(i\) se relacionam por

\[ \text{valor} = P\,\frac{1 - (1 + i)^{-n}}{i}. \]

Escreva juros_mensais(valor, parcela, n), que acha \(i\) com a bissecao (já escrita) entre \(0{,}0001\) e \(0{,}5\).

juros_mensais(30000, 1100, 36)    # -> ≈ 0.01585  (1,585 % ao mês)

Quanto é isso ao ano? (Não é \(12 \times 1{,}585\,\%\).)

def bissecao(f, a, b, tol, max_iter):
    """Bisseção. JÁ ESCRITA."""
    xm = a
    for it in range(max_iter):
        anterior = xm
        xm = (a + b) / 2
        if f(a) * f(xm) < 0:
            b = xm
        else:
            a = xm
        if abs((xm - anterior) / xm) * 100 < tol:
            break
    return xm


def juros_mensais(valor, parcela, n):
    """Taxa de juros mensal (fração) de um financiamento em n parcelas iguais."""
    # TODO: defina diferenca(i) = valor - parcela * (1 - (1 + i)**(-n)) / i e use a bissecao
    pass


# --- testes (não altere) ---
assert abs(juros_mensais(30000, 1100, 36) - 0.015850879862043078) < 1e-6
assert abs(juros_mensais(1000, 100, 12) - 0.029228540951898314) < 1e-6
print("Exercício 07: todos os testes passaram!")

Exercício 08

Várias raízes: a busca incremental. A bisseção precisa de um intervalo com uma troca de sinal. Quando a função tem várias raízes, o primeiro passo é procurá-las: dividir \([a, b]\) em \(n\) pedaços iguais e testar o sinal nas pontas de cada um.

Escreva intervalos_com_raiz(f, a, b, n), que devolve a lista de tuplas (x_esq, x_dir) dos pedaços em que f troca de sinal.

import numpy as np
intervalos_com_raiz(np.sin, 0.5, 10, 100)
# -> três pedaços: um perto de pi, um de 2*pi e um de 3*pi

Depois de passar: o que acontece com n = 3? Por que a busca pode "pular" duas raízes muito próximas?

def intervalos_com_raiz(f, a, b, n):
    """Divide [a, b] em n pedaços iguais e devolve a lista dos pedaços
    (x_esq, x_dir) em que f troca de sinal."""
    largura = (b - a) / n
    achados = []
    # TODO: para cada pedaço k, calcule as pontas e teste f(esq) * f(dir) < 0
    return achados


# --- testes (não altere) ---
import numpy as np


r = intervalos_com_raiz(np.sin, 0.5, 10, 100)
assert len(r) == 3, f"sen(x) tem 3 raízes entre 0.5 e 10 (veio {len(r)})"
for k in range(3):
    esq, dir = r[k]
    assert esq < (k + 1) * np.pi < dir, f"o pedaço {k} devia conter {k + 1}*pi"
assert intervalos_com_raiz(np.sin, 0.5, 3, 10) == [], "sen não troca de sinal entre 0.5 e 3"
print("Exercício 08: todos os testes passaram!")