세상에서 가장 지루한 수를 찾아낸 순간, 그 수는 더 이상 지루하지 않습니다
0. 서론: 런던 교외의 택시 번호 1729
1918년 무렵의 어느 날, 영국의 수학자 G. H. 하디는 런던 교외 퍼트니(Putney)의 요양원으로 병문안을 갑니다. 침대에 누워 있던 사람은 인도에서 온 천재 수학자 스리니바사 라마누잔. 결핵으로 몸이 많이 상한 상태였습니다. 그 날의 짧은 대화를 하디는 훗날(1940년) 자신의 강연집에 직접 적어 두었는데, 수학의 역사에서 가장 사랑받는 일화 중 하나가 바로 이 대목입니다.
나는 1729번 택시를 타고 갔는데, 그 번호가 영 심심한 수 같아서, 불길한 징조가 아니기를 바란다고 말을 건넸다. 그러자 그가 답했다. “아닙니다. 아주 흥미로운 수예요. 두 세제곱수의 합으로, 두 가지 서로 다른 방법으로 쓸 수 있는 가장 작은 수거든요.”
병상에서 즉석으로 나온 그 말을 식으로 적으면 이렇습니다.
$$1729 = 1^3 + 12^3 = 9^3 + 10^3$$확인은 초등 산수입니다. $1 + 1728 = 1729$ 그리고 $729 + 1000 = 1729$. 같은 수에 도착하는 전혀 다른 두 갈래 길이 있고, 이런 일이 일어나는 수 가운데 1729가 막내라는 것이지요. 덕분에 1729에는 아예 택시수(Taxicab number) 라는 별명이 붙었고, 하디-라마누잔 수라고도 불립니다.
그런데 이 유명한 일화를 뒤집어 보면 질문이 하나 남습니다. 라마누잔급의 변호사가 붙으니 “심심한 수” 1729도 한 방에 스타가 됐습니다. 그렇다면 변호가 불가능한 수, 그러니까 정말로 아무 특징 없는 ‘지루한 수’라는 게 세상에 있기는 할까요?
놀랍게도 수학자들 사이에는 이 질문에 대한 농담 반 진담 반의 답이 오래전부터 전해 내려옵니다. “지루한 수는 단 하나도 없다. 그리고 그걸 증명할 수 있다.” 이것이 오늘의 주인공, 흥미로운 수 역설(Interesting Number Paradox) 입니다. 이번 글에서는 이 수상한 증명을 끝까지 따라가 봅니다. 먼저 증명을 구경하고(1장), 첫 번째 부품을 두 각도에서 뜯어보고(2~3장), 두 번째 부품을 검사한 다음(4장), 마지막에 범인을 잡습니다(5장). 무서운 수식은 쓰지 않습니다. 우리가 쓸 도구는 줄 세우기, 개수 세기, 그리고 컴퓨터로 수천 개의 수를 채점해 보는 작은 실험 — 이게 전부입니다.
이 글의 파이썬 코드는 모두 접이식입니다. 코드가 부담스러우시면 펼치지 않고 그림과 숫자, 이야기만 따라오셔도 내용은 온전히 이어집니다.
1. 가장 뻔뻔한 정리: 모든 수는 흥미롭다
1945년 4월, 점잖기로 유명한 미국의 수학 학술지(American Mathematical Monthly)에 짤막한 글 하나가 실립니다. 수학자 에드윈 베켄바흐(Edwin Beckenbach)가 쓴 “흥미로운 정수들(Interesting Integers)“이라는 노트로, 이 역설이 활자로 남은 가장 오래된 기록입니다. 주장은 이렇습니다.
정리(라고 주장됨). 모든 자연수 1, 2, 3, 4, … 는 흥미롭다.
증명이라는 것도 따라옵니다. 한 걸음씩 가 보겠습니다.
1단계. 흥미롭지 않은 수, 그러니까 ‘지루한 수’가 하나라도 있다고 가정해 봅시다.
2단계. 지루한 수가 있다면, 지루한 수들을 전부 모아서 작은 것부터 한 줄로 세울 수 있습니다. 그리고 자연수를 모아 놓은 줄에는 — 텅 빈 줄만 아니라면 — 반드시 맨 앞에 선 수(가장 작은 수) 가 있습니다.
3단계. 그 맨 앞의 수를 $m$ 이라고 부르겠습니다. $m$ 은 “세상에서 가장 작은 지루한 수"입니다.
4단계. 그런데 잠깐. “세상에서 가장 작은 지루한 수"라니, 그것 자체가 엄청나게 희귀한 타이틀 아닌가요? 무한히 많은 수 가운데 그 타이틀을 가진 수는 딱 하나뿐입니다. 친구에게 자랑해도 될 만한 특징입니다. 그러니까 $m$ 은… 흥미로운 수입니다.
5단계. 모순입니다. $m$ 은 지루한 수들의 줄에서 뽑아 왔는데, 흥미로워져 버렸으니까요. 무언가 잘못됐고, 잘못될 수 있는 곳은 1단계의 가정뿐입니다. 따라서 지루한 수는 존재하지 않습니다. 증명 끝.
어딘가 사기당한 기분이 드시지요? 정상입니다. 그 직감을 끝까지 밀고 가는 것이 이 글의 목표입니다.
이 논증은 발표 이후 꾸준히 사랑받았습니다. 1955년에는 콘스턴스 리드의 베스트셀러 교양서 “0에서 무한대까지(From Zero to Infinity)“에 실렸고, 1958년에는 마틴 가드너가 사이언티픽 아메리칸의 그 유명한 수학 칼럼에서 소개하며 대중적으로 널리 퍼졌습니다. 다만 가드너는 이것을 정리가 아니라 오류(fallacy) 모음 — 그럴듯해 보이지만 증명 어딘가에 교묘한 잘못이 숨어 있는 주장들 — 의 하나로 소개했습니다. 즉 수학계의 공식 입장은 “어딘가 틀렸다"입니다.
그런데, 어디가요? 한눈에 짚어 내기가 의외로 어렵습니다. 이 증명은 크게 부품 세 개로 조립되어 있습니다.
- ‘흥미롭다’는 수가 가질 수 있는 속성이다. (그래서 ‘지루한 수들’을 모을 수 있다.)
- 자연수의 줄에는 반드시 맨 앞이 있다. (그래서 가장 작은 지루한 수 $m$ 을 뽑을 수 있다.)
- “가장 작은 지루한 수"라는 타이틀은 그 자체로 흥미롭다. (그래서 모순이 터진다.)
범인을 잡으려면 부품을 하나씩 분해해서 검사해 봐야겠지요. 첫 번째 부품부터 시작합니다.
2. 첫 번째 부품: ‘흥미롭다’는 정말 수의 속성일까 — 1729의 변호
1729로 돌아가 봅시다. 솔직히 말해, 라마누잔의 한마디를 듣기 전까지 1729는 누가 봐도 평범한 수였습니다. 제곱수도 아니고, 소수도 아니고(7로 나누어집니다), 기념일이나 비밀번호에 쓸 만한 모양새도 아닙니다. 세계 최고 수준의 수학자였던 하디조차 “심심한 수"라고 불렀을 정도니까요.
그런데 변호사 라마누잔이 나서자 상황이 한 방에 뒤집혔습니다. 변호의 논리는 이렇습니다. 어떤 수들은 두 세제곱수의 합으로 쓸 수 있습니다. 예를 들어 $9 = 1 + 8 = 1^3 + 2^3$ 처럼요. 그런데 그렇게 쓸 수 있는 수들도 거의 전부는 쓰는 방법이 한 가지뿐입니다. 1729는 그 방법이 두 가지나 되는, 그러면서도 가장 작은 수라는 것이지요.
정말 그런지는 말로 다툴 필요가 없습니다. 컴퓨터에게 25,000까지 몽땅 세어 보라고 시키면 되니까요.
파이썬 코드 보기 (펼치기 / 접기)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 한글 차트용 폰트 자동 등록(윈도우=맑은 고딕). 없으면 시스템 한글 폰트를 이름으로 탐색,
# 그래도 없으면 라벨을 영어로 폴백(두부 방지, OS 이식성).
_has_kr, KFONT = False, "DejaVu Sans"
for _fp in [r"C:\Windows\Fonts\malgun.ttf",
"/System/Library/Fonts/AppleSDGothicNeo.ttc",
"/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"]:
try:
fm.fontManager.addfont(_fp)
KFONT = fm.FontProperties(fname=_fp).get_name()
_has_kr = True
break
except Exception:
continue
if not _has_kr:
_avail = {f.name for f in fm.fontManager.ttflist}
for _n in ["Malgun Gothic", "AppleGothic", "Apple SD Gothic Neo", "NanumGothic",
"Noto Sans CJK KR", "Noto Sans KR", "Source Han Sans KR", "UnDotum"]:
if _n in _avail:
KFONT, _has_kr = _n, True
break
if _has_kr:
plt.rcParams["font.family"] = KFONT
plt.rcParams["axes.unicode_minus"] = False
def L(ko, en):
return ko if _has_kr else en
DARK, LIGHT = "#0d1117", "#e6edf3"
BLUE, ORANGE, YELLOW, GREY, RED, GREEN = "#5dade2", "#e08a3c", "#f1c40f", "#7d8590", "#e74c3c", "#2ecc71"
# 1729: 두 양의 세제곱수의 합으로 '두 가지 방법'으로 쓸 수 있는 가장 작은 수
from collections import defaultdict
LIMIT = 25000 # 이 한도 안의 수만 전수 조사
ways = defaultdict(list) # 합 -> 그 합을 만드는 (a, b) 목록 (1 <= a <= b)
top = round(LIMIT ** (1 / 3)) + 1
for a in range(1, top + 1):
for b in range(a, top + 1):
s = a ** 3 + b ** 3
if s <= LIMIT:
ways[s].append((a, b))
single = sorted(s for s, ab in ways.items() if len(ab) == 1)
double = sorted(s for s, ab in ways.items() if len(ab) >= 2)
print(f"{LIMIT:,} 이하에서 '두 양의 세제곱수의 합'으로 쓸 수 있는 수: {len(ways):,}개")
print(f"그중 두 가지 방법으로 쓸 수 있는 수: 단 {len(double)}개\n")
for s in double:
reps = " = ".join(f"{a}³ + {b}³" for a, b in ways[s])
print(f" {s:>6,} = {reps}")
fig = plt.figure(figsize=(13, 5.6))
fig.patch.set_facecolor(DARK)
gs = fig.add_gridspec(1, 2, width_ratios=[1.0, 1.62], wspace=0.24,
left=0.06, right=0.975, top=0.80, bottom=0.13)
# (왼쪽) 곡선 x³ + y³ = 1729 위에 정확히 올라앉는 두 쌍의 정수 좌표
axL = fig.add_subplot(gs[0, 0])
axL.set_facecolor(DARK)
xx = np.linspace(0, 1729 ** (1 / 3), 400)
axL.plot(xx, np.cbrt(1729 - xx ** 3), color=BLUE, lw=2.4, zorder=3)
gx, gy = np.meshgrid(np.arange(14), np.arange(14))
axL.scatter(gx, gy, s=7, color="#2b3138", zorder=1)
for (px, py), col in [((1, 12), RED), ((9, 10), YELLOW)]:
axL.scatter([px, py], [py, px], s=130, color=col, edgecolor=LIGHT,
linewidth=0.9, zorder=5)
axL.annotate("1³ + 12³", xy=(1, 12), xytext=(2.7, 12.3), color=RED,
fontsize=13, fontweight="bold")
axL.annotate("9³ + 10³", xy=(9, 10), xytext=(9.7, 11.2), color=YELLOW,
fontsize=13, fontweight="bold")
axL.set_xlim(-0.6, 13.6)
axL.set_ylim(-0.6, 13.6)
axL.set_aspect("equal")
axL.set_title(L("곡선 x³ + y³ = 1729 를 통과하는 정수 좌표", "Integer points on x³ + y³ = 1729"),
color=LIGHT, fontsize=12.5, pad=10)
# (오른쪽) 25,000 이하의 모든 '두 세제곱수의 합'을 가짓수별로 늘어놓기
axR = fig.add_subplot(gs[0, 1])
axR.set_facecolor(DARK)
rng = np.random.default_rng(1729)
axR.scatter(single, 1 + rng.uniform(-0.16, 0.16, len(single)),
s=9, color=GREY, alpha=0.5, lw=0)
for s in double:
axR.plot([s, s], [0.55, 1.97], color=ORANGE, lw=1.1, alpha=0.75, zorder=2)
axR.scatter([s], [2], marker="*", s=340, color=YELLOW, edgecolor=DARK,
lw=0.6, zorder=5)
axR.annotate(f"{s:,}", xy=(s, 2), xytext=(s, 2.22), color=YELLOW,
ha="center", fontsize=11)
axR.annotate(L("첫 번째 주인공 = 1729", "the first one = 1729"),
xy=(1850, 2.06), xytext=(7300, 2.62), color=LIGHT, fontsize=12.5,
arrowprops=dict(arrowstyle="->", color=LIGHT, lw=1.1))
axR.set_ylim(0.4, 2.95)
axR.set_yticks([1, 2])
axR.set_yticklabels([L("1가지 방법", "1 way"), L("2가지 방법", "2 ways")],
color=LIGHT, fontsize=11.5)
axR.set_xlabel(L("두 양의 세제곱수의 합으로 만들어지는 수", "sums of two positive cubes"),
color=LIGHT, fontsize=11.5)
axR.set_title(L("같은 수에 도착하는 길의 가짓수 (25,000 이하)", "How many ways to reach each sum (≤ 25,000)"),
color=LIGHT, fontsize=12.5, pad=10)
for ax in (axL, axR):
for sp in ax.spines.values():
sp.set_color("#3a414a")
ax.tick_params(colors=GREY)
fig.suptitle(L("1729 — 두 세제곱수의 합으로 '두 번' 쓸 수 있는 가장 작은 수",
"1729 — the smallest sum of two cubes in two different ways"),
color=LIGHT, fontsize=15.5, fontweight="bold", y=0.95)
plt.show()
25,000 이하에서 '두 양의 세제곱수의 합'으로 쓸 수 있는 수: 373개
그중 두 가지 방법으로 쓸 수 있는 수: 단 4개
1,729 = 1³ + 12³ = 9³ + 10³
4,104 = 2³ + 16³ = 9³ + 15³
13,832 = 2³ + 24³ = 18³ + 20³
20,683 = 10³ + 27³ = 19³ + 24³

