3회 실험 · 유리수의 구멍#
이 실험
짝 서술 |
|
실험 목록 |
실험 1 (1층) — 확대해도 채워지지 않는다 / 실험 2 (3층) — 유리수와 무리수를 구별한다 |
이번 회차의 대상은 공리이다. 공리는 그림으로 확인할 수 있는 것이 아니므로 실험은 두 개뿐이며, 둘 다 서술 쪽 정리 7가 말하는 내용을 다른 각도에서 비추는 데 쓴다.
실험 1 (1층) — 확대해도 채워지지 않는다#
유리수는 조밀하다. 어떤 두 실수 사이에도 유리수가 있고, 확대해도 사정은 같다.
그런데 조밀하다는 것과 빈틈이 없다는 것은 다른 말이다. 아래 세 그림은 같은 지점을 세 배율로 확대한 것이다. 배율을 올리면 분모를 키워 유리수를 더 촘촘히 찍는다. 빨간 세로선은 제곱해서 \(2\)가 되는 수가 있어야 할 위치이다.
# [3회] 유리수의 조밀성과 구멍
# 목적 : 유리수는 어디에나 있지만 제곱해서 2가 되는 유리수는 없다는 것을 확인한다
# 층 : 1층
# 주의 : 그림은 유한개의 유리수만 찍는다. 그림이 증명이 될 수는 없다
중심 = 2 ** 0.5
그림, 축 = plt.subplots(3, 1, figsize=(9, 6))
for i, 반경 in enumerate([0.5, 0.05, 0.005]):
분모 = int(20 / 반경)
값 = np.array([j / 분모
for j in range(int((중심 - 반경) * 분모), int((중심 + 반경) * 분모) + 1)])
축[i].scatter(값, np.zeros_like(값), s=10, color=색["하합"], label="유리수")
축[i].axvline(중심, color=색["차이"], label="제곱해서 2가 되는 수의 위치")
축[i].set_xlim(중심 - 반경, 중심 + 반경)
축[i].set_yticks([])
축[i].set_title(f"확대 배율 {0.5 / 반경:.0f}배 (분모 {분모}까지)")
축[0].legend(loc="upper right")
plt.tight_layout()
plt.show()
세 그림 모두에서 빨간 선 양옆에 파란 점이 빽빽하다. 그러나 어느 그림에서도 파란 점이 빨간 선 위에 정확히 놓이지 않는다. 분모를 아무리 키워도 그렇다.
이것을 유리수로 직접 확인해 보자. 분모를 키워 가며 제곱이 \(2\)에 가장 가까운 분수를 찾는다.
# [3회] 제곱이 2 에 가장 가까운 분수
# 목적 : 분모를 키우면 제곱값이 2 에 가까워지지만 도달하지는 않음을 확인한다
# 층 : 1층 (Fraction 으로 정확히 계산한다. 부동소수를 쓰지 않는다)
# 주의 : 가까워지는 것과 도달하는 것은 다르다
from calc_viz import 칸 # 한글이 섞인 표의 열을 맞춘다
머리 = ["분모", "가장 가까운 분수", "제곱 − 2", "제곱 = 2 인가"]
폭 = [8, 20, 28, 14]
print(" ".join(칸(글, w) for 글, w in zip(머리, 폭)))
for 분모 in (10, 100, 1000, 10000):
# 제곱이 2 를 넘지 않는 가장 큰 분자를 찾는다
분자 = max(j for j in range(1, 2 * 분모) if Fraction(j, 분모) ** 2 <= 2)
후보 = Fraction(분자, 분모)
차이 = 후보 ** 2 - 2
줄 = [분모, 후보, 차이, 후보 ** 2 == 2]
print(" ".join(칸(항목, w) for 항목, w in zip(줄, 폭)))
분모 가장 가까운 분수 제곱 − 2 제곱 = 2 인가
10 7/5 -1/25 False
100 141/100 -119/10000 False
1000 707/500 -151/250000 False
10000 7071/5000 -959/25000000 False
차이 열이 정확한 분수로 나온다는 점에 유의한다. 부동소수로 계산하면 반올림 때문에 0.0이 나올 수도 있지만, Fraction은 정확히 계산하므로 차이가 결코 \(0\)이 되지 않는다는 것이 보인다.
확인할 것
세 그림에서 파란 점의 개수는 거의 같다. 그런데 배율은 100배까지 올라간다. 이것이 무엇을 뜻하는지 생각해 보자.
두 번째 코드에서 분모를 키울 때
제곱 − 2가 어떻게 변하는지 확인하자. \(0\)이 되는 경우가 있는가.위 두 실험을 아무리 반복해도 정리 7를 증명할 수 없는 이유를 말해 보자.
실험 2 (3층) — 유리수와 무리수를 구별한다#
부동소수는 유리수와 무리수를 구별하지 못한다. 어떤 값이든 유한한 자릿수로 잘라 저장하므로, 저장된 값은 언제나 유리수이다.
기호 계산은 값을 자르지 않고 식의 형태로 들고 있으므로 구별이 가능하다.
# [3회] 유리수인지 아닌지를 sympy 로 판정한다
# 목적 : 부동소수로는 구별할 수 없는 것을 기호 계산으로 구별한다
# 층 : 3층
# 주의 : is_rational 은 판정 알고리즘의 결과이다. 증명이 아니다
후보 = {
"sqrt(2)": sp.sqrt(2),
"sqrt(4)": sp.sqrt(4),
"22/7": sp.Rational(22, 7),
"sqrt(2)*sqrt(2)": sp.sqrt(2) * sp.sqrt(2),
"sqrt(2)+sqrt(2)": sp.sqrt(2) + sp.sqrt(2),
}
for 이름, 값 in 후보.items():
print(f" {이름:18s} 정리하면 {str(sp.nsimplify(값)):12s} 유리수인가 : {값.is_rational}")
sqrt(2) 정리하면 sqrt(2) 유리수인가 : False
sqrt(4) 정리하면 2 유리수인가 : True
22/7 정리하면 22/7 유리수인가 : True
sqrt(2)*sqrt(2) 정리하면 2 유리수인가 : True
sqrt(2)+sqrt(2) 정리하면 2*sqrt(2) 유리수인가 : False
sqrt(2)*sqrt(2)가 유리수로 판정되는 것에 유의한다. 무리수끼리의 연산 결과가 언제나 무리수인 것은 아니다.
같은 값을 부동소수와 기호로 각각 다루면 결과가 갈린다.
# [3회] 부동소수와 기호 계산의 차이
# 목적 : 같은 등식이 한쪽에서는 거짓, 다른 쪽에서는 참으로 나오는 것을 확인한다
# 층 : 1층과 3층의 대조
# 주의 : 부동소수의 결과가 틀린 것이 아니라, 저장 방식이 다른 것이다
부동 = float(sp.sqrt(2))
print("부동소수로 계산하면")
print(f" float(sqrt(2))**2 == 2 : {부동 ** 2 == 2}")
print(f" 차이 : {부동 ** 2 - 2:.3e}")
print("\n기호로 계산하면")
print(f" sp.sqrt(2)**2 == 2 : {sp.simplify(sp.sqrt(2) ** 2 - 2) == 0}")
부동소수로 계산하면
float(sqrt(2))**2 == 2 : False
차이 : 4.441e-16
기호로 계산하면
sp.sqrt(2)**2 == 2 : True
이제 방정식 \(x^2 = 2\)를 두 정의역에서 각각 풀어 본다.
# [3회] 정의역을 바꾸어 방정식을 푼다
# 목적 : 같은 방정식이 실수에서는 해를 갖고 유리수에서는 갖지 않음을 확인한다
# 층 : 3층
# 주의 : 이것이 3회 4절의 결론과 같은 내용이다
x = sp.Symbol("x", real=True)
방정식 = sp.Eq(x**2, 2)
print(f" 실수 범위의 해 : {sp.solveset(방정식, x, domain=sp.S.Reals)}")
print(f" 유리수 범위의 해 : {sp.solveset(방정식, x, domain=sp.S.Rationals)}")
실수 범위의 해 : {-sqrt(2), sqrt(2)}
유리수 범위의 해 : EmptySet
유리수 범위에서 해집합이 EmptySet이다. 이것이 서술 쪽 정리 7가 말하는 내용이며, 동시에 3회 4절이 지적한 \(\R\)과 \(\Q\)의 차이이다.
확인할 것
float(sqrt(2))**2가 정확히 2인지 확인하자. 아니라면 차이는 얼마인가.유리수 범위에서 해집합이 무엇인지 확인하자. 공집합인가.
실험 1의 그림과 실험 2의 마지막 결과 중 어느 쪽이 3회의 정리를 뒷받침하는가.
어느 것도 증명이 아니다
세 번째 확인 항목이 이 회차의 요점이다.
실험 1의 그림은 정황이다. 유한개의 점을 찍어 보았을 뿐이므로, 찍지 않은 어딘가에 해가 있을 가능성을 배제하지 못한다.
실험 2의 EmptySet은 알고리즘의 결과이다. sympy가 내부에서 어떤 판정 절차를 돌린 결과이며, 그 절차가 옳다는 것은 별도의 문제이다.
둘 다 3회 4절의 귀류법 증명을 대신하지 못한다. 증명이 하는 일은 “찾지 못했다”가 아니라 “있을 수 없다”를 보이는 것이며, 이것은 유한 번의 확인으로는 도달할 수 없다.
짝이 되는 서술