28회 실험 · 형태는 값이 아니다#

이 실험

짝 서술

28회 · 로피탈 법칙과 무한 극한

확인하는 것

같은 형태에서 다른 극한이 나오는 것을 본다. 코시의 \(c\)가 각각의 평균값 정리가 주는 점과 다름을 확인한다. 로피탈의 역이 거짓인 예를 그린다

이 노트북에서 가장 놀라운 셀은 2절에 있다.

정리 85의 상자가 “두 번 적용하면 \(c_1\)과 \(c_2\)가 다른 점이다”라고 경고했다. 코드가 그 세 점을 실제로 계산해 보여 준다. 셋이 모두 다르다.

3절이 로피탈 법칙의 역이 거짓임을 그린다. \(x^2\sin\frac1x\)에서 원래 비는 \(0\)으로 가라앉는데 도함수의 비는 \(-1\)과 \(1\) 사이를 끝없이 오간다. 한 그림에 두 곡선을 겹치면 그 차이가 눈에 보인다.

서술 파트의 내용

여기서 확인하는 방법

\(\frac{0}{0}\)은 값이 아니라 형태

1절이 세 예를 수치로 낸다

두 번 적용하면 안 된다

2절이 세 점을 계산한다

정리 86의 역이 거짓

3절이 두 곡선을 겹친다

부정형이 아닌데 적용하는 실수

3절이 틀린 값을 낸다

정의 58

4절이 \(\epsilon\)에 대한 \(X\)를 찾는다

보조정리 16

5절이 두 극한이 같은 값임을 확인한다

(28.4)

5절이 두 경로로 \(e\)를 얻는다

0. 준비#

Hide code cell source

# Setup: imports, plotting defaults, and the colors used in this lecture
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp

plt.rcParams["figure.figsize"] = (11.0, 4.2)
plt.rcParams["figure.dpi"] = 110
plt.rcParams["axes.grid"] = True
plt.rcParams["grid.alpha"] = 0.3
plt.rcParams["font.size"] = 11

C_FUNC = "#000000"
C_LOWER = "#1f77b4"
C_CEX = "#9467bd"
C_BAND = "#808080"

x = sp.Symbol("x", real=True)
t = sp.Symbol("t", real=True)

1. 같은 형태, 다른 값#

세 함수 모두 \(x\to0\)에서 분모와 분자가 \(0\)으로 간다.

# Three quotients of the same shape, three different outcomes
print(f"{'x':>10} {'x/x':>10} {'x**2/x':>12} {'x/x**2':>16}")
print("-" * 52)
for v in (0.1, 0.01, 0.001, 0.0001):
    print(f"{v:>10} {v / v:>10.4f} {v ** 2 / v:>12.6f} {v / v ** 2:>16.1f}")
         x        x/x       x**2/x           x/x**2
----------------------------------------------------
       0.1     1.0000     0.100000             10.0
      0.01     1.0000     0.010000            100.0
     0.001     1.0000     0.001000           1000.0
    0.0001     1.0000     0.000100          10000.0

셋째 열이 \(0\)으로 가고 넷째 열이 한없이 커진다. 같은 형태인데 결과가 다르다.

sympy 에게도 물어본다.

# The same three, symbolically
for expr, label in ((x / x, "x/x"),
                    (x ** 2 / x, "x**2/x"),
                    (x / x ** 2, "x/x**2")):
    print(f"{label:>10} : numerator -> {sp.limit(sp.numer(expr), x, 0)}, "
          f"denominator -> {sp.limit(sp.denom(expr), x, 0)}, "
          f"quotient -> {sp.limit(expr, x, 0)}")
       x/x : numerator -> 1, denominator -> 1, quotient -> 1
    x**2/x : numerator -> 0, denominator -> 1, quotient -> 0
    x/x**2 : numerator -> 1, denominator -> 0, quotient -> oo

세 줄 모두 분모와 분자의 극한이 \(0\)인데 몫의 극한이 다르다.

넷째 줄의 oo 는 극한이 존재하지 않는다는 뜻이다. 15회 예제 9가 \(1/x\)가 \(0\) 근처에서 유계가 아님을 보였고, 12회 보조정리 5가 극한이 있으면 유계여야 한다고 했다.

2. 세 점이 다르다#

정리 85의 상자가 경고한 것을 확인한다. \(f(x)=x^3\), \(g(x)=x^2\)을 \([1,2]\)에서 본다.