결과를 읽어 봅시다. 25,000 이하에서 두 양의 세제곱수의 합으로 쓸 수 있는 수는 모두 373개. 오른쪽 그림 아래쪽에 회색 점으로 깔린 다수가 전부 “한 가지 방법"짜리입니다. 그중 “두 가지 방법"이 되는 수는 딱 4개 — 1729, 4104, 13832, 20683 — 뿐이고, 그 맨 앞에 1729가 있습니다. 왼쪽 그림은 같은 사실을 다른 각도에서 보여 줍니다. 곡선 $x^3 + y^3 = 1729$ 가 정수 좌표를 정확히 통과하는 사건이 두 번 일어나는 것이지요. (대각선 반대편의 거울상까지 치면 점은 네 개입니다만, 조합으로는 같은 두 가지입니다.)
여담을 셋만 얹겠습니다. 첫째, 사실 이 성질은 라마누잔이 처음 발견한 것이 아닙니다. 프랑스의 수학자 프레니클 드 베시(Frénicle de Bessy)가 1657년에 이미 기록해 두었습니다. 라마누잔보다 260여 년 앞선 것인데, 그렇다고 일화의 가치가 깎이지는 않습니다. 병상에서 그 사실이 즉석에서 튀어나왔다는 것 자체가 이미 명장면이니까요. 둘째, ‘양의’ 세제곱수라는 조건이 은근히 중요합니다. 음수까지 허용하면 $91 = 3^3 + 4^3 = 6^3 + (-5)^3$ 이라서, 91이 훨씬 먼저 두 가지 방법을 갖게 됩니다. 셋째, 1729의 이력서에는 다른 줄도 있습니다. 소인수분해하면 $1729 = 7 \times 13 \times 19$ 인데, 자릿수를 더하면 $1 + 7 + 2 + 9 = 19$ 가 되고, 1729는 이 19로 나누어떨어집니다($1729 = 19 \times 91$). 게다가 그 몫 91은 남은 두 소인수의 곱($7 \times 13$)입니다. 1729는 또 ‘소수 판별 시험을 속이는 가짜 소수’로 악명 높은 카마이클 수의 세 번째 멤버이기도 합니다(561, 1105 다음). 변호사가 작정하고 뒤지면 변호 거리가 줄줄이 나오는 수인 셈입니다.
정리하면, 첫 번째 부품은 절반쯤 합격입니다. ‘흥미롭다’의 정확한 정의는 아직 흐릿하지만, 적어도 “이 수에는 이런 특징이 있다"라고 변호하는 일 자체는 분명히 가능합니다. 그렇다면 자연스럽게 궁금해집니다. 모든 수가 1729처럼 변호받을 수 있을까요? 아니면 변호사도 두 손 드는 수가 어딘가에 있을까요?
직접 재 봅시다.
3. 첫 번째 부품, 이어서: 지루한 수 사냥 — 잣대 11개로 1,000개를 채점하다
‘흥미롭다’를 정확히 잴 수는 없지만, 흉내 낼 수는 있습니다. 수학에서 사랑받는 유명한 특징 11가지로 잣대 목록을 만들어 보겠습니다.
- 소수 — 1과 자기 자신으로만 나누어지는 수 (2, 3, 5, 7, …)
- 제곱수 — 같은 수를 두 번 곱해 만든 수 (1, 4, 9, 16, …)
- 세제곱수 — 같은 수를 세 번 곱해 만든 수 (1, 8, 27, …)
- 삼각수 — 1, 1+2, 1+2+3, … 처럼 차곡차곡 더해 만든 수 (1, 3, 6, 10, …)
- 피보나치 수 — 앞의 두 수를 더해 다음 수를 만드는 그 유명한 수열 (1, 2, 3, 5, 8, …)
- 팩토리얼 — 1×2, 1×2×3, … 처럼 차례로 곱해 만든 수 (1, 2, 6, 24, 120, …)
- 2의 거듭제곱 — 2를 거듭 곱한 수 (1, 2, 4, 8, 16, …)
- 회문수 — 거꾸로 읽어도 똑같은 수 (1~9, 11, 22, …, 121, …)
- 완전수 — 자신을 뺀 약수를 모두 더하면 자기 자신이 되는 수 (6, 28, 496)
- 고합성수 — 약수 개수의 역대 신기록 보유자 (1, 2, 4, 6, 12, 24, …)
- 카탈랑 수 — 조합 문제마다 단골로 튀어나오는 수열 (1, 2, 5, 14, 42, …)
어떤 수가 이 가운데 몇 개의 잣대를 통과하는지를 그 수의 ‘흥미로움 점수’라고 부르기로 하고, 1부터 1,000까지 전부 채점해 보겠습니다. 과연 0점짜리 — 우리 잣대 기준의 ‘지루한 수’ — 가 나올까요?
파이썬 코드 보기 (펼치기 / 접기)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 한글 차트용 폰트 자동 등록(윈도우=맑은 고딕). 없으면 시스템 한글 폰트를 이름으로 탐색,
# 그래도 없으면 라벨을 영어로 폴백(두부 방지, OS 이식성).
_has_kr, KFONT = False, "DejaVu Sans"
for _fp in [r"C:\Windows\Fonts\malgun.ttf",
"/System/Library/Fonts/AppleSDGothicNeo.ttc",
"/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"]:
try:
fm.fontManager.addfont(_fp)
KFONT = fm.FontProperties(fname=_fp).get_name()
_has_kr = True
break
except Exception:
continue
if not _has_kr:
_avail = {f.name for f in fm.fontManager.ttflist}
for _n in ["Malgun Gothic", "AppleGothic", "Apple SD Gothic Neo", "NanumGothic",
"Noto Sans CJK KR", "Noto Sans KR", "Source Han Sans KR", "UnDotum"]:
if _n in _avail:
KFONT, _has_kr = _n, True
break
if _has_kr:
plt.rcParams["font.family"] = KFONT
plt.rcParams["axes.unicode_minus"] = False
def L(ko, en):
return ko if _has_kr else en
DARK, LIGHT = "#0d1117", "#e6edf3"
BLUE, ORANGE, YELLOW, GREY, RED, GREEN = "#5dade2", "#e08a3c", "#f1c40f", "#7d8590", "#e74c3c", "#2ecc71"
# 잣대 11개로 1~1,000의 '흥미로움 점수'를 매기고, 첫 번째 0점짜리 수를 찾는다
N = 1000
sieve = np.ones(N + 1, dtype=bool)
sieve[:2] = False
for k in range(2, int(N ** 0.5) + 1):
if sieve[k]:
sieve[k * k::k] = False
primes = {int(n) for n in np.flatnonzero(sieve)}
squares = {k * k for k in range(1, N) if k * k <= N}
cubes = {k ** 3 for k in range(1, N) if k ** 3 <= N}
tri = {k * (k + 1) // 2 for k in range(1, N) if k * (k + 1) // 2 <= N}
fib, (a, b) = set(), (1, 2)
while a <= N:
fib.add(a)
a, b = b, a + b
fact, (f, k) = set(), (1, 1)
while f <= N:
fact.add(f)
k += 1
f *= k
pow2 = {2 ** k for k in range(20) if 2 ** k <= N}
palin = {n for n in range(1, N + 1) if str(n) == str(n)[::-1]}
perfect = {n for n in range(2, N + 1)
if sum(d for d in range(1, n) if n % d == 0) == n}
ndiv = np.zeros(N + 1, dtype=int) # 고합성수: 약수 개수의 신기록 보유자
for d in range(1, N + 1):
ndiv[d::d] += 1
hc, best = set(), 0
for n in range(1, N + 1):
if ndiv[n] > best:
hc.add(n)
best = int(ndiv[n])
catalan, (c, k) = set(), (1, 0)
while c <= N:
catalan.add(c)
c = c * 2 * (2 * k + 1) // (k + 2)
k += 1
BATTERY = [("소수", primes), ("제곱수", squares), ("세제곱수", cubes),
("삼각수", tri), ("피보나치 수", fib), ("팩토리얼", fact),
("2의 거듭제곱", pow2), ("회문수", palin), ("완전수", perfect),
("고합성수", hc), ("카탈랑 수", catalan)]
score = np.array([sum(n in s for _, s in BATTERY) for n in range(N + 1)])
zeros = [n for n in range(1, N + 1) if score[n] == 0]
first_boring = zeros[0]
print("우리 잣대 11개:", ", ".join(name for name, _ in BATTERY))
print(f"\n1~{N:,} 채점 결과:")
for v in range(int(score[1:].max()) + 1):
cnt = int((score[1:] == v).sum())
if cnt:
print(f" {v}점: {cnt:>3}개 ({cnt / N:.1%})")
print(f"\n가장 작은 0점짜리 수 = {first_boring}")
print(f"→ 축하합니다, {first_boring}. 방금 '가장 작은 지루한 수'가 되면서 그림의 주인공이 되었습니다.")
fig = plt.figure(figsize=(13, 5.6))
fig.patch.set_facecolor(DARK)
gs = fig.add_gridspec(1, 2, width_ratios=[2.6, 1.0], wspace=0.18,
left=0.055, right=0.975, top=0.82, bottom=0.13)
# (왼쪽) 수직선 위의 흥미로움 점수
ax = fig.add_subplot(gs[0, 0])
ax.set_facecolor(DARK)
rng = np.random.default_rng(42)
ns = np.arange(1, N + 1)
ys = score[1:] + rng.uniform(-0.13, 0.13, N)
m0 = score[1:] == 0
m1 = (score[1:] >= 1) & (score[1:] <= 2)
m3 = score[1:] >= 3
ax.scatter(ns[m0], ys[m0], s=10, color="#3a414a", lw=0)
ax.scatter(ns[m1], ys[m1], s=12, color=BLUE, alpha=0.75, lw=0)
ax.scatter(ns[m3], ys[m3], s=34, color=YELLOW, lw=0, zorder=4)
ax.scatter([first_boring], [ys[first_boring - 1]], s=240, facecolors="none",
edgecolors=RED, lw=2.2, zorder=6)
ax.annotate(L(f"가장 작은 0점 = {first_boring}\n(이 순간 흥미로워짐)",
f"smallest zero-scorer = {first_boring}\n(instantly interesting)"),
xy=(first_boring, ys[first_boring - 1]), xytext=(175, 3.85),
color=RED, fontsize=12.5, fontweight="bold",
arrowprops=dict(arrowstyle="->", color=RED, lw=1.4))
for n in [int(v) for v in ns[score[1:] >= 5]]: # 5점 이상의 유명인사들
ax.annotate(f"{n} ({score[n]}{L('점', ' pts')})", xy=(n, score[n]),
xytext=(n + 16, score[n] + 0.22), color=YELLOW, fontsize=11)
ax.set_xlabel(L("수 (1~1,000)", "n (1–1,000)"), color=LIGHT, fontsize=11.5)
ax.set_ylabel(L("흥미로움 점수 (통과한 잣대 개수)", "interestingness score"),
color=LIGHT, fontsize=11.5)
ax.set_yticks(range(0, int(score.max()) + 1))
ax.set_title(L("대부분의 수는 어떤 잣대도 통과하지 못합니다", "Most numbers pass no test at all"),
color=LIGHT, fontsize=12.5, pad=10)
# (오른쪽) 점수 분포: 지루함이 기본값
ax2 = fig.add_subplot(gs[0, 1])
ax2.set_facecolor(DARK)
vals = range(int(score[1:].max()) + 1)
cnts = [int((score[1:] == v).sum()) for v in vals]
cols = ["#3a414a" if v == 0 else (BLUE if v <= 2 else YELLOW) for v in vals]
ax2.bar(vals, cnts, color=cols, width=0.72)
for v, c in zip(vals, cnts):
if c:
ax2.annotate(f"{c}", xy=(v, c), xytext=(v, c * 1.25), ha="center",
color=LIGHT, fontsize=10)
ax2.set_yscale("log")
ax2.set_ylim(0.7, 2300)
ax2.set_xticks(list(vals))
ax2.set_xlabel(L("점수", "score"), color=LIGHT, fontsize=11.5)
ax2.set_ylabel(L("수의 개수 (로그 눈금)", "count (log scale)"), color=LIGHT, fontsize=11.5)
ax2.set_title(L("지루함이 기본값", "Boring is the default"), color=LIGHT,
fontsize=12.5, pad=10)
for a_ in (ax, ax2):
for sp in a_.spines.values():
sp.set_color("#3a414a")
a_.tick_params(colors=GREY, which="both")
fig.suptitle(L("잣대 11개로 잰 1~1,000의 '흥미로움 점수' — 그리고 첫 번째 0점의 운명",
"Interestingness scores of 1–1,000 under 11 tests — and the first zero"),
color=LIGHT, fontsize=15.5, fontweight="bold", y=0.95)
plt.show()
우리 잣대 11개: 소수, 제곱수, 세제곱수, 삼각수, 피보나치 수, 팩토리얼, 2의 거듭제곱, 회문수, 완전수, 고합성수, 카탈랑 수
1~1,000 채점 결과:
0점: 660개 (66.0%)
1점: 290개 (29.0%)
2점: 39개 (3.9%)
3점: 4개 (0.4%)
4점: 4개 (0.4%)
5점: 1개 (0.1%)
7점: 1개 (0.1%)
9점: 1개 (0.1%)
가장 작은 0점짜리 수 = 18
→ 축하합니다, 18. 방금 '가장 작은 지루한 수'가 되면서 그림의 주인공이 되었습니다.

채점표가 꽤 가혹합니다. 1,000개 가운데 660개, 그러니까 66%가 0점입니다. 1점이 290개, 2점부터는 가파르게 줄어서 3점 이상의 ‘유명인사’는 통틀어 11개뿐입니다. 만점왕은 수 1입니다. 제곱수이자 세제곱수이자 삼각수이자 피보나치 수이자 팩토리얼이자… 무려 9점입니다. 2가 7점으로 그 뒤를 잇고, 6은 삼각수·팩토리얼·회문수·완전수·고합성수를 동시에 만족하는 5점짜리입니다. 55처럼 “삼각수이면서 피보나치 수이면서 회문수"인 숨은 재주꾼(3점)도 있습니다.
그리고 대망의 첫 0점, 18. 소수도, 제곱수도, 삼각수도, 피보나치 수도 아니고… 열한 개의 잣대를 전부 빠져나간 첫 번째 수입니다. 세상에서 가장 지루한 수의 후보를 찾았습니다.
그런데 축배를 들기 전에, 벌써 두 가지가 수상합니다.
첫째, 18에게도 변호사를 붙여 보면 — 나옵니다. 18은 자기 자릿수 합의 정확히 두 배가 되는 유일한 자연수입니다: $18 = 2 \times (1 + 8)$. 다른 수로는 안 됩니다(한 자리 수는 두 배 하는 순간 자기 자신을 넘어가 버리고, 두 자리 수로 따져 보면 조건이 “일의 자리 = 8 × 십의 자리"가 되어 18 하나만 남으며, 세 자리부터는 자릿수 합을 두 배 해도 턱없이 모자랍니다). 그러니까 18이 0점을 받은 것은 18이 지루해서가 아니라 우리 잣대가 11개뿐이어서입니다. 잣대 목록을 바꾸면 채점 결과도 바뀝니다. ‘흥미롭다’의 정체가 목록에 따라 출렁인다는 첫 번째 신호입니다.
둘째가 더 고약합니다. 18은 방금 “이 글에서 가장 작은 0점"이라는 이유로 그림의 주인공이 되어, 빨간 동그라미와 전용 화살표까지 받았습니다. 지루하다고 손가락질당하는 순간 흥미로워진 것입니다. 농담처럼 들리시겠지만, 정확히 이 일이 현실에서 반복적으로 일어났습니다. 기록이 남은 사건만 셋을 보여 드리겠습니다.
사건 1 — 39의 운명 (1986). 영국의 수학 저술가 데이비드 웰스는 흥미로운 수들을 작은 수부터 차례로 모은 사전, “흥미롭고 신기한 수 사전(The Penguin Dictionary of Curious and Interesting Numbers)“을 펴냈습니다. 그런데 이 사전에는 39번 항목에 이렇게 적혀 있습니다. “이 수는 첫 번째 흥미롭지 않은 수로 보인다. 물론 바로 그 사실이 이 수를 특별히 흥미롭게 만든다. 흥미롭지 않다는 성질을 가진 가장 작은 수이기 때문이다.” 지루함의 대표로 지목되는 순간 사전에 등재되어 버린 것입니다. 후일담도 있습니다. 1997년 개정판에서는 그 타이틀이 51로 넘어갔다고 전해집니다. 39는 한 번 지목된 덕분에 더 이상 그 자리에 있을 수 없었던 것이지요.
사건 2 — 11630의 저주 (2009). 수학자들에게는 OEIS(온라인 정수열 사전)라는 거대한 도감이 있습니다. 닐 슬론이 1964년에 손으로 쓴 카드 묶음으로 시작해 1973년에 수열 2,372개짜리 책으로, 1996년부터는 웹사이트로 키워 온 데이터베이스로, 지금은 39만 개가 넘는 수열(2025년 말 기준)이 등록되어 있습니다. 덕분에 ‘흥미로움’의 제법 객관적인 대용 지표가 하나 생깁니다. 그 수가 몇 개의 수열에 등장하는가. 2009년 6월 12일, 수학자 너새니얼 존스턴은 당시 159,437개의 수열을 전부 뒤져서 “11630은 첫 번째 흥미롭지 않은 수"라는 글을 발표합니다. 11630이 단 하나의 수열에도 등장하지 않는 가장 작은 수였던 것입니다. 그다음에 벌어진 일은 예상하신 그대로입니다. 글이 화제가 되자 11630은 이런저런 수열에 등장하기 시작했고, 같은 해 11월에는 타이틀이 12407로 넘어갔습니다. 2013년에는 14228로, 2020년대 초의 집계로는 20,067로 — 이 저주받은 왕관은 쓰는 순간 박탈되면서 계속 옮겨 다니는 중입니다. (덧붙이자면 존스턴이 이런 ‘흥미롭지 않은 수들’을 모아 아예 새 수열로 OEIS에 제출해 보았는데, 등재가 거절되었다는 후일담까지 있습니다.)
사건 3 — 슬론의 간극 (2011). 그렇다면 OEIS 등장 횟수라는 지표는 수의 세계를 어떤 모습으로 그려 낼까요? 2011년 고브리, 들라에, 제닐 세 연구자가 1부터 10,000까지의 모든 수에 대해 “등장하는 수열의 개수"를 세어 점을 찍어 보았습니다. 수가 커질수록 등장 횟수가 줄어드는 내리막이야 예상대로였는데, 충격은 모양이었습니다. 점들이 매끈한 띠 하나가 아니라 위아래 두 개의 구름으로 갈라져 있고, 그 사이가 휑하니 비어 있었던 것입니다. 이 빈 띠에는 ‘슬론의 간극(Sloane’s Gap)‘이라는 이름이 붙었습니다. 위쪽 구름(조사 구간의 약 18%)은 소수(위 구름의 약 3분의 2), 제곱수, 소인수가 많은 수 같은 ‘셀럽’들의 동네였습니다. 흥미로운 것은 그 이유입니다. 수의 복잡도에 대한 이론으로 구름 전체의 내리막 모양은 설명이 되는데, 간극만은 설명이 안 됩니다. 무작위로 만든 가짜 데이터베이스에서는 간극이 생기지 않거든요. 연구진이 지목한 남은 용의자는 수학 그 자체가 아니라 사람이었습니다. 수학자들이 일부 수를 더 많이 연구하니까, 그 수들이 더 많이 등장하고, 그래서 더 유명해지고, 그래서 더 연구되는 되먹임이 구름을 두 동강 낸다는 것입니다. 같은 데이터의 한 장면이 이 아이러니를 잘 보여 줍니다. 2009년 집계에서 전설의 주인공 1729는 380개의 수열에 등장했는데, 아무 일화도 없는 이웃 1728은 622개의 수열에 등장했습니다($1728 = 12^3$ 이라 조합 문제의 단골이거든요). 흥미로움이란 잰다고 가만히 있는 잣대가 아니라, 재는 사람들 때문에 움직이는 잣대였던 것입니다.
첫 번째 부품의 판정을 내리겠습니다. ‘흥미롭다’는 수에 새겨진 고정된 속성이라기보다, 수와 사람 사이에서 일어나는 사건에 가깝습니다. 일단 “수상함"으로 보류해 두고, 두 번째 부품으로 넘어갑니다.
4. 두 번째 부품: 막내는 반드시 있다 — 그리고 도미노
두 번째 부품은 “자연수의 줄에는 반드시 맨 앞이 있다"였습니다. 결론부터 말씀드리면 이 부품은 정품입니다. 수학에서는 정렬 원리(well-ordering principle) 라고 부릅니다. 자연수를 모아 놓은 묶음은, 비어 있지만 않다면, 반드시 가장 작은 원소를 갖는다는 원리입니다.
너무 당연해서 원리라고 부르기 민망할 정도지만, 사실 이 성질은 자연수의 세계가 누리는 특권입니다. 옆 동네인 ‘0보다 큰 분수의 세계’만 가도 무너지거든요. 가장 작은 양의 분수를 찾아보세요. 1/2? 그보다 작은 1/4이 있습니다. 1/100? 1/200이 있습니다. 어떤 후보를 잡아도 그 절반이 항상 더 작기 때문에, 그 세계에는 막내가 없습니다. 자연수의 세계는 다릅니다. 바닥이 1로 막혀 있고 수들이 띄엄띄엄 서 있어서, 어떤 묶음을 골라도 아래로 무한히 내려가는 일이 불가능합니다. 그래서 막내가 반드시 있습니다. 두 번째 부품은 의심의 여지 없이 합격 — 증명의 엔진 자체는 정품이라는 뜻입니다.
엔진이 정품이라면, 이제 증명이 실제로 일하는 모습을 구경해 봅시다. 3장의 잣대 11개를 1부터 50까지에 적용하면 0점짜리 ‘지루한 수’가 10개 나옵니다. 역설의 논리를 그대로 기계로 만들어서, 매 라운드 “지금 가장 작은 지루한 수"에게 타이틀을 수여하고 흥미로움으로 승격시키면 무슨 일이 벌어질까요?
파이썬 코드 보기 (펼치기 / 접기)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 한글 차트용 폰트 자동 등록(윈도우=맑은 고딕). 없으면 시스템 한글 폰트를 이름으로 탐색,
# 그래도 없으면 라벨을 영어로 폴백(두부 방지, OS 이식성).
_has_kr, KFONT = False, "DejaVu Sans"
for _fp in [r"C:\Windows\Fonts\malgun.ttf",
"/System/Library/Fonts/AppleSDGothicNeo.ttc",
"/usr/share/fonts/truetype/nanum/NanumGothic.ttf",
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
"/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc"]:
try:
fm.fontManager.addfont(_fp)
KFONT = fm.FontProperties(fname=_fp).get_name()
_has_kr = True
break
except Exception:
continue
if not _has_kr:
_avail = {f.name for f in fm.fontManager.ttflist}
for _n in ["Malgun Gothic", "AppleGothic", "Apple SD Gothic Neo", "NanumGothic",
"Noto Sans CJK KR", "Noto Sans KR", "Source Han Sans KR", "UnDotum"]:
if _n in _avail:
KFONT, _has_kr = _n, True
break
if _has_kr:
plt.rcParams["font.family"] = KFONT
plt.rcParams["axes.unicode_minus"] = False
def L(ko, en):
return ko if _has_kr else en
DARK, LIGHT = "#0d1117", "#e6edf3"
BLUE, ORANGE, YELLOW, GREY, RED, GREEN = "#5dade2", "#e08a3c", "#f1c40f", "#7d8590", "#e74c3c", "#2ecc71"
# 역설의 논리를 그대로 실행: '가장 작은 지루한 수'를 한 라운드에 하나씩 승격시키는 도미노
import matplotlib.patches as mpatches
from matplotlib.patches import Rectangle
M = 50
sieve = np.ones(M + 1, dtype=bool)
sieve[:2] = False
for k in range(2, int(M ** 0.5) + 1):
if sieve[k]:
sieve[k * k::k] = False
primes = {int(n) for n in np.flatnonzero(sieve)}
squares = {k * k for k in range(1, M) if k * k <= M}
cubes = {k ** 3 for k in range(1, M) if k ** 3 <= M}
tri = {k * (k + 1) // 2 for k in range(1, M) if k * (k + 1) // 2 <= M}
fib, (a, b) = set(), (1, 2)
while a <= M:
fib.add(a)
a, b = b, a + b
fact, (f, k) = set(), (1, 1)
while f <= M:
fact.add(f)
k += 1
f *= k
pow2 = {2 ** k for k in range(10) if 2 ** k <= M}
palin = {n for n in range(1, M + 1) if str(n) == str(n)[::-1]}
perfect = {n for n in range(2, M + 1)
if sum(d for d in range(1, n) if n % d == 0) == n}
ndiv = np.zeros(M + 1, dtype=int)
for d in range(1, M + 1):
ndiv[d::d] += 1
hc, best = set(), 0
for n in range(1, M + 1):
if ndiv[n] > best:
hc.add(n)
best = int(ndiv[n])
catalan, (c, k) = set(), (1, 0)
while c <= M:
catalan.add(c)
c = c * 2 * (2 * k + 1) // (k + 2)
k += 1
BATTERY = [primes, squares, cubes, tri, fib, fact, pow2, palin, perfect, hc, catalan]
born_interesting = {n for n in range(1, M + 1) if any(n in s for s in BATTERY)}
boring0 = [n for n in range(1, M + 1) if n not in born_interesting]
print(f"1~{M}에서 우리 잣대(11개)로 0점인 '지루한 수' {len(boring0)}개: {boring0}\n")
# 0 = 아직 지루함, 1 = 원래 흥미로움(잣대 통과), 2 = '타이틀'로 승격됨
state = np.zeros(M + 1, dtype=int)
state[list(born_interesting)] = 1
rows, promoted = [state[1:].copy()], []
r = 0
while (state[1:] == 0).any():
r += 1
m = min(n for n in range(1, M + 1) if state[n] == 0)
print(f"{r:>2}라운드: 지금 가장 작은 지루한 수 = {m} → "
f"'가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격")
state[m] = 2
promoted.append(m)
rows.append(state[1:].copy())
print(f"\n종료: 지루한 수 0개. '증명' 완료 — ...정말일까요?")
grid = np.array(rows) # (라운드 + 1) x 50
R = grid.shape[0]
fig, ax = plt.subplots(figsize=(13, 5.2))
fig.patch.set_facecolor(DARK)
ax.set_facecolor(DARK)
PAL = {0: "#30363d", 1: "#33536e", 2: "#f1c40f"}
rgb = np.array([[tuple(int(PAL[v][i:i + 2], 16) / 255 for i in (1, 3, 5))
for v in row] for row in grid])
ax.imshow(rgb, aspect="auto", interpolation="nearest",
extent=(0.5, M + 0.5, R - 0.5, -0.5))
# 셀 경계선 + 이번 라운드에 승격된 칸 강조(빨간 테두리와 숫자)
for x in np.arange(0.5, M + 1, 1):
ax.plot([x, x], [-0.5, R - 0.5], color=DARK, lw=0.7)
for y in np.arange(-0.5, R, 1):
ax.plot([0.5, M + 0.5], [y, y], color=DARK, lw=0.7)
for r_, m in enumerate(promoted, start=1):
ax.add_patch(Rectangle((m - 0.5, r_ - 0.5), 1, 1, fill=False,
edgecolor=RED, lw=1.8, zorder=5))
ax.annotate(str(m), xy=(m, r_), ha="center", va="center",
color=DARK, fontsize=7.5, fontweight="bold", zorder=6)
ax.set_yticks(range(R))
ax.set_yticklabels([L("시작", "start")] +
[L(f"{i}라운드", f"round {i}") for i in range(1, R)],
color=LIGHT, fontsize=10)
ax.set_xticks([1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50])
ax.tick_params(colors=GREY)
ax.set_xlabel(L("수 (1~50)", "n (1–50)"), color=LIGHT, fontsize=11.5)
for sp in ax.spines.values():
sp.set_color("#3a414a")
handles = [mpatches.Patch(color=PAL[1], label=L("원래 흥미로움 (잣대 통과)", "interesting by the tests")),
mpatches.Patch(color=PAL[0], label=L("아직 지루함", "still boring")),
mpatches.Patch(color=PAL[2], label=L("'가장 작은 지루한 수' 타이틀로 승격", "promoted by the title"))]
leg = ax.legend(handles=handles, loc="upper center", bbox_to_anchor=(0.5, -0.13),
ncol=3, frameon=False, fontsize=10.5)
for t in leg.get_texts():
t.set_color(LIGHT)
fig.suptitle(L("도미노 멸종 — '가장 작은 지루한 수'는 그 타이틀 때문에 살아남지 못합니다",
"Domino extinction — the smallest boring number never survives its own title"),
color=LIGHT, fontsize=15, fontweight="bold", y=0.97)
fig.subplots_adjust(left=0.075, right=0.985, top=0.88, bottom=0.2)
plt.show()
1~50에서 우리 잣대(11개)로 0점인 '지루한 수' 10개: [18, 20, 26, 30, 35, 38, 39, 40, 46, 50]
1라운드: 지금 가장 작은 지루한 수 = 18 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
2라운드: 지금 가장 작은 지루한 수 = 20 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
3라운드: 지금 가장 작은 지루한 수 = 26 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
4라운드: 지금 가장 작은 지루한 수 = 30 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
5라운드: 지금 가장 작은 지루한 수 = 35 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
6라운드: 지금 가장 작은 지루한 수 = 38 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
7라운드: 지금 가장 작은 지루한 수 = 39 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
8라운드: 지금 가장 작은 지루한 수 = 40 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
9라운드: 지금 가장 작은 지루한 수 = 46 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
10라운드: 지금 가장 작은 지루한 수 = 50 → '가장 작은 지루한 수'라는 희귀한 타이틀 획득 → 흥미로움으로 승격
종료: 지루한 수 0개. '증명' 완료 — ...정말일까요?

1라운드에서 18이 — 3장에서 본 그대로 — 왕관을 쓰고 승격됩니다. 그러면 “가장 작은 지루한 수” 자리는 20에게 넘어가고, 2라운드에서 20도 같은 논리로 승격됩니다. 26, 30, 35, 38이 차례로 사라지고, 7라운드에는 웰스 사전의 그 39가 정확히 같은 운명을 맞습니다. 그렇게 딱 10라운드 만에 지루한 수는 멸종합니다. 시작 목록이 무엇이었든 결과는 같습니다. 매번 줄의 맨 앞이 잘려 나가니, 지루한 수의 줄은 반드시 앞에서부터 통째로 사라질 수밖에 없습니다.
그런데 이 그림을 가만히 들여다보면 속임수의 꼬리가 보이기 시작합니다. 색을 보세요. 파란 칸은 잣대 11개 중 무언가를 통과해서 흥미로워진 수입니다. 반면 노란 칸은 잣대를 단 하나도 통과하지 못한 채, “타이틀"이라는 전혀 다른 종류의 이유로 흥미로워진 수입니다. 그리고 그 타이틀(“가장 작은 지루한 수”)이 가리키는 대상은 한 라운드가 지날 때마다 바뀝니다. 18이 왕관을 쓴 근거는 1라운드가 끝나는 순간 이미 소멸했습니다. 2라운드의 세계에서 “가장 작은 지루한 수"는 18이 아니라 20이니까요.
엄밀히 말하면 베켄바흐의 원래 증명은 이런 도미노가 아니라 “막내 $m$ 이 있다, $m$ 은 흥미롭다, 모순"으로 한 방에 끝나는 귀류법입니다. 하지만 일부러 도미노 버전으로 풀어 그려 본 이유가 있습니다. 한 방짜리 귀류법에서는 트릭이 어디서 작동하는지 보이지 않지만, 라운드를 거듭하는 도미노에서는 매 라운드 게임의 규칙이 바뀌고 있다는 사실이 색깔로 드러나기 때문입니다. 범인의 윤곽이 잡혔습니다. 검거하러 갑시다.
5. 범인 검거: 움직이는 골대
부품 검사 결과를 모아 봅시다. 두 번째 부품(정렬 원리)은 정품이었습니다. 세 번째 부품(“가장 작은 지루한 수라는 타이틀은 흥미롭다”)도 심정적으로는 인정할 만합니다. 그렇다면 남는 것은 첫 번째 부품, ‘흥미롭다’ 그 자체입니다. 그리고 범인은 실제로 여기에 숨어 있습니다.
정렬 원리는 아무 모임에나 들이댈 수 있는 만능 도구가 아닙니다. 어떤 수가 속하는지 아닌지가 똑 떨어지게 정해지는 모임에만 적용할 수 있습니다. “100보다 작은 짝수들"은 그런 모임입니다. 어떤 수를 들이밀어도 소속 여부가 다툼의 여지 없이 판정되니까요. 그런데 “지루한 수들"은 어떤가요? 18은 지루한가요? 우리 잣대 11개로는 지루하고, $18 = 2 \times (1+8)$ 잣대를 추가하면 흥미롭고, 이 글을 여기까지 읽으신 분에게는 이미 흥미로운 수입니다. 소속이 사람과 시점에 따라 출렁이는 이런 모임은 수학적인 의미의 집합이 아니고, 따라서 정렬 원리를 적용할 대상 자체가 애초에 없었습니다. 증명은 2단계에서 이미 시작조차 못 하고 있었던 것입니다.
그런데도 증명이 그럴듯해 보였던 이유는 4단계의 바꿔치기에 있습니다. 증명은 1~3단계에서 ‘흥미롭다’를 마치 고정된 잣대 목록처럼 다룹니다. 그래 놓고 결정적인 4단계에서 “가장 작은 지루한 수라는 타이틀” — 그 목록에는 없던, 목록 전체를 밖에서 내려다봐야만 생겨나는 새 잣대 — 를 슬쩍 추가합니다. 골대를 옮겨 놓고 골인을 선언한 셈입니다. 철학자 콰인의 분류를 빌리면 이런 부류는 허위 역설(falsidical paradox), 즉 결론도 틀렸고 증명에도 오류가 있지만 그 오류가 교묘해서 곱씹을 가치가 있는 역설에 해당합니다.
이 진단이 맞는지는 사고 실험 두 개로 확인할 수 있습니다.
실험 1 — 정의를 못박으면 역설은 증발합니다. ‘흥미롭다 = 우리 잣대 11개 중 하나라도 통과’라고 박아 두면 어떻게 될까요? 그러면 “가장 작은 지루한 수"는… 그냥 18입니다. 모순도 폭발도 없습니다. 18이 이 글에서 유명해진 것은 잣대 목록 밖의 일이고, 목록 기준 0점이라는 사실은 조금도 변하지 않으니까요. 고정된 정의는 자기 자신을 삼킬 수 없고, 자기 자신을 삼킬 수 없는 정의에서는 모순이 만들어지지 않습니다.
실험 2 — 정의가 자기 자신을 삼키게 만들면 진짜 폭탄이 됩니다. 반대로 ‘흥미롭다’를 “짧은 말로 콕 집어 묘사할 수 있다"처럼 정확하게 다듬어 봅시다. 그러면 “스무 글자로 설명할 수 없는 가장 작은 수"라는 문구를 만들 수 있는데 — 띄어쓰기를 빼고 한 글자씩 세어 보세요. 이 문구는 고작 열여섯 글자입니다. 설명할 수 없는 수를 방금 열여섯 글자로 설명해 버린 것이지요. 이것이 바로 베리의 역설이고, 거기서부터는 웃자고 하는 이야기가 아니라 괴델과 튜링까지 이어지는 수리논리학의 진짜 사건이 됩니다(그 이야기는 베리의 역설 글에서 따로 다뤘습니다). 말하자면 흥미로운 수 역설은 베리의 역설의 순한맛입니다. ‘흥미롭다’가 애매한 덕분에 모두가 웃고 넘어갈 수 있지만, 그 애매함을 걷어 내는 순간 같은 자리에서 진짜 폭탄이 나옵니다.
그리고 3장의 OEIS 이야기가 마지막 교훈 하나를 보탭니다. ‘흥미로움’을 객관적인 지표로 바꿔치기해도 — 이를테면 “등장하는 수열의 개수"로 — 그 지표가 사람들의 관심으로 만들어지는 한 문제는 사라지지 않습니다. 재는 행위가 잰 값을 바꾸니까요. 11630은 “어디에도 안 나오는 수"로 지목되는 순간 여기저기 나오는 수가 되었습니다. 흥미로움은, 측정하면 변하는 양입니다.
6. 대단원의 막: 그래도, 모든 수는 흥미롭습니다
정리는 무너졌습니다. “모든 수는 흥미롭다"는 수학의 정리가 아니라 교묘하게 포장된 농담이었고, 범인은 ‘흥미롭다’라는 움직이는 골대였습니다. 그런데 이 농담이 팔십 년 가까이 사랑받아 온 데에는 이유가 있다고 생각합니다. 결론이 — 정리로서는 가짜인데 — 태도로서는 진짜이기 때문입니다.
라마누잔이 서른두 살에 세상을 떠난 이듬해, 하디는 추도문에 이런 문장을 남겼습니다.
모든 양의 정수는 그의 개인적인 친구였다 — 라고 말한 것은, 내 기억이 맞는다면, 리틀우드 씨였다.
이 명언에는 귀여운 후일담이 있습니다. 리틀우드의 회고에 따르면 하디의 초고에는 “누군가 말했듯이"라고만 적혀 있었답니다. 교정쇄를 읽던 리틀우드는 “누가 한 말이지? 내가 했더라면 좋았을걸” 하고 생각했는데, 다음 교정쇄에서는 그 말의 주인이 리틀우드로 바뀌어 있었다고 합니다. 명언의 소유권마저 ‘가장 작은 지루한 수’의 왕관처럼 슬그머니 자리를 옮겨 다닌 셈입니다.
사족을 하나만 보태겠습니다. 하디가 시시하다고 했던 그 수는, 백 년이 지난 지금 픽션 속 이스터에그로 살고 있습니다. 작가진에 수학 박사까지 둔 애니메이션 〈퓨처라마〉는 로봇 벤더를 ‘1729번째 아들’로 부르고, 우주선 님버스호에 선체번호 BP-1729를 달고, 평행우주 1729호를 등장시켰습니다. 수학 저술가 사이먼 싱이 2013년 BBC에 “왜 1729가 퓨처라마 곳곳에 숨어 있는가"라는 글을 따로 썼을 정도입니다. 극장판에는 한술 더 떠 번호가 87539319인 택시가 지나가는데 — 두 세제곱수의 합으로 ‘세 가지’ 방법으로 쓸 수 있는 가장 작은 수입니다. 라마누잔의 택시는 픽션 속에서 아직도 달리는 중입니다.
“모든 수는 흥미롭다"를 증명할 수는 없습니다. 하지만 “지루해 보이는 수는 아직 변호사를 만나지 못했을 뿐"이라는 쪽에 거는 것은, 적어도 지금까지의 전적을 보면 꽤 안전한 베팅입니다. 39가 그랬고, 11630이 그랬고, 우리의 18이 그랬으니까요.
다음에 택시를 타시면 번호판을 한번 봐 주세요. 처음에는 분명 심심한 수로 보일 겁니다. 하디에게도 그랬으니까요. 하지만 “넌 뭐가 특별하니"라고 묻기 시작하는 순간, 이 역설이 우리에게 허락한 단 하나의 확실한 결론이 작동하기 시작합니다.
지루한 수는, 우리가 들여다보는 동안에는, 존재할 수 없습니다.