---
jupytext:
  formats: md:myst
  text_representation:
    extension: .md
    format_name: myst
    format_version: 0.13
kernelspec:
  display_name: Python 3
  language: python
  name: python3
---

# 5회 실험 · 최소상계

```{admonition} 이 실험
:class: seealso

| | |
|---|---|
| **짝 서술** | {doc}`L05` |
| **실험 목록** | 실험 1 (1층) — 상계와 최소상계 / 실험 2 (1층) — 유리수 안에서 상계를 줄인다 / 실험 3 (3층) — 최소상계를 기호로 확인한다 |
```

```{code-cell} ipython3
:tags: [hide-input]

# 공통 준비 — 색 규약과 한글 글꼴을 맞춘다
import numpy as np
import matplotlib.pyplot as plt
import sympy as sp
from fractions import Fraction

from calc_viz import 색, 칸
from calc_style import 한글글꼴설정

글꼴 = 한글글꼴설정()   # 돌려주는 값은 실제로 선택된 글꼴 이름이다
```

## 실험 1 (1층) — 상계와 최소상계

{prf:ref}`def-5-sup`의 두 조건을 그림으로 확인한다. 아래 집합은

$$
S = \left\{ \tfrac{1}{2},\ \tfrac{3}{4},\ \tfrac{7}{8},\ \tfrac{15}{16},\ \tfrac{31}{32},\ \tfrac{63}{64} \right\}
$$

이며, 원소를 더 늘려도 모두 $1$보다 작다. 주황 점들은 상계이고, 그중 가장 왼쪽에 있는 것이 최소상계이다.

```{code-cell} ipython3
# [5회] 상계와 최소상계
# 목적 : 상계는 무수히 많고 그중 가장 작은 것이 하나임을 확인한다
# 층  : 1층
# 주의 : 유한개의 원소만 찍는다. 그림이 정의를 대신하지는 않는다

집합 = np.array([1/2, 3/4, 7/8, 15/16, 31/32, 63/64])
최소상계 = 1.0

그림, 축 = plt.subplots(figsize=(9, 2.6))
축.scatter(집합, np.zeros_like(집합), s=45, color=색["하합"], label="집합 S 의 원소", zorder=4)
상계후보 = np.linspace(1.0, 1.6, 13)
축.scatter(상계후보, np.zeros_like(상계후보) + 0.05, s=28, color=색["상합"],
           label="상계들", zorder=3)
축.axvline(최소상계, color=색["차이"], label="최소상계 sup S = 1")
축.set_yticks([]); 축.set_xlim(0.3, 1.7); 축.set_ylim(-0.05, 0.12)
축.set_xlabel("x"); 축.legend(loc="upper left")
plt.tight_layout()
plt.show()

print(f"S 의 최대 원소            : {집합.max():.6f}")
print(f"S 의 최소상계             : {최소상계:.6f}")
print(f"최소상계가 S 에 속하는가  : {최소상계 in 집합}")
```

최소상계 $1$이 집합에 속하지 않는다. 그런데도 최소상계이다. 이것이 최댓값과 최소상계의 차이이다.

```{admonition} 확인할 것
:class: tip
1. 주황 점의 개수는 유한하지만 실제 상계는 몇 개인가.
2. $1$보다 조금이라도 작은 수를 하나 고르면, 그것보다 큰 원소가 $S$ 안에 있는지 확인하자. 이것이 {prf:ref}`thm-5-eps`의 세 번째 조건이다.
3. $S$의 최댓값은 무엇인가. 최소상계와 같은가.
```

## 실험 2 (1층) — 유리수 안에서 상계를 줄인다

{prf:ref}`thm-5-q-incomplete`가 말하는 상황을 직접 만들어 본다. 구간을 반씩 줄여 가며 제곱이 $2$보다 작은 쪽과 큰 쪽을 유지한다. 왼쪽 끝은 언제나 $S$의 원소이고 오른쪽 끝은 언제나 상계이다.

`Fraction`으로 계산하므로 모든 값이 정확한 유리수이다.