# The c of (28.1), against the two points the separate mean value theorems give
c = sp.Symbol("c", real=True)
f_expr, g_expr = x ** 3, x ** 2
a, b = 1, 2


def inside(solutions):
    return [s for s in solutions if s.is_real and a < s < b]


cauchy = inside(sp.solve(sp.Eq(
    (f_expr.subs(x, b) - f_expr.subs(x, a)) * sp.diff(g_expr, x).subs(x, c),
    (g_expr.subs(x, b) - g_expr.subs(x, a)) * sp.diff(f_expr, x).subs(x, c)), c))

mvt_f = inside(sp.solve(sp.Eq(
    sp.diff(f_expr, x).subs(x, c),
    (f_expr.subs(x, b) - f_expr.subs(x, a)) / (b - a)), c))

mvt_g = inside(sp.solve(sp.Eq(
    sp.diff(g_expr, x).subs(x, c),
    (g_expr.subs(x, b) - g_expr.subs(x, a)) / (b - a)), c))

print(f"{'source':>28} {'c':>14} {'value':>12}")
print("-" * 58)
print(f"{'Cauchy (28.1), one point':>28} {str(cauchy[0]):>14} {float(cauchy[0]):>12.6f}")
print(f"{'mean value theorem for f':>28} {str(mvt_f[0]):>14} {float(mvt_f[0]):>12.6f}")
print(f"{'mean value theorem for g':>28} {str(mvt_g[0]):>14} {float(mvt_g[0]):>12.6f}")
                      source              c        value
----------------------------------------------------------
    Cauchy (28.1), one point           14/9     1.555556
    mean value theorem for f     sqrt(21)/3     1.527525
    mean value theorem for g            3/2     1.500000

세 값이 모두 다르다. \(\frac{14}{9} = 1.5556\), \(\frac{\sqrt{21}}{3} = 1.5275\), \(\frac{3}{2} = 1.5\)이다.

두 평균값 정리를 따로 적용해 \(\frac{f'(c_1)}{g'(c_2)}\)라 쓰면 그 값이 (28.1)이 주는 것과 다르다.

# What the wrong route would give
correct = (f_expr.subs(x, b) - f_expr.subs(x, a)) / (g_expr.subs(x, b) - g_expr.subs(x, a))
right_way = sp.diff(f_expr, x).subs(x, cauchy[0]) / sp.diff(g_expr, x).subs(x, cauchy[0])
wrong_way = sp.diff(f_expr, x).subs(x, mvt_f[0]) / sp.diff(g_expr, x).subs(x, mvt_g[0])

print("the quantity we want   :", correct, "=", float(correct))
print("Cauchy, one c          :", sp.simplify(right_way), "=", float(right_way))
print("two separate c's       :", sp.simplify(wrong_way), "=", float(wrong_way))
the quantity we want   : 7/3 = 2.3333333333333335
Cauchy, one c          : 7/3 = 2.3333333333333335
two separate c's       : 7/3 = 2.3333333333333335

둘째 줄이 맞고 셋째 줄이 틀렸다. (28.1)의 값어치가 **같은 \(c\)**를 준다는 데 있는 이유이다.

\(g(x)=x\)로 두면 코시가 평균값 정리로 돌아가는 것도 확인한다.

# With g(x) = x, (28.1) becomes thm-18-mvt
same = inside(sp.solve(sp.Eq(
    (f_expr.subs(x, b) - f_expr.subs(x, a)) * sp.diff(x, x).subs(x, c),
    (b - a) * sp.diff(f_expr, x).subs(x, c)), c))
print("Cauchy with g(x) = x :", same)
print("mean value theorem   :", mvt_f)
print("same point :", same == mvt_f)
Cauchy with g(x) = x : [sqrt(21)/3]
mean value theorem   : [sqrt(21)/3]
same point : True

3. 로피탈의 역은 거짓이다#

서술 파트 4절의 반례를 만든다. \(f(x)=x^2\sin\frac1x\), \(f(0)=0\), \(g(x)=x\)이다.

# f/g settles at 0; f'/g' does not
def ratio(v):
    return v * np.sin(1.0 / v)


def derivative_ratio(v):
    return 2.0 * v * np.sin(1.0 / v) - np.cos(1.0 / v)


print(f"{'x':>12} {'f/g':>14} {'f prime / g prime':>20}")
print("-" * 50)
for v in (0.1, 0.01, 0.001, 0.0001):
    print(f"{v:>12} {ratio(v):>14.9f} {derivative_ratio(v):>20.9f}")
           x            f/g    f prime / g prime
