Skip to content

Commit fa89642

Browse files
committed
✨ feat: adicionar tradução da seção 1.3.3 - Funções como Métodos Gerais
- Tradução completa de "Functions as General Methods" - 27 exemplos de código com CodePlayground - Método da metade do intervalo (bisection method) para encontrar raízes - Busca de ponto fixo e amortecimento médio - Aplicação à computação de raízes quadradas - 5 exercícios traduzidos (1.35-1.39) - 3 notas de rodapé traduzidas - Conceitos: half-interval method, fixed-point search, average damping - Frações contínuas, razão áurea, número de Euler - Completa Fase 1: todas seções 1.3.x agora traduzidas
1 parent c0e89c8 commit fa89642

File tree

1 file changed

+378
-0
lines changed

1 file changed

+378
-0
lines changed

docs/chapter-1/1.3.3.mdx

Lines changed: 378 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,378 @@
1+
# 1.3.3 Funções como Métodos Gerais
2+
3+
import CodePlayground from '@site/src/components/CodePlayground';
4+
5+
Introduzimos funções compostas na seção 1.1.4 como um mecanismo para abstrair padrões de operações numéricas de modo a torná-los independentes dos números particulares envolvidos. Com funções de ordem superior, como a função `integral` da seção 1.3.1, começamos a ver um tipo mais poderoso de abstração: funções usadas para expressar métodos gerais de computação, independentemente das funções particulares envolvidas. Nesta seção discutimos dois exemplos mais elaborados — métodos gerais para encontrar zeros e pontos fixos de funções — e mostramos como esses métodos podem ser expressos diretamente como funções.
6+
7+
## Encontrando raízes de equações pelo método da metade do intervalo
8+
9+
O *método da metade do intervalo* é uma técnica simples mas poderosa para encontrar raízes de uma equação $f(x)=0$, onde $f$ é uma função contínua. A ideia é que, se nos forem dados pontos $a$ e $b$ tais que $f(a) < 0 < f(b)$, então $f$ deve ter pelo menos um zero entre $a$ e $b$. Para localizar um zero, seja $x$ a média de $a$ e $b$ e calcule $f(x)$. Se $f(x) > 0$, então $f$ deve ter um zero entre $a$ e $x$. Se $f(x) < 0$, então $f$ deve ter um zero entre $x$ e $b$. Continuando dessa maneira, podemos identificar intervalos cada vez menores nos quais $f$ deve ter um zero. Quando chegamos a um ponto onde o intervalo é pequeno o suficiente, o processo para. Como o intervalo de incerteza é reduzido pela metade em cada passo do processo, o número máximo de passos necessários cresce como $\Theta(\log(L/T))$, onde $L$ é o comprimento do intervalo original e $T$ é a tolerância de erro (isto é, o tamanho do intervalo que consideraremos "pequeno o suficiente").
10+
11+
Aqui está uma função que implementa esta estratégia:
12+
13+
<CodePlayground
14+
code={`function search(f, neg_point, pos_point) {
15+
const midpoint = average(neg_point, pos_point);
16+
if (close_enough(neg_point, pos_point)) {
17+
return midpoint;
18+
} else {
19+
const test_value = f(midpoint);
20+
return positive(test_value)
21+
? search(f, neg_point, midpoint)
22+
: negative(test_value)
23+
? search(f, midpoint, pos_point)
24+
: midpoint;
25+
}
26+
}`}
27+
hiddenCode={`function average(x, y) {
28+
return (x + y) / 2;
29+
}
30+
function positive(x) { return x > 0; }
31+
function negative(x) { return x < 0; }
32+
function close_enough(x, y) {
33+
return abs(x - y) < 0.001;
34+
}
35+
function abs(x) {
36+
return x >= 0 ? x : -x;
37+
}`}
38+
height={450}
39+
showLineNumbers={false}
40+
/>
41+
42+
Assumimos que inicialmente recebemos a função $f$ juntamente com pontos nos quais seus valores são negativos e positivos. Primeiro calculamos o ponto médio dos dois pontos dados. Em seguida, verificamos se o intervalo dado é pequeno o suficiente e, se for, simplesmente retornamos o ponto médio como nossa resposta. Caso contrário, calculamos como um valor de teste o valor de $f$ no ponto médio. Se o valor de teste for positivo, continuamos o processo com um novo intervalo indo do ponto negativo original até o ponto médio. Se o valor de teste for negativo, continuamos com o intervalo do ponto médio ao ponto positivo. Finalmente, há a possibilidade de que o valor de teste seja 0, caso em que o ponto médio é em si a raiz que estamos procurando.
43+
44+
<a name="footnote-link-1"></a>
45+
Para testar se os pontos finais estão "próximos o suficiente", podemos usar uma função similar à usada na seção 1.1.7 para calcular raízes quadradas:[<sup>1</sup>](#footnote-1)
46+
47+
<CodePlayground
48+
code={`function close_enough(x, y) {
49+
return abs(x - y) < 0.001;
50+
}`}
51+
hiddenCode={`function abs(x) {
52+
return x >= 0 ? x : -x;
53+
}`}
54+
height={150}
55+
showLineNumbers={false}
56+
/>
57+
58+
<a name="footnote-link-2"></a>
59+
A função `search` é complicada para usar diretamente, porque podemos acidentalmente fornecer pontos nos quais os valores de $f$ não têm o sinal necessário, caso em que obteríamos uma resposta errada. Em vez disso, usaremos `search` por meio da seguinte função, que verifica quais dos pontos finais tem um valor de função negativo e qual tem um valor positivo, e chama a função `search` de acordo. Se a função tiver o mesmo sinal nos dois pontos dados, o método da metade do intervalo não pode ser usado, caso em que a função sinaliza um erro.[<sup>2</sup>](#footnote-2)
60+
61+
<CodePlayground
62+
code={`function half_interval_method(f, a, b) {
63+
const a_value = f(a);
64+
const b_value = f(b);
65+
return negative(a_value) && positive(b_value)
66+
? search(f, a, b)
67+
: negative(b_value) && positive(a_value)
68+
? search(f, b, a)
69+
: error("values are not of opposite sign");
70+
}`}
71+
hiddenCode={`function average(x, y) {
72+
return (x + y) / 2;
73+
}
74+
function positive(x) { return x > 0; }
75+
function negative(x) { return x < 0; }
76+
function close_enough(x, y) {
77+
return abs(x - y) < 0.001;
78+
}
79+
function abs(x) {
80+
return x >= 0 ? x : -x;
81+
}
82+
function search(f, neg_point, pos_point) {
83+
const midpoint = average(neg_point, pos_point);
84+
if (close_enough(neg_point, pos_point)) {
85+
return midpoint;
86+
} else {
87+
const test_value = f(midpoint);
88+
return positive(test_value)
89+
? search(f, neg_point, midpoint)
90+
: negative(test_value)
91+
? search(f, midpoint, pos_point)
92+
: midpoint;
93+
}
94+
}
95+
function error(msg) {
96+
throw new Error(msg);
97+
}`}
98+
height={300}
99+
showLineNumbers={false}
100+
/>
101+
102+
O exemplo a seguir usa o método da metade do intervalo para aproximar $\pi$ como a raiz entre 2 e 4 de $\sin x = 0$:
103+
104+
<CodePlayground
105+
code={`half_interval_method(math_sin, 2, 4);`}
106+
hiddenCode={`function average(x, y) {
107+
return (x + y) / 2;
108+
}
109+
function positive(x) { return x > 0; }
110+
function negative(x) { return x < 0; }
111+
function close_enough(x, y) {
112+
return abs(x - y) < 0.001;
113+
}
114+
function abs(x) {
115+
return x >= 0 ? x : -x;
116+
}
117+
function search(f, neg_point, pos_point) {
118+
const midpoint = average(neg_point, pos_point);
119+
if (close_enough(neg_point, pos_point)) {
120+
return midpoint;
121+
} else {
122+
const test_value = f(midpoint);
123+
return positive(test_value)
124+
? search(f, neg_point, midpoint)
125+
: negative(test_value)
126+
? search(f, midpoint, pos_point)
127+
: midpoint;
128+
}
129+
}
130+
function error(msg) {
131+
throw new Error(msg);
132+
}
133+
function half_interval_method(f, a, b) {
134+
const a_value = f(a);
135+
const b_value = f(b);
136+
return negative(a_value) && positive(b_value)
137+
? search(f, a, b)
138+
: negative(b_value) && positive(a_value)
139+
? search(f, b, a)
140+
: error("values are not of opposite sign");
141+
}
142+
const math_sin = Math.sin;`}
143+
height={100}
144+
showLineNumbers={false}
145+
/>
146+
147+
Aqui está outro exemplo, usando o método da metade do intervalo para procurar uma raiz da equação $x^3 - 2x - 3 = 0$ entre 1 e 2:
148+
149+
<CodePlayground
150+
code={`half_interval_method(x => x * x * x - 2 * x - 3, 1, 2);`}
151+
hiddenCode={`function average(x, y) {
152+
return (x + y) / 2;
153+
}
154+
function positive(x) { return x > 0; }
155+
function negative(x) { return x < 0; }
156+
function close_enough(x, y) {
157+
return abs(x - y) < 0.001;
158+
}
159+
function abs(x) {
160+
return x >= 0 ? x : -x;
161+
}
162+
function search(f, neg_point, pos_point) {
163+
const midpoint = average(neg_point, pos_point);
164+
if (close_enough(neg_point, pos_point)) {
165+
return midpoint;
166+
} else {
167+
const test_value = f(midpoint);
168+
return positive(test_value)
169+
? search(f, neg_point, midpoint)
170+
: negative(test_value)
171+
? search(f, midpoint, pos_point)
172+
: midpoint;
173+
}
174+
}
175+
function error(msg) {
176+
throw new Error(msg);
177+
}
178+
function half_interval_method(f, a, b) {
179+
const a_value = f(a);
180+
const b_value = f(b);
181+
return negative(a_value) && positive(b_value)
182+
? search(f, a, b)
183+
: negative(b_value) && positive(a_value)
184+
? search(f, b, a)
185+
: error("values are not of opposite sign");
186+
}`}
187+
height={100}
188+
showLineNumbers={false}
189+
/>
190+
191+
## Encontrando pontos fixos de funções
192+
193+
Um número $x$ é chamado de *ponto fixo* de uma função $f$ se $x$ satisfaz a equação $f(x)=x$. Para algumas funções $f$ podemos localizar um ponto fixo começando com um palpite inicial e aplicando $f$ repetidamente,
194+
195+
$$
196+
f(x), \quad f(f(x)), \quad f(f(f(x))), \quad \ldots
197+
$$
198+
199+
até que o valor não mude muito. Usando esta ideia, podemos desenvolver uma função `fixed_point` que recebe como entradas uma função e um palpite inicial e produz uma aproximação de um ponto fixo da função. Aplicamos a função repetidamente até encontrarmos dois valores sucessivos cuja diferença é menor que alguma tolerância prescrita:
200+
201+
<CodePlayground
202+
code={`const tolerance = 0.00001;
203+
function fixed_point(f, first_guess) {
204+
function close_enough(x, y) {
205+
return abs(x - y) < tolerance;
206+
}
207+
function try_with(guess) {
208+
const next = f(guess);
209+
return close_enough(guess, next)
210+
? next
211+
: try_with(next);
212+
}
213+
return try_with(first_guess);
214+
}`}
215+
hiddenCode={`function abs(x) {
216+
return x >= 0 ? x : -x;
217+
}`}
218+
height={450}
219+
showLineNumbers={false}
220+
/>
221+
222+
Por exemplo, podemos usar este método para aproximar o ponto fixo do cosseno, começando com 1 como palpite inicial:[<sup>3</sup>](#footnote-3)
223+
224+
<CodePlayground
225+
code={`fixed_point(math_cos, 1);`}
226+
hiddenCode={`const math_cos = Math.cos;
227+
function abs(x) {
228+
return x >= 0 ? x : -x;
229+
}
230+
const tolerance = 0.00001;
231+
function fixed_point(f, first_guess) {
232+
function close_enough(x, y) {
233+
return abs(x - y) < tolerance;
234+
}
235+
function try_with(guess) {
236+
const next = f(guess);
237+
return close_enough(guess, next)
238+
? next
239+
: try_with(next);
240+
}
241+
return try_with(first_guess);
242+
}`}
243+
height={100}
244+
showLineNumbers={false}
245+
/>
246+
247+
Da mesma forma, podemos encontrar uma solução da equação $y = \sin y + \cos y$:
248+
249+
<CodePlayground
250+
code={`fixed_point(y => math_sin(y) + math_cos(y), 1);`}
251+
hiddenCode={`const math_sin = Math.sin;
252+
const math_cos = Math.cos;
253+
function abs(x) {
254+
return x >= 0 ? x : -x;
255+
}
256+
const tolerance = 0.00001;
257+
function fixed_point(f, first_guess) {
258+
function close_enough(x, y) {
259+
return abs(x - y) < tolerance;
260+
}
261+
function try_with(guess) {
262+
const next = f(guess);
263+
return close_enough(guess, next)
264+
? next
265+
: try_with(next);
266+
}
267+
return try_with(first_guess);
268+
}`}
269+
height={100}
270+
showLineNumbers={false}
271+
/>
272+
273+
O processo de busca de ponto fixo nos lembra o processo que usamos para encontrar raízes quadradas. Ambos são baseados na ideia de melhorar repetidamente um palpite até que o resultado satisfaça algum critério. De fato, podemos formular facilmente a computação de raiz quadrada como uma busca de ponto fixo. Calcular a raiz quadrada de algum número $x$ requer encontrar um $y$ tal que $y^2 = x$. Colocando essa equação na forma equivalente $y = x/y$, reconhecemos que estamos procurando um ponto fixo da função $y \mapsto x/y$, e podemos, portanto, tentar calcular raízes quadradas como
274+
275+
```javascript
276+
function sqrt(x) {
277+
return fixed_point(y => x / y, 1);
278+
}
279+
```
280+
281+
Infelizmente, esta busca de ponto fixo não converge. Considere um palpite inicial $y_1$. O próximo palpite é $y_2 = x/y_1$ e o próximo palpite é $y_3 = x/y_2 = x/(x/y_1) = y_1$. Isso resulta em um loop infinito no qual os dois palpites $y_1$ e $y_2$ se repetem indefinidamente, oscilando em torno da resposta.
282+
283+
Uma maneira de controlar tais oscilações é evitar que os palpites mudem tanto. Como a resposta está sempre entre nosso palpite $y$ e $x/y$, podemos fazer um novo palpite que não está tão longe de $y$ quanto $x/y$ fazendo a média de $y$ com $x/y$, para que o próximo palpite após $y$ seja $(1/2)(y + x/y)$ em vez de $x/y$. O processo de fazer tal sequência de palpites é simplesmente o processo de procurar por um ponto fixo de $y \mapsto (1/2)(y + x/y)$:
284+
285+
<CodePlayground
286+
code={`function sqrt(x) {
287+
return fixed_point(y => average(y, x / y), 1);
288+
}`}
289+
hiddenCode={`function average(x, y) {
290+
return (x + y) / 2;
291+
}
292+
function abs(x) {
293+
return x >= 0 ? x : -x;
294+
}
295+
const tolerance = 0.00001;
296+
function fixed_point(f, first_guess) {
297+
function close_enough(x, y) {
298+
return abs(x - y) < tolerance;
299+
}
300+
function try_with(guess) {
301+
const next = f(guess);
302+
return close_enough(guess, next)
303+
? next
304+
: try_with(next);
305+
}
306+
return try_with(first_guess);
307+
}`}
308+
height={150}
309+
showLineNumbers={false}
310+
/>
311+
312+
(Observe que $y = (1/2)(y + x/y)$ é uma transformação simples da equação $y = x/y$; para derivar isso, some $y$ a ambos os lados da equação e divida por 2.)
313+
314+
Com essa modificação, a função de raiz quadrada funciona. Na verdade, se desenrolarmos as declarações, podemos ver que a sequência de aproximações ao ponto fixo de busca de ponto fixo é precisamente a mesma sequência gerada pela nossa função de raiz quadrada original da seção 1.1.7. Este método de fazer a média de aproximações sucessivas a uma solução, uma técnica que chamamos de *amortecimento médio*, geralmente ajuda a convergência de buscas de ponto fixo.
315+
316+
## Exercício 1.35
317+
318+
<a name="ex-1-35"></a>
319+
Mostre que a razão áurea $\phi$ (seção 1.2.2) é um ponto fixo da transformação $x \mapsto 1 + 1/x$, e use isso para calcular $\phi$ por meio da função `fixed_point`.
320+
321+
## Exercício 1.36
322+
323+
<a name="ex-1-36"></a>
324+
Modifique `fixed_point` para que ela imprima a sequência de aproximações que gera, usando a função primitiva `display` mostrada no exercício 1.22. Então encontre uma solução de $x^x = 1000$ encontrando um ponto fixo de $x \mapsto \log(1000)/\log(x)$. (Use a função primitiva `math_log` do JavaScript, que calcula logaritmos naturais.) Compare o número de passos necessários com e sem amortecimento médio. (Observe que você não pode iniciar `fixed_point` com um palpite de 1, pois isso causaria divisão por $\log(1) = 0$.)
325+
326+
## Exercício 1.37
327+
328+
<a name="ex-1-37"></a>
329+
**a.** Uma *fração contínua infinita* é uma expressão da forma
330+
331+
$$
332+
f = \frac{N_1}{D_1 + \frac{N_2}{D_2 + \frac{N_3}{D_3 + \cdots}}}
333+
$$
334+
335+
Como exemplo, pode-se mostrar que a expansão em fração contínua infinita com os $N_i$ e os $D_i$ todos iguais a 1 produz $1/\phi$, onde $\phi$ é a razão áurea (descrita na seção 1.2.2). Uma forma de aproximar uma fração contínua infinita é truncá-la após um dado número de termos. Tal truncamento — uma chamada *fração contínua de $k$ termos* — tem a forma
336+
337+
$$
338+
\frac{N_1}{D_1 + \frac{N_2}{\ddots + \frac{N_K}{D_K}}}
339+
$$
340+
341+
Suponha que `n` e `d` sejam funções de um argumento (o índice do termo $i$) que retornam os $N_i$ e $D_i$ dos termos da fração contínua. Declare uma função `cont_frac` tal que avaliar `cont_frac(n, d, k)` calcule o valor de uma fração contínua de $k$ termos. Verifique sua função aproximando $1/\phi$ usando
342+
343+
```javascript
344+
cont_frac(i => 1, i => 1, k)
345+
```
346+
347+
para valores sucessivos de `k`. Quão grande você tem que fazer `k` para obter uma aproximação que é precisa em 4 casas decimais?
348+
349+
**b.** Se sua função `cont_frac` gera um processo recursivo, escreva uma que gere um processo iterativo. Se ela gera um processo iterativo, escreva uma que gere um processo recursivo.
350+
351+
## Exercício 1.38
352+
353+
<a name="ex-1-38"></a>
354+
Em 1737, o matemático suíço Leonhard Euler publicou um artigo *De Fractionibus Continuis*, que incluía uma expansão em fração contínua para $e - 2$, onde $e$ é a base dos logaritmos naturais. Nesta fração, os $N_i$ são todos 1, e os $D_i$ são sucessivamente 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, ... Escreva um programa que usa sua função `cont_frac` do exercício 1.37 para aproximar $e$, baseado na expansão de Euler.
355+
356+
## Exercício 1.39
357+
358+
<a name="ex-1-39"></a>
359+
Uma fração contínua para a função tangente foi publicada em 1770 pelo matemático alemão J.H. Lambert:
360+
361+
$$
362+
\tan x = \frac{x}{1 - \frac{x^2}{3 - \frac{x^2}{5 - \cdots}}}
363+
$$
364+
365+
onde $x$ está em radianos. Declare uma função `tan_cf(x, k)` que calcula uma aproximação da função tangente baseada na fórmula de Lambert. A constante `k` especifica o número de termos a calcular, como no exercício 1.37.
366+
367+
---
368+
369+
## Notas de Rodapé
370+
371+
<a name="footnote-1"></a>
372+
**[1](#footnote-link-1)** Usamos 0.001 como um número "pequeno" representativo para indicar uma tolerância para o erro aceitável em um cálculo. A tolerância apropriada para um cálculo real depende do problema a ser resolvido e das limitações do computador e do algoritmo. Isso é muitas vezes uma consideração muito sutil, exigindo ajuda de um analista numérico ou algum outro tipo de mago.
373+
374+
<a name="footnote-2"></a>
375+
**[2](#footnote-link-2)** Isso pode ser feito usando `error`, que recebe como argumento uma string que é exibida como mensagem de erro juntamente com a informação de que o programa foi interrompido.
376+
377+
<a name="footnote-3"></a>
378+
**[3](#footnote-link-3)** Tente isso durante um intervalo de ócio. Veja se você consegue usar isso para implementar a calculadora de ``logaritmo'' inventada pelo brincalhão mencionado na nota de rodapé da seção 1.3.3.

0 commit comments

Comments
 (0)