```{code-cell} ipython3
# [5회] 유리수 안에서 최소상계가 존재하지 않는 예
# 목적 : S = { x : x² < 2 } 의 상계를 유리수 안에서 계속 줄여도 최소가 나오지 않음을 확인한다
# 층  : 1층 (Fraction 으로 정확히 계산한다)
# 주의 : 어느 단계에서도 제곱이 정확히 2 가 되지 않는다

왼쪽, 오른쪽 = Fraction(1), Fraction(2)

머리 = ["단계", "S 의 원소 (왼쪽)", "상계 (오른쪽)", "상계의 제곱", "제곱 = 2 인가"]
폭 = [5, 20, 20, 22, 14]
print(" ".join(칸(글, w) for 글, w in zip(머리, 폭)))

for 단계 in range(1, 13):
    가운데 = (왼쪽 + 오른쪽) / 2
    if 가운데 * 가운데 < 2:
        왼쪽 = 가운데        # 아직 S 의 원소이다
    else:
        오른쪽 = 가운데      # 상계이다
    줄 = [단계, 왼쪽, 오른쪽, f"{float(오른쪽**2):.15f}", 오른쪽**2 == 2]
    print(" ".join(칸(항목, w) for 항목, w in zip(줄, 폭)))
```

오른쪽 값은 계속 작아지지만 어느 단계에서도 멈추지 않는다. 제곱이 정확히 $2$가 되는 단계도 없다. 3회 {prf:ref}`thm-3-sqrt2`에 의하여 그런 유리수가 없기 때문이다.

**이 표는 최소상계가 없음을 증명하지 못한다.** 열두 단계를 보였을 뿐이다. 증명은 {prf:ref}`thm-5-q-incomplete`의 (5.3)이 하며, 그 논증은 어떤 유리수 상계를 가져와도 더 작은 상계를 만들어 낸다.

```{admonition} 확인할 것
:class: tip
1. 왼쪽 값과 오른쪽 값의 분모가 단계마다 어떻게 변하는지 확인하자.
2. `제곱 = 2 인가` 열에 `True`가 나오는 단계가 있는가.
3. 단계를 100까지 늘리면 `True`가 나올 수 있는가. 답의 근거는 이 표인가 아니면 3회의 정리인가.
```

## 실험 3 (3층) — 최소상계를 기호로 확인한다

실험 2의 이분법이 좁혀 간 값이 무엇이었는지를 기호로 확인한다.

```{code-cell} ipython3
# [5회] 집합의 최소상계를 sympy 로 구한다
# 목적 : 이분법으로 좁혀 간 값이 무엇이었는지 기호로 확인한다
# 층  : 3층
# 주의 : sup 은 집합의 성질이다. 원소가 아닐 수 있다

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

S = sp.solveset(x**2 < 2, x, domain=sp.S.Reals)
print("S = { x : x² < 2 } 의 실수 범위 해집합")
sp.pprint(S)

print(f"\n이 집합의 최소상계        : {S.sup}")
print(f"최소상계가 S 에 속하는가  : {S.sup in S}")
print(f"최소상계가 유리수인가     : {S.sup.is_rational}")
```

최소상계는 존재하지만 집합에 속하지 않고, 유리수도 아니다. 세 결과가 각각 서술 쪽의 세 가지에 대응한다.

- 존재한다 → {prf:ref}`ax-5-completeness`
- 집합에 속하지 않는다 → 최댓값과 최소상계의 차이
- 유리수가 아니다 → 3회 {prf:ref}`thm-3-sqrt2`, 그리고 {prf:ref}`thm-5-q-incomplete`

유리수 범위에서 상계를 찾아보면 사정이 다르다.

```{code-cell} ipython3
# [5회] 유리수 상계 후보를 분모를 키워 가며 시험한다
# 목적 : 상계가 되는 유리수는 있지만 그중 최소인 것이 없음을 살펴본다
# 층  : 3층
# 주의 : 상계인 후보를 찾았다고 해서 그것이 최소상계인 것은 아니다

머리 = ["분모", "후보 q", "q 의 제곱", "상계인가"]
폭 = [10, 20, 20, 10]
print(" ".join(칸(글, w) for 글, w in zip(머리, 폭)))

for 분모 in (10, 100, 1000, 10**6):
    q = sp.Rational(round(float(sp.sqrt(2)) * 분모), 분모)
    줄 = [분모, q, f"{float(q**2):.12f}", bool(q**2 >= 2)]
    print(" ".join(칸(항목, w) for 항목, w in zip(줄, 폭)))
```

상계인 후보가 나오더라도, {prf:ref}`thm-5-q-incomplete`의 (5.3)에 그 값을 넣으면 더 작은 유리수 상계가 언제나 만들어진다.

```{admonition} 확인할 것
:class: tip
1. 실험 3에서 최소상계가 무엇으로 나오는지 확인하자. 실험 2의 이분법이 좁혀 간 값과 같은가.
2. 최소상계가 집합 $S$에 속하는지 확인하자.
3. 최소상계가 유리수인지 확인하자. 이 결과가 3회의 정리와 어떻게 연결되는가.
```

```{admonition} 짝이 되는 서술
:class: note
{doc}`L05`
```