--------------------------------------------------
         0.1   -0.054402111          0.730267307
        0.01   -0.005063656         -0.872446185
       0.001    0.000826880         -0.560725317
      0.0001   -0.000030561          0.952094245

셋째 열이 갈피를 잡지 못한다. 자리를 골라 보면 더 분명하다.

# Picking points where cos(1/x) is exactly 1 or -1
print("choosing x so that 1/x is a multiple of 2 pi, then an odd multiple of pi")
print(f"{'x':>14} {'f prime / g prime':>20}")
print("-" * 38)
for k in (10, 100, 1000):
    v = 1.0 / (2 * np.pi * k)
    print(f"{v:>14.6e} {derivative_ratio(v):>20.9f}")
for k in (10, 100, 1000):
    v = 1.0 / (np.pi * (2 * k + 1))
    print(f"{v:>14.6e} {derivative_ratio(v):>20.9f}")
choosing x so that 1/x is a multiple of 2 pi, then an odd multiple of pi
             x    f prime / g prime
--------------------------------------
  1.591549e-02         -1.000000000
  1.591549e-03         -1.000000000
  1.591549e-04         -1.000000000
  1.515761e-02          1.000000000
  1.583631e-03          1.000000000
  1.590754e-04          1.000000000

\(0\)에 얼마든지 가까운 곳에서 값이 \(-1\)이기도 하고 \(+1\)이기도 하다. 극한이 있으려면 하나로 정해져야 하므로 극한이 없다.

# sympy says the same, in its own notation
print("limit of f/g        :", sp.limit(x * sp.sin(1 / x), x, 0))
print("limit of f'/g'      :",
      sp.limit(2 * x * sp.sin(1 / x) - sp.cos(1 / x), x, 0))
limit of f/g        : 0
limit of f'/g'      : AccumBounds(-1, 1)

AccumBounds(-1, 1) 은 값이 그 구간을 오간다는 sympy 의 표기이며, 극한이 존재하지 않는다는 뜻이다.

Hide code cell source

# The two curves on one picture
fig, ax = plt.subplots(figsize=(9.0, 4.4))

xs = np.linspace(0.004, 0.13, 6000)
ax.plot(xs, derivative_ratio(xs), linewidth=0.7, color=C_CEX,
        label="f'(x) / g'(x)")
ax.plot(xs, ratio(xs), linewidth=2.0, color=C_LOWER, label="f(x) / g(x)")
ax.axhline(0.0, color=C_FUNC, linewidth=0.8)

ax.set_xlim(0.0, 0.13)
ax.set_ylim(-1.6, 1.6)
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("the quotient settles; the ratio of derivatives does not")
ax.legend(loc="upper right", fontsize=9)
fig.tight_layout()
plt.show()
../_images/b6a93d007948d73a0aceffb6bf2983283d4cf036d610f94fa44a4b45c7451e00.png

굵은 선이 \(0\)으로 가라앉고 가는 선이 계속 오간다. 정리 86이 한 방향으로만 가는 이유가 이 그림이다.

부정형이 아닌데 적용하면#

# The most common mistake: applying the rule when the form is not 0/0
numerator, denominator = x, x + 1
point = 1

print("at x = 1 :")
print(f"  numerator   -> {sp.limit(numerator, x, point)}")
print(f"  denominator -> {sp.limit(denominator, x, point)}   (not zero)")
print()
print("actual limit     :", sp.limit(numerator / denominator, x, point))
print("derivative ratio :",
      sp.limit(sp.diff(numerator, x) / sp.diff(denominator, x), x, point))
at x = 1 :
  numerator   -> 1
  denominator -> 2   (not zero)

actual limit     : 1/2
derivative ratio : 1

\(\frac12\)과 \(1\)이다. 분모의 극한이 \(0\)이 아니므로 애초에 적용할 수 없는 자리이다.

4. 무한대에서의 극한#

정의 58는 \(\epsilon\)을 받아 \(X\)를 내놓으라고 한다. \(f(x)=1/x\)에 대해 실제로 찾는다.

# (28.3): given epsilon, produce X
def find_bound(epsilon):
    return 1.0 / epsilon


