8회 실험 · 리만 조건#

이 실험

짝 서술

8회 · 적분의 성질과 리만 조건

실험 목록

실험 1 (1층) — \(\epsilon\)이 먼저, \(n\)이 나중 / 실험 2 (3층) — 필요한 \(n\)을 식으로 푼다

정리 24의 조건은 “\(\epsilon\)이 주어지면 계단함수 쌍이 존재한다”는 형태이다. 이 실험의 목적은 코드에서 무엇이 먼저 정해지는지를 눈으로 확인하는 것이다.

Hide code cell source

# 공통 준비 — 색 규약과 한글 글꼴을 맞춘다
import numpy as np
import sympy as sp

from calc_viz import 상합하합, 
from calc_style import 한글글꼴설정

글꼴 = 한글글꼴설정()

f = lambda x: x**2

실험 1 (1층) — \(\epsilon\)이 먼저, \(n\)이 나중#

함수 필요한분할수의 인자를 보면 eps가 입력이고 n이 출력이다. \(\epsilon\)을 받아서 \(n\)을 돌려준다. 이 방향이 리만 조건의 양화사 순서와 같다.

# [8회] 리만 조건
# 목적 : 허용 오차가 먼저 주어지고 그다음 분할을 찾는다는 순서를 확인한다
# 층  : 1층
# 주의 : eps 가 인자이고 n 이 반환값이다. 순서가 뒤바뀌면 다른 명제가 된다

def 필요한분할수(f, a, b, eps, 최대=10**6):
    """U_n − L_n < eps 를 만족하는 n 을 하나 찾는다. 최선의 n 을 찾지는 않는다."""
    n_ = 1
    while n_ <= 최대:
        ,  = 상합하합(f, a, b, n_)
        if  -  < eps:
            return n_,  - 
        n_ *= 2
    return None, None

머리 = ["eps", "찾은 n", "그때의 U_n − L_n", "eps 보다 작은가"]
 = [12, 10, 22, 16]
print(" ".join((, w) for , w in zip(머리, )))

for eps in (0.1, 0.01, 0.001, 0.0001):
    n_, 차이 = 필요한분할수(f, 0.0, 1.0, eps)
     = [eps, n_, f"{차이:.10f}", 차이 < eps]
    print(" ".join((항목, w) for 항목, w in zip(, )))
         eps     찾은 n       그때의 U_n − L_n  eps 보다 작은가
         0.1         16           0.0625000000             True
        0.01        128           0.0078125000             True
       0.001       1024           0.0009765625             True
      0.0001      16384           0.0000610352             True

\(\epsilon\)이 작아지면 필요한 \(n\)이 커진다. 하나의 \(n\)이 모든 \(\epsilon\)에 대하여 통하지 않는다는 것이 이 표가 보여 주는 것이다.

확인할 것

  1. 필요한분할수의 인자와 반환값을 보자. \(\epsilon\)\(n\) 중 무엇이 먼저 정해지는가.

  2. \(\epsilon\)을 10배 줄이면 찾은 \(n\)이 몇 배가 되는가.

  3. 이 함수가 돌려주는 \(n\)이 조건을 만족하는 가장 작은 \(n\)인가.

실험 2 (3층) — 필요한 \(n\)을 식으로 푼다#

실험 1은 \(n\)을 2배씩 늘려 가며 찾으므로 실제로 필요한 최소 \(n\)을 주지 못한다. 기호로 풀면 정확한 경계가 나온다.

# [8회] 리만 조건을 만족하는 n 의 정확한 경계
# 목적 : eps 가 주어졌을 때 필요한 n 을 식으로 구한다
# 층  : 3층
# 주의 : 부등호가 < 이지 ≤ 가 아니다. 경계값 자체는 조건을 만족하지 않는다

from calc_sym import 균등분할_상합하합, x, n, b as b기호, eps as eps기호

하합, 상합 = 균등분할_상합하합(x**2, x, 0, b기호, 증가함수=True)
차이 = sp.simplify(상합 - 하합)
print("U_n − L_n ="); sp.pprint(차이)

조건 = sp.simplify(차이.subs(b기호, 1)) < eps기호
print(f"\n조건 : {조건}")
print(f"풀면 : {sp.solve(조건, n)}")
U_n − L_n =
 3
b 
──
n 

조건 : 1/n < epsilon
풀면 : 1/epsilon < n

\(1/n < \epsilon\)의 해는 \(n > 1/\epsilon\)이다. 부등호가 엄격하므로 최소인 자연수는 \(1/\epsilon\)의 올림값이 아니라

\[ n_{\min} = \left\lfloor \frac{1}{\epsilon} \right\rfloor + 1 \]

이다. \(\epsilon = 0.1\)이면 \(1/\epsilon = 10\)인데 \(1/10 = 0.1\)\(0.1\)보다 작지 않으므로 \(n=10\)은 조건을 만족하지 않는다. \(n=11\)이 최소이다.

# [8회] 정확한 최소 n 과 실험 1 이 찾은 n 의 대조
# 목적 : 2배씩 늘리는 탐색이 최소값을 넘어선다는 것을 확인한다
# 층  : 3층과 1층의 대조
# 주의 : Rational(str(e)) 로 두어 부동소수 오차 없이 계산한다

머리 = ["eps", "정확한 최소 n", "실험 1 의 n", "1/n_min < eps 인가"]
 = [12, 16, 14, 20]
print(" ".join((, w) for , w in zip(머리, )))

for e in (0.1, 0.01, 0.001, 0.0001):
    er = sp.Rational(str(e))
    최소n = int(sp.floor(1 / er) + 1)
    n1, _ = 필요한분할수(f, 0.0, 1.0, e)
     = [e, 최소n, n1, bool(sp.Rational(1, 최소n) < er)]
    print(" ".join((항목, w) for 항목, w in zip(, )))
         eps    정확한 최소 n    실험 1 의 n   1/n_min < eps 인가
         0.1               11             16                 True
        0.01              101            128                 True
       0.001             1001           1024                 True
      0.0001            10001          16384                 True

실험 1이 찾은 \(n\)이 언제나 최소값보다 크다. \(1\)에서 시작해 2배씩 늘리므로 조건을 처음 만족하는 2의 거듭제곱에서 멈추기 때문이다.

이것은 문제가 되지 않는다. 정리 24은 조건을 만족하는 계단함수 쌍이 존재한다고만 요구한다. 최선의 쌍을 찾을 필요가 없다.

확인할 것

  1. 실험 2에서 필요한 최소 \(n\)\(1/\epsilon\)의 올림값인지 확인하자. 아니라면 왜 그런가.

  2. 실험 1이 찾은 \(n\)이 실험 2의 최소값보다 큰지 확인하자. 왜 그런가.

  3. 코드에서 \(\epsilon\)\(n\) 중 무엇이 먼저 정해지는지 확인하자.

첫 번째 항목에 유의한다

\(\epsilon = 0.1\)일 때 \(1/\epsilon\)의 올림값은 \(10\)이지만, \(1/10 = 0.1\)\(0.1\)보다 작지 않다. 리만 조건의 부등호는 엄격한 부등호이므로 \(n=10\)은 조건을 만족하지 못한다.

경계에서 등호가 어느 쪽에 붙는지를 확인하는 것은 이후에도 계속 필요하다. 부등호 하나가 답을 하나 어긋나게 한다.