6회 실험 · 넓이 공리#
이 실험
짝 서술 |
|
확인하는 것 |
넓이 공리 A6은 안쪽과 바깥쪽의 넓이가 정확히 같을 때만 쓸 수 있다 |
서술 파트에서 함수를 순서쌍의 집합으로 정의하고(정의 9), 넓이 공리 여섯 개를 세웠다(공리 2). 공리는 넓이가 무엇인지 말하지 않는다. 넓이가 만족해야 할 성질만 말한다. 여기서는 그 성질만 가지고 어디까지 계산할 수 있는지 확인해 보자.
먼저 함수의 정의를 코드로 옮긴다. 정의 9의 조건은 “한 입력에 두 값이 대응하지 않는다”는 한 줄이고, 이것은 그대로 판정 함수로 옮길 수 있다.
그다음 A6을 다룬다. 원을 대상 \(Q\)로 두고 안쪽 정사각형 \(S\)와 바깥쪽 정사각형 \(T\)를 잡으면 \(S \subseteq Q \subseteq T\)가 성립하지만 \(a(S) \neq a(T)\)이므로 A6을 쓸 수 없다. 정사각형을 정 \(m\)각형으로 바꾸면 두 값의 차이가 줄어든다. 그런데 유한한 \(m\)에서는 끝내 같아지지 않는다.
정 \(m\)각형의 넓이는 새로 가정하는 것이 아니다. 서술 4절에서 A2·A4·A5로부터 직각삼각형의 넓이 \(\tfrac{1}{2}hk\)를 얻었고, 정 \(m\)각형은 직각삼각형 \(2m\)개로 나뉜다. 이 분할을 코드로 그대로 적어서 확인한다.
서술 파트의 내용 |
여기서 확인하는 방법 |
|---|---|
정의 9 — 함수는 순서쌍의 집합이다 |
순서쌍 목록을 받아 조건을 판정하는 함수를 직접 만든다 |
함수는 식이 아니라 대응이다 |
서로 다른 두 식이 같은 순서쌍 집합을 주는지 비교한다 |
공리 2의 A5와 A6 |
원을 사이에 낀 두 정사각형의 넓이를 재고 A6의 조건이 갖추어졌는지 본다 |
정 \(m\)각형을 직각삼각형 \(2m\)개로 나누어 넓이를 더한다 |
|
1회 정리 1 형태의 논증 |
변의 개수를 늘리며 \(a(T) - a(S)\)를 표로 본다 |
0. 준비#
이 회차에서 쓸 것만 불러온다. 색은 안쪽·바깥쪽·차이를 구분하는 데만 쓴다.
1. 함수는 순서쌍의 집합이다#
정의 9은 순서쌍의 집합 \(F\)가 함수일 조건을 하나만 요구한다. \((x,y) \in F\)이고 \((x,z) \in F\)이면 \(y = z\)라는 조건이다. 이 조건은 그대로 코드가 된다. 순서쌍을 하나씩 훑으면서 이미 나온 첫 성분이 다른 값과 짝지어졌는지만 보면 된다.
# Test the single-valued condition in the definition of a function
def is_function(pairs):
"""Return True if no first component is paired with two different values."""
seen = {}
for x, y in pairs:
if x in seen and seen[x] != y:
return False
seen[x] = y
return True
F = [(0, 0), (1, 1), (2, 4), (3, 9)]
G = [(0, 0), (1, 1), (1, -1), (4, 2)]
print("F =", F)
print("is_function(F) =", is_function(F))
print()
print("G =", G)
print("is_function(G) =", is_function(G))
F = [(0, 0), (1, 1), (2, 4), (3, 9)]
is_function(F) = True
G = [(0, 0), (1, 1), (1, -1), (4, 2)]
is_function(G) = False
G는 함수가 아니다. 첫 성분 \(1\)이 \(1\)과도 \(-1\)과도 짝지어져 있기 때문이다. 이런 집합에서는 \(G(1)\)이라는 표기가 무엇을 가리키는지 정해지지 않는다. 판정 함수가 걸러 내는 것이 바로 이 상황이다.
정의역과 치역도 식이 아니라 순서쌍에서 읽는다.
# Domain and range are read off the pairs themselves
def domain(pairs):
return sorted({x for x, y in pairs})
def range_of(pairs):
return sorted({y for x, y in pairs})
print("domain(F) =", domain(F))
print("range(F) =", range_of(F))
domain(F) = [0, 1, 2, 3]
range(F) = [0, 1, 4, 9]
서술 파트에서 강조한 것은 함수가 식이 아니라 대응이라는 점이다. 이 말을 뒤집으면, 서로 달라 보이는 두 식이 같은 순서쌍 집합을 만들면 그 둘은 같은 함수라는 뜻이 된다. 확인해 보자.
# Two different rules can produce the same set of ordered pairs
def pairs_from_rule(rule, dom):
return [(x, rule(x)) for x in dom]
dom = [0, 1, 2, 3, 4, 5]
P1 = pairs_from_rule(lambda x: x * x, dom)
P2 = pairs_from_rule(lambda x: sum(2 * k + 1 for k in range(x)), dom)
print("P1 =", P1)
print("P2 =", P2)
print("same set of pairs :", set(P1) == set(P2))
P1 = [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
P2 = [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
same set of pairs : True
P1은 제곱으로 만들었고 P2는 홀수를 차례로 더해서 만들었다. 두 식은 다르지만 순서쌍의 집합은 같다. 정의 9의 관점에서 이 둘은 같은 함수이다.
2. 사이에 낀 집합#
이제 넓이로 넘어간다. 반지름 \(1.5\)인 원을 대상 \(Q\)로 두고, 원에 내접하는 정사각형 \(S\)와 원을 덮는 정사각형 \(T\)를 그린다. 세 집합은 \(S \subseteq Q \subseteq T\)를 만족한다.
이 시점에서 우리가 아는 것은 A5로 계산되는 두 정사각형의 넓이뿐이다. 원의 넓이는 아직 모른다.
# Inner square S, circle Q, outer square T, drawn outer first then inner on top
r = 1.5
side_in = r * np.sqrt(2.0)
side_out = 2.0 * r
fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.add_patch(plt.Rectangle((-side_out / 2, -side_out / 2), side_out, side_out,
facecolor=C_UPPER, alpha=0.55, label="T (outer square)"))
ax.add_patch(plt.Rectangle((-side_in / 2, -side_in / 2), side_in, side_in,
facecolor=C_LOWER, alpha=0.85, label="S (inner square)"))
ax.add_patch(plt.Circle((0.0, 0.0), r, facecolor="none", edgecolor=C_FUNC,
linewidth=2.0, label="Q (target set)"))
ax.set_xlim(-1.9, 1.9)
ax.set_ylim(-1.9, 1.9)
ax.set_aspect("equal")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title("S inside Q inside T")
ax.legend(loc="upper right", fontsize=8)
plt.tight_layout()
plt.show()
주황색 띠가 \(T\)에서 \(S\)를 뺀 부분이다. 원은 그 띠를 가로지른다. 두 정사각형의 넓이를 재 보자.
# The two square areas come from axiom A5 alone
area_S = side_in ** 2
area_T = side_out ** 2
print(f"a(S) = {area_S:.6f}")
print(f"a(T) = {area_T:.6f}")
print(f"a(T) - a(S) = {area_T - area_S:.6f}")
print(f"a(S) == a(T) : {area_S == area_T}")
a(S) = 4.500000
a(T) = 9.000000
a(T) - a(S) = 4.500000
a(S) == a(T) : False
\(a(S) = 4.5\)이고 \(a(T) = 9\)이다. A6은 \(a(S) = a(T)\)일 때만 결론을 준다. 두 값이 다르므로 여기서는 A6을 쓸 수 없고, \(a(Q)\)는 확정되지 않는다.
그렇다고 아무것도 얻지 못한 것은 아니다. 정리 19에 의하여 \(Q\)가 가측이기만 하면 \(4.5 \le a(Q) \le 9\)이다. 다만 이 구간이 너무 넓다. 구간을 좁히려면 안쪽과 바깥쪽 도형을 더 잘 잡아야 한다.
3. 삼각형에서 정다각형으로#
정사각형 대신 정 \(m\)각형을 쓰면 구간이 좁아진다. 그런데 정 \(m\)각형의 넓이를 쓰려면 그 넓이가 공리에서 나온다는 것이 먼저 확인되어야 한다.
서술 4절에서 밑변 \(h\), 높이 \(k\)인 직각삼각형의 넓이가 \(\tfrac{1}{2}hk\)임을 A2, A4, A5로부터 얻었다. (6.6)에서 합동인 두 직각삼각형의 넓이가 같음을 쓰고, (6.7)에서 둘을 붙인 직사각형의 넓이를 A5로 계산한 결과였다.
정 \(m\)각형은 중심에서 각 변으로 이등변삼각형 \(m\)개를 자르고, 그 각각을 중심에서 변에 내린 수선으로 다시 반씩 자르면 직각삼각형 \(2m\)개가 된다. 이 직각삼각형의 밑변은 변의 길이의 절반이고 높이는 중심에서 변까지의 거리이다. 그러니 정 \(m\)각형의 넓이 계산에 새로 가정한 것은 없다.
# Area of a regular polygon built only from the right triangle area (1/2)*h*k
def right_triangle_area(h, k):
return 0.5 * h * k
def regular_polygon_area(m, apothem, side):
"""A regular m-gon splits into 2m right triangles of base side/2 and height apothem."""
return 2 * m * right_triangle_area(side / 2.0, apothem)
def inscribed_parts(m, radius):
return radius * np.cos(np.pi / m), 2.0 * radius * np.sin(np.pi / m)
def circumscribed_parts(m, radius):
return radius, 2.0 * radius * np.tan(np.pi / m)
이 계산이 맞는지 다른 방법으로 검산한다. 다각형의 꼭짓점 좌표를 직접 만들고 신발끈 공식(shoelace formula)으로 넓이를 구해 비교해 보자. 두 값이 일치하면 삼각형을 세는 방식에 실수가 없었다는 뜻이다.
# Vertices of the inscribed and circumscribed regular m-gons of a circle of radius R
def inscribed_vertices(m, radius):
t = np.arange(m) * (2.0 * np.pi / m)
return np.column_stack([radius * np.cos(t), radius * np.sin(t)])
def circumscribed_vertices(m, radius):
t = np.arange(m) * (2.0 * np.pi / m) + np.pi / m
s = radius / np.cos(np.pi / m)
return np.column_stack([s * np.cos(t), s * np.sin(t)])
def shoelace_area(v):
"""Independent check on the triangle count."""
x, y = v[:, 0], v[:, 1]
return 0.5 * abs(np.dot(x, np.roll(y, -1)) - np.dot(y, np.roll(x, -1)))
sides = 12
radius = 1.5
v_in = inscribed_vertices(sides, radius)
v_out = circumscribed_vertices(sides, radius)
area_in = regular_polygon_area(sides, *inscribed_parts(sides, radius))
area_out = regular_polygon_area(sides, *circumscribed_parts(sides, radius))
print(f"m = {sides}, R = {radius}")
print(f"inscribed : triangles {area_in:.10f} shoelace {shoelace_area(v_in):.10f}")
print(f"circumscribed : triangles {area_out:.10f} shoelace {shoelace_area(v_out):.10f}")
print(f"a(T) - a(S) : {area_out - area_in:.10f}")
m = 12, R = 1.5
inscribed : triangles 6.7500000000 shoelace 6.7500000000
circumscribed : triangles 7.2346281956 shoelace 7.2346281956
a(T) - a(S) : 0.4846281956
두 방법이 같은 값을 준다. 이제 \(m = 12\)인 두 다각형을 그려서 2절의 정사각형과 비교해 보자.
# The same picture as in section 2, with squares replaced by regular 12-gons
fig, ax = plt.subplots(figsize=(4.8, 4.8))
ax.add_patch(plt.Polygon(v_out, closed=True, facecolor=C_UPPER, alpha=0.55,
label="T (circumscribed 12-gon)"))
ax.add_patch(plt.Polygon(v_in, closed=True, facecolor=C_LOWER, alpha=0.85,
label="S (inscribed 12-gon)"))
ax.add_patch(plt.Circle((0.0, 0.0), radius, facecolor="none", edgecolor=C_FUNC,
linewidth=2.0, label="Q (target set)"))
ax.set_xlim(-1.9, 1.9)
ax.set_ylim(-1.9, 1.9)
ax.set_aspect("equal")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_title(f"m = {sides}")
ax.legend(loc="upper right", fontsize=7)
plt.tight_layout()
plt.show()
주황색 띠가 2절보다 눈에 띄게 얇아졌다. 여전히 띠는 남아 있고, 원은 그 안에 있다.
4. 변의 개수를 늘린다#
\(m\)을 바꿔 가며 값을 보려면 \(m\)의 식이 있는 편이 낫다. 3절의 분할을 그대로 기호로 적으면 다음과 같다. 내접 다각형은 밑변 \(R\sin(\pi/m)\), 높이 \(R\cos(\pi/m)\)인 직각삼각형 \(2m\)개이고, 외접 다각형은 밑변 \(R\tan(\pi/m)\), 높이 \(R\)인 직각삼각형 \(2m\)개이다.
# Symbolic areas of the inscribed and circumscribed regular m-gons
m, R = sp.symbols("m R", positive=True)
inner_expr = sp.simplify(2 * m * R * sp.cos(sp.pi / m) * R * sp.sin(sp.pi / m) / 2)
outer_expr = sp.simplify(2 * m * R * sp.tan(sp.pi / m) * R / 2)
diff_expr = sp.simplify(outer_expr - inner_expr)
print("inscribed area :")
sp.pprint(inner_expr)
print()
print("circumscribed area :")
sp.pprint(outer_expr)
print()
print("difference :")
sp.pprint(diff_expr)
inscribed area :
2 ⎛2⋅π⎞
R ⋅m⋅sin⎜───⎟
⎝ m ⎠
─────────────
2
circumscribed area :
2 ⎛π⎞
R ⋅m⋅tan⎜─⎟
⎝m⎠
difference :
2 ⎛ ⎛2⋅π⎞ ⎛π⎞⎞
R ⋅m⋅⎜- sin⎜───⎟ + 2⋅tan⎜─⎟⎟
⎝ ⎝ m ⎠ ⎝m⎠⎠
────────────────────────────
2
\(R = 1\)로 두고 변의 개수를 늘려 가며 값을 본다. 극한은 취하지 않는다. 유한한 \(m\)에 값을 넣을 뿐이다.
# Values at finite numbers of sides; no limit is taken anywhere here
header = ["m", "inscribed a(S)", "circumscribed a(T)", "difference", "equal?"]
widths = [7, 18, 22, 16, 9]
print("".join(name.rjust(w) for name, w in zip(header, widths)))
for count in (4, 6, 12, 24, 96, 384):
lo = float(inner_expr.subs({m: count, R: 1}))
hi = float(outer_expr.subs({m: count, R: 1}))
row = [str(count), f"{lo:.10f}", f"{hi:.10f}", f"{hi - lo:.10f}", str(lo == hi)]
print("".join(item.rjust(w) for item, w in zip(row, widths)))
m inscribed a(S) circumscribed a(T) difference equal?
4 2.0000000000 4.0000000000 2.0000000000 False
6 2.5980762114 3.4641016151 0.8660254038 False
12 3.0000000000 3.2153903092 0.2153903092 False
24 3.1058285412 3.1596599421 0.0538314009 False
96 3.1393502030 3.1427145996 0.0033643966 False
384 3.1414524723 3.1416627471 0.0002102748 False
\(m = 96\)은 아르키메데스가 실제로 쓴 변의 개수이다. 그때 차이는 약 \(0.0034\)이다. 같은 표의 첫 줄, 즉 \(m = 4\)일 때의 차이 \(2\)와 비교하면 상당히 좁아졌다.
equal? 열을 보자. 모든 줄에서 False이다. 어느 유한한 \(m\)에서도 두 넓이는 같아지지 않는다. 따라서 A6의 조건은 이 표의 어느 줄에서도 갖추어지지 않으며, 표만으로는 원의 넓이가 확정되지 않는다.
구간이 어떻게 줄어드는지 그림으로 보자.
# The nested intervals [a(S), a(T)] as the number of sides grows
counts = [4, 6, 12, 24, 48, 96, 192, 384]
lows = [float(inner_expr.subs({m: c, R: 1})) for c in counts]
highs = [float(outer_expr.subs({m: c, R: 1})) for c in counts]
fig, ax = plt.subplots(figsize=(6.2, 3.8))
for c, lo, hi in zip(counts, lows, highs):
ax.plot([c, c], [lo, hi], color=C_DIFF, linewidth=7, alpha=0.55,
solid_capstyle="butt")
ax.plot(counts, lows, "o-", color=C_LOWER, markersize=4, linewidth=1.2,
label="inscribed a(S)")
ax.plot(counts, highs, "o-", color=C_UPPER, markersize=4, linewidth=1.2,
label="circumscribed a(T)")
ax.set_xscale("log", base=2)
ax.set_xticks(counts)
ax.set_xticklabels([str(c) for c in counts])
ax.xaxis.set_minor_locator(plt.NullLocator())
ax.set_xlabel("m (number of sides)")
ax.set_ylabel("area at R = 1")
ax.set_title("nested intervals from a(S) to a(T)")
ax.legend(loc="lower right", fontsize=8)
plt.tight_layout()
plt.show()
빨간 막대가 \(a(S)\)와 \(a(T)\) 사이의 구간이다. 오른쪽으로 갈수록 짧아지지만 길이가 \(0\)이 되는 자리는 없다. 원의 넓이를 \(A\)라 하면 모든 \(m\)에 대하여
이고, 구간의 길이를 원하는 만큼 작게 만들 수 있다. 이 상황에서 \(A\)의 후보를 하나씩 배제하는 것이 1회 정리 1에서 본 방법이다.
A6은 언제 쓰이는가
2절과 4절 어느 쪽에서도 A6을 쓰지 못하였다. 두 곳 모두에서 \(a(S) \neq a(T)\)였기 때문이다.
A6이 놓이는 자리는 다르다. 유한한 \(m\)마다 얻은 부등식 전체를 모아 후보를 배제하는 논증을 끝낸 뒤에, 그 결과로 안쪽과 바깥쪽의 넓이가 같아지는 상황을 만들었을 때 쓰인다.
즉 이 공리는 계산의 출발점이 아니라 논증의 마지막 단계에 놓인다. 이 순서는 다음 회차에서 적분을 정의할 때 그대로 반복된다.
마치며#
1절에서 정의 9을 판정 함수로 옮겼다. 조건이 한 줄이므로 코드도 한 줄짜리 검사이다. 서로 다른 두 식이 같은 순서쌍 집합을 만들면 같은 함수라는 것도 확인하였다. 함수를 식으로 보지 않는다는 정의의 뜻이 여기에 있다.
2절부터 4절까지는 넓이 공리 A6의 조건을 확인하였다. 원을 사이에 낀 두 정사각형은 넓이가 \(4.5\)와 \(9\)로 달랐고, 정 \(12\)각형으로 바꾸자 차이가 크게 줄었으며, \(R = 1\)에서 \(m = 96\)일 때는 약 \(0.0034\)까지 줄었다. 그러나 어느 유한한 \(m\)에서도 두 값은 같아지지 않았다.
정 \(m\)각형의 넓이를 쓸 때 새로 가정한 것이 없다는 점도 확인하였다. 직각삼각형 \(2m\)개로 나누어 \(\tfrac{1}{2}hk\)를 \(2m\)번 더했고, 그 결과가 좌표에서 신발끈 공식으로 구한 값과 일치하였다. 서술 4절의 (6.6), (6.7)이 이 계산의 근거 전부이다.
서술 파트의 주장과 그것을 확인한 코드를 나란히 놓으면 다음과 같다.
서술 파트의 내용 |
이 노트북의 코드 |
|---|---|
정의 9 — 한 첫 성분에 두 값이 대응하지 않는다 |
|
함수는 식이 아니라 대응이다 |
1절의 |
|
|
정리 19 — \(S \subseteq Q \subseteq T\) 이면 \(a(S) \le a(T)\) |
4절의 |
공리 2의 A6 — \(a(S) = a(T)\) 일 때만 결론이 나온다 |
2절의 |
더 해 볼 것#
2절의 반지름
r을 바꿔 보자. \(a(T) - a(S)\)는r에 비례하는가,r의 제곱에 비례하는가. 양수인r중에 차이를 \(0\)으로 만드는 것이 있는가.4절의 표에 \(m = 10^9\)을 넣어 보자.
equal?열이True가 된다. 그것이 두 넓이가 정말 같아졌다는 뜻인지, 아니면float계산의 한계인지 판단해 보자.sp.N(diff_expr.subs({m: 10**9, R: 1}), 30)으로 자릿수를 늘려 확인할 수 있다. A6을 쓸 수 있는 상황과 쓸 수 없는 상황을 가르는 것이 무엇인지 다시 생각해 보자.3절의
regular_polygon_area를 정 \(m\)각형이 아닌 도형에도 쓸 수 있게 고쳐 보자. 꼭짓점 목록을 받아 삼각형으로 나누어 넓이를 더하는 방식이다. 그렇게 만든 함수가shoelace_area와 같은 값을 주는지 확인해 보자.
짝이 되는 서술