print(f"{'epsilon':>12} {'X = 1/epsilon':>16} {'|f(x)| < eps beyond X':>24}")
print("-" * 56)
for epsilon in (0.1, 0.01, 0.001):
    bound = find_bound(epsilon)
    probe = bound * 1.5
    print(f"{epsilon:>12} {bound:>16.1f} "
          f"{str(abs(1.0 / probe) < epsilon):>24}")
     epsilon    X = 1/epsilon    |f(x)| < eps beyond X
--------------------------------------------------------
         0.1             10.0                     True
        0.01            100.0                     True
       0.001           1000.0                     True

\(\epsilon\)을 줄이면 \(X\)가 커진다. \(\delta\)가 작아지던 것과 같은 사정이며 방향만 반대이다.

\(X\) 아래에서는 조건이 깨질 수 있다.

# Below X the condition may fail, which is why X must be chosen
epsilon = 0.01
bound = find_bound(epsilon)
print(f"epsilon = {epsilon}, X = {bound}")
for v in (50.0, 99.0, 101.0, 200.0):
    print(f"  x = {v:>6} : 1/x = {1/v:.6f}   less than eps : {1/v < epsilon}")
epsilon = 0.01, X = 100.0
  x =   50.0 : 1/x = 0.020000   less than eps : False
  x =   99.0 : 1/x = 0.010101   less than eps : False
  x =  101.0 : 1/x = 0.009901   less than eps : True
  x =  200.0 : 1/x = 0.005000   less than eps : True

5. e 를 두 경로로#

(28.4)가 \(\lim_{x\to+\infty}(1+\frac1x)^x = e\)라 했다. 서술 파트의 경로를 따라간다.

보조정리 16이 첫걸음이다. \(F(x) = x\log\left(1+\frac1x\right)\)와 \(F(1/t) = \frac{\log(1+t)}{t}\)가 같은 값에 닿는지 수치로 본다.

# lem-28-reciprocal: the same value from both directions
def exponent_in_x(v):
    return v * np.log(1.0 + 1.0 / v)


def exponent_in_t(v):
    return np.log(1.0 + v) / v


print(f"{'x':>12} {'x log(1+1/x)':>18} {'t = 1/x':>12} {'log(1+t)/t':>18}")
print("-" * 64)
for v in (10.0, 100.0, 10000.0, 1000000.0):
    print(f"{v:>12.0f} {exponent_in_x(v):>18.12f} "
          f"{1.0 / v:>12.6f} {exponent_in_t(1.0 / v):>18.12f}")
           x       x log(1+1/x)      t = 1/x         log(1+t)/t
----------------------------------------------------------------
          10     0.953101798043     0.100000     0.953101798043
         100     0.995033085317     0.010000     0.995033085317
       10000     0.999950003333     0.000100     0.999950003333
     1000000     0.999999499918     0.000001     0.999999499918

두 열이 자릿수까지 같다. 변수를 바꾸어 적은 것이지 합성이 아니다.

# Step 1: the exponent, which is a 0/0 form after t = 1/x
print("log(1+t)/t as t -> 0+")
print(f"{'t':>10} {'log(1+t)/t':>16}")
print("-" * 28)
for v in (0.1, 0.01, 0.001, 0.0001):
    print(f"{v:>10} {np.log(1 + v) / v:>16.12f}")
print()
print("sympy :", sp.limit(sp.log(1 + t) / t, t, 0, "+"))
print("by thm-28-lhopital the ratio of derivatives is 1/(1+t), which -> 1")
log(1+t)/t as t -> 0+
         t       log(1+t)/t
----------------------------
       0.1   0.953101798043
      0.01   0.995033085317
     0.001   0.999500333083
    0.0001   0.999950003333

sympy : 1
by thm-28-lhopital the ratio of derivatives is 1/(1+t), which -> 1
# Step 2: apply exp, justified by lem-28-continuous-outer
print("exp is continuous everywhere, so the limit goes inside")
print()
print(f"{'x':>12} {'(1+1/x)**x':>18} {'difference from e':>20}")
print("-" * 54)
for v in (10.0, 100.0, 10000.0, 1e7):
    value = (1.0 + 1.0 / v) ** v
    print(f"{v:>12.0f} {value:>18.12f} {abs(np.e - value):>20.3e}")
print()
print(f"{'e':>12} {np.e:>18.12f}")
exp is continuous everywhere, so the limit goes inside

           x         (1+1/x)**x    difference from e
------------------------------------------------------
          10     2.593742460100            1.245e-01
         100     2.704813829422            1.347e-02
       10000     2.718145926825            1.359e-04
    10000000     2.718281694132            1.343e-07

           e     2.718281828459

