정말 재미있는 당신을 위한 구수략
먼저 기초가 되는 6 × 9 = 54에 대한 검산부터 시작한다.
(6, 4)
합이 10이 되게 한다.
(5, 4)
연산 결과를 적는다.
(9, 1)
이게 기본 구조다.
대각선 결과를 곱해서 같은지 검산한다.
여기까지는 실제 값 6 x 9를 그대로 써서 격자를 만든 것이다. 이제 여기서 한 단계 더 나가면, 이 구조는 굳이 원래 숫자를 전부 쓰지 않아도 유지된다는 점을 이용할 수 있다.
어떤 수든 9로 나눈 나머지만 남겨도, 9를 기준으로 했을 때의 위치는 그대로 유지된다. 예를 들어 15, 24, 33 같은 수는 전부 9로 나누면 6이 되고, 이들은 모두 같은 자리로 취급할 수 있다. 즉, 큰 수를 그대로 다루지 않고 1부터 9 사이의 값으로 압축해도 구조가 깨지지 않는다.
그래서 곱셈 A x B를 검증할 때도 A mod 9, B mod 9만 가지고 위에서 만든 격자를 그대로 구성할 수 있다.
(6, 4)처럼 시작하면, 6에 대해 이미 만들어 둔 6 -> (5, 4) -> (9, 1) 구조를 그대로 가져와서 아래를 채운다.
이렇게 하면 실제 A와 B의 값이 무엇이든 간에, 9를 기준으로 같은 위치에 있는 값들은 동일한 격자를 만들게 된다.
결과적으로 대각선 검사도 그대로 적용된다. 실제 큰 수를 곱하지 않아도, mod 9로 줄인 값만으로 동일한 방식의 검산이 가능해지는 이유가 여기 있다.
정리하면, mod 9는 계산을 새로 하는 것이 아니라 이미 만든 격자 구조를 유지한 채 숫자만 9 기준으로 축소해서 재사용하는 과정이다.
이걸 조금 비틀어서 모듈러 연산을 해 보자. 2x3 배열로 바꿔서 쓰면 모듈러 연산이 훨씬 단순해진다.
matrix = [[6, 9], [5, 4], [9, 1]]
if abs(matrix[0][0] - matrix[2][1]) == abs(matrix[0][1] - matrix[2][0]):
print("검산 결과 정상입니다.")
이제 이 구조를 mod 9를 이용한 곱셈 검증으로 확장한다.
피연산자 A에 대해 A mod 9 = 6, B에 대해 B mod 9 = 4라고 하자.
처음에 보면 mod 9가 왜 나오는지 좀 헷갈린다.
사실 이건 앞에서 했던 6 × 9 = 54 검산을 그대로 확장한 것이다.
이미 앞에서 구조를 만들어놓았다.
(6, 9) -> (5, 4) -> (9, 1)
그리고 5 + 4 = 9로 수렴하는 과정까지 끝났다.
즉, 6이라는 숫자가 9라는 기준 안에서 (5, 4)로 풀리고 다시 9로 돌아오는 메커니즘이 이미 확정된 상태다.
mod 9 문제로 넘어가면
A mod 9 = 6
B mod 9 = 4
이건 원래 A와 B를 9로 나눈 나머지로 압축한 형태일 뿐이다. 실제 숫자 크기는 상관없다.
그래서 같은 로직을 적용한다.
(6, 4)
여기서 중요한 점은 6 x 4를 새로 계산하지 않는다는 거다.
대신 앞에서 이미 검증한 6의 구조를 그대로 가져온다.
6 x 9 = 54 -> (5, 4)
이걸 두 번째 줄로 그대로 쓴다.
(5, 4)
5 + 4 = 9 관계는 그대로 유지되니까 마지막 줄은
(9, 1)
최종 격자는 이렇게 된다.
(6, 4)
(5, 4)
(9, 1)
대각선 검사식을 넣어보면
6 - 1 == 9 - 4
5 == 5
성립한다.
이 과정에서 A와 B의 실제 값은 전혀 쓰지 않았다. 오직 A mod 9와 B mod 9만으로 검증이 끝났다.
이게 되는 이유는 처음에 6 × 9 = 54 검산으로 “6이 9 기준에서 어떻게 분해되고 다시 합쳐지는지” 구조를 미리 만들어 뒀기 때문이다. 실제 큰 수를 다루든 mod 9로 줄인 값을 다루든 결과는 같다.
결국 승제별법으로 한 번 만든 구조를 mod 9 공간에 투영해서 재사용하는 방식이다.
from typing import List, Tuple, Optional
def mod9(n: int) -> int:
r = n % 9
return 9 if r == 0 else r
def reconstruct_triples(left: int, right: int, product: Optional[int], start: int = 1, end: int = 40) -> List[Tuple[int, int, int]]:
triples = []
for a in range(start, end + 1):
if mod9(a) != left: continue
for b in range(start, end + 1):
if mod9(b) != right: continue
c = a * b
if product is not None and mod9(c) != product: continue
triples.append((a, b, c))
return triples
# 예시 실행
triples = reconstruct_triples(6, 4, 6)
print(triples[:15])
원본 식이 뭔지 몰라도 2x3 격자 하나만 그리면 검증 끝난다. 수천억 단위 계산을 하던 세금 집행인들에게도 이 방법은 꽤 실용적이었을 거다.
9 대신 2의 거듭제곱으로 모듈러를 바꾸면 비트 연산으로 처리할 수 있을 테니, 나중에 코드 짜서 벤치마크 해볼 생각이다.
어라, 수포자인데 모듈러 연산을 했다.
수식으로만 보면 복잡한데, 격자로 풀어보니 연산 흐름이 한눈에 들어온다. 무작정 mod 9 계산만 반복하는 것보다 훨씬 낫다.
오늘도 계산에 시달리는 공돌이들을 위해 이 체계를 정리한 최석정에게 감사를 전한다. 우리는 뉴턴 말대로 거인의 어깨 위에 서 있는 셈이다.