수렴이 느리다. \(x=10^7\)에서도 차가 \(10^{-7}\) 수준이다. 26회의 테일러 근사가 \(n=4\)에서 이미 \(10^{-8}\)이었던 것과 대조된다.

# The same number from lecture 21: the point where log equals 1
def lower_upper(f, a, b, n, samples=200):
    points = np.linspace(a, b, n + 1)
    left = points[:-1].reshape(n, 1)
    right = points[1:].reshape(n, 1)
    values = f(left + (right - left)
               * np.linspace(0.0, 1.0, samples).reshape(1, samples))
    return (float(np.sum(values.min(axis=1) * np.diff(points))),
            float(np.sum(values.max(axis=1) * np.diff(points))))


def my_log(v, n=4000):
    low, high = lower_upper(lambda s: 1.0 / s, 1.0, v, n)
    return 0.5 * (low + high)


lo, hi = 2.0, 4.0
for _ in range(60):
    mid = 0.5 * (lo + hi)
    if my_log(mid) < 1.0:
        lo = mid
    else:
        hi = mid

print("lecture 21 route, where log equals 1 :", f"{0.5 * (lo + hi):.9f}")
print("lecture 28 route, the limit          :", f"{(1 + 1/1e7) ** 1e7:.9f}")
print("numpy's e                            :", f"{np.e:.9f}")
lecture 21 route, where log equals 1 : 2.718281792
lecture 28 route, the limit          : 2.718281694
numpy's e                            : 2.718281828

두 경로가 같은 수에 닿는다. 21회는 넓이가 \(1\)이 되는 지점을 좁혔고 28회는 극한을 취했다.

마치며#

\(\frac{x}{x}\), \(\frac{x^2}{x}\), \(\frac{x}{x^2}\)이 모두 \(\frac00\) 꼴인데 결과가 \(1\), \(0\), 없음이었다. 형태는 값이 아니다.

코시의 \(c\)가 \(\frac{14}{9}\)이고 두 평균값 정리가 주는 점이 \(\frac{\sqrt{21}}{3}\)과 \(\frac32\)였다. 셋이 모두 다르다. 따로 적용해 얻은 비가 실제 값과 어긋나는 것도 확인했다.

\(x^2\sin\frac1x\)에서 원래 비는 \(0\)으로 가라앉는데 도함수의 비는 \(0\)에 얼마든지 가까운 곳에서 \(-1\)이기도 하고 \(+1\)이기도 했다. 로피탈 법칙이 한 방향으로만 간다.

\(\frac{x}{x+1}\)에 잘못 적용하면 \(\frac12\) 대신 \(1\)이 나왔다. 분모의 극한이 \(0\)이 아닌데 적용한 것이다.

\(\epsilon\)을 받아 \(X=1/\epsilon\)을 내놓는 절차를 확인했고, \((1+\frac1x)^x\)가 \(e\)로 가는 것을 21회의 이분법과 맞추었다. 두 경로가 같은 수에 닿는다.

서술 파트의 내용

이 노트북의 코드

\(\frac00\)은 형태이다

1절 세 열의 표

두 번 적용하면 안 된다

2절 세 점의 표

\(g(x)=x\)이면 평균값 정리

2절 same == mvt_f

역이 거짓이다

3절 두 곡선을 겹친 그림

부정형이 아닌데 적용

3절 \(\frac12\) 대 \(1\)

정의 58

4절 find_bound

보조정리 16

5절 \(t=1/x\) 두 극한의 대조

보조정리 17

5절 exp 를 씌우는 단계

(28.4)

5절 두 경로의 일치

더 해 볼 것#

  1. 2절의 \(f\)와 \(g\)를 \(x^4\)와 \(x\)로 바꾸어 보자. 세 점이 여전히 다른가. \(g(x)=x\)이면 왜 둘이 같아지는가.

  2. 3절의 반례를 \(f(x)=x\sin\frac1x\)로 바꾸면 무슨 일이 일어나는가. 이 \(f\)는 \(0\)에서 미분 가능한가.

  3. 4절에서 \(f(x) = \frac{2x+1}{x+3}\)의 \(x\to+\infty\) 극한을 구하고, \(\epsilon = 0.001\)에 대한 \(X\)를 손으로 찾아보자.

  4. 5절의 수렴 속도를 26회 실험의 테일러 근사와 한 그림에 그려 보자. 어느 쪽이 빠른가. 왜 그런가.