프로젝트 기록

PROJECT RECORD

연속 소수 100개로 10×10 마방진을 만들었다

EN 한국어

가로와 세로뿐 아니라 모서리를 잇는 두 대각선까지, 어느 줄을 더해도 같은 값이 나오도록 수를 배치한 것이 마방진이다. 이번에는 쓸 수 있는 수부터 정해져 있었다. 소수는 1보다 큰 자연수 중 1과 자기 자신으로만 나누어지는 수다. 그중 797부터 1493까지 이어지는 100개를 골라, 하나도 빼거나 중복하지 않고 10×10 칸에 넣는 문제였다.

나는 모든 소수 묶음에 통하는 방법보다 이 100개로 정확한 배열 하나를 만드는 데 범위를 정했다. 행과 열만 맞는 배열에서 멈추지 않도록 두 대각선을 모두 조건에 넣고, 답을 찾는 코드와 다른 구현으로 소수 목록과 합을 확인하도록 했다. 그 조건으로 얻은 배열에서는 가로 10줄, 세로 10줄, 대각선 2줄의 합이 모두 11,426이다.

문제와 수치, 출처, 검증 조건은 내가 정했으며, 수학적 모델링과 알고리즘 선택, C++·Python 코드 작성 및 실행에는 GPT-6 Pro의 실질적인 도움을 받았다. 탐색만으로는 몇 줄의 작은 오차가 남았고, 마지막에 정확한 교환과 열 재배치를 거쳐 배열이 완성됐다.

배열 하나가 답이 되는 이유

소수를 작은 것부터 세어 첫 번째를 2라고 하면, 이번에 쓴 수는 139번째 소수부터 238번째 소수까지다. 이 100개의 합은 114,260이다. 같은 합을 가진 행이 10개여야 하므로 한 줄의 목표 합은 114,260을 10으로 나눈 11,426으로 정해진다. 다만 총합이 맞는다고 그런 배치가 저절로 생기는 것은 아니다.

다음이 최종 배열이다. 표의 열 번호는 위치를 읽기 위한 표시이며, 마방진에 들어가는 수는 그 아래의 100개다.

1열2열3열4열5열6열7열8열9열10열
117192982112799471277797122314891493
82710131217142913191033130312839831019
1163128981112499771297123710938591451
1259122914538231481105186310971361809
9711021140982910911291136714331061953
1427118711171201119314479071039997911
13011109106911531087881142385710631483
1327112910091373991123111518391439937
110314711213967941887145911811321883
8771049130711231399103191913818531487

예를 들어 왼쪽 위에서 오른쪽 아래로 내려가는 대각선은 1171, 1013, 811, 823, 1091, 1447, 1423, 839, 1321, 1487을 지나며 합이 11,426이다. 반대 대각선의 1493, 983, 1093, 863, 1291, 1193, 1153, 1009, 1471, 877도 같은 합을 만든다. 나머지 행과 열의 합까지 맞고 원소 목록에도 빠짐이나 중복이 없다면, 이 특정 문제에는 배열 자체가 존재 증명이 된다.

거의 맞는 배열에서 남은 두 단계를 채웠다

계산의 출발점은 OEIS A192087에 실린 Natalia Makarova의 기존 사례였다. 그 배열은 136번째부터 235번째까지의 소수를 써서 공통 합 11,212를 만든다. 각 소수를 소수 목록에서 세 칸 뒤의 수로 바꾸면 이번 목표인 139번째부터 238번째까지를 모두 쓰게 된다. 바뀌는 값의 차이가 제각각이어서, 원래 맞던 줄의 합은 더 이상 맞지 않는다.

그 상태에서 원소 두 개의 위치를 바꾸며 줄별 오차를 줄이는 탐색을 진행했다. 난수 시드는 20260905, 반복문의 상한은 50,000,000회였다. 기록된 마지막 체크포인트에서는 모든 행의 합이 맞았지만, 5열은 11,424로 2가 부족하고 7열은 11,428로 2가 남았다. 왼쪽 위에서 내려가는 대각선도 11,424였고, 반대 대각선은 11,426으로 맞았다. 오차가 작아졌어도 내가 요구한 답은 아니었다.

먼저 행 안에서만 수를 교환하면 이미 맞춘 행의 합은 유지할 수 있다. 2행·4행·8행에서 각각 5열과 7열의 수를 맞바꾸자, 5열의 변화량은 270 − 188 − 80 = 2가 됐다. 7열에서는 그만큼 빠지므로 모든 행과 열의 합이 11,426으로 맞춰졌다. 여기서 행과 열 번호는 모두 1부터 센다.

그러나 이 교환 뒤의 대각선 합은 11,424와 11,614였다. 행과 열만 맞는 이런 배열을 준마방진이라고 부른다. 대각선 조건이 빠진 답을 걸러내겠다는 처음의 요구가 바로 이 지점에서 필요했다.

남은 방법은 열 전체의 순서를 바꾸는 것이었다. 열을 통째로 옮기면 각 행에 든 수는 그대로이고, 이미 맞는 열의 합도 유지된다. 대각선을 지나는 수는 달라지므로, 열 순서를 하나씩 바꿔 두 대각선을 다시 계산했다. 사전식 순서로 769,734번째에 두 합이 모두 맞았으며, 그때의 열 순서는 (3, 2, 1, 9, 4, 7, 5, 10, 8, 6)이었다. 위에 실은 표가 그 결과다.

탐색 점수와 정답 판정은 어떻게 달랐나

탐색 중에는 22개 줄이 목표에서 얼마나 벗어났는지를 하나의 점수로 모았다. 한 줄의 합을 S_L이라고 하면 점수는 E = Σ((S_L − 11,426) / 2)²이다. 여기서 Σ는 가로·세로·대각선 22줄에 대해 더한다는 뜻이다. 각 줄은 홀수인 소수 10개를 더하므로 합이 짝수이며, 오차를 2로 나눈 값도 정수다. 제곱해서 더하므로 서로 반대인 오차가 상쇄되지 않고, 점수가 0일 때에만 모든 합이 맞는다.

기존 배열의 소수 순번을 옮긴 직후의 점수는 3278, 탐색 체크포인트는 3이었다. 이 3은 2만큼 어긋난 줄 세 개가 남았다는 뜻이다. C++ 탐색 코드는 가끔 점수가 나빠지는 교환도 확률적으로 허용해 가까운 배치만 맴도는 것을 피하도록 구성됐다. 그 확률에는 부동소수점 지수함수가 쓰이지만, 줄의 오차와 마지막 배열 검사는 정수로 계산한다.

원고에는 각 칸에 소수 하나, 각 소수에 자리 하나를 배정하고 22개 합을 강제하는 이진 변수 모형도 적었다. 이는 문제의 조건을 정확히 표현한 것이며, 실제로 혼합정수계획 솔버를 돌렸다는 뜻은 아니다. 실행한 경로는 위의 위치 교환 탐색과 정확한 수선이었다. 특별한 대칭이나 합이 일정한 소수 쌍을 탐색 조건으로 추가하지 않았지만, 마지막 수선은 확보한 체크포인트의 행 안에서 원소를 교환하고 열 순서를 바꾸는 범위만 살폈다. 거기서 실패했다면 ‘그 방법으로 못 찾았다’는 결론이지, 마방진이 없다는 증명이 되지는 않는다.

답을 찾는 과정과 별개로 확인할 수 있게

최종 배열이 맞는지 확인하는 데 탐색을 처음부터 반복할 필요는 없다. 공개 패키지의 data/square.csv와 data/square.json에는 같은 배열이 들어 있고, 검사기는 그 파일을 읽어 원소와 합을 계산한다. 소수 목록도 탐색 코드의 결과를 그대로 믿지 않고, 2부터 시작해 제곱근 이하의 정수로 직접 나눠 보며 처음 238개를 다시 만든다. 생성기의 에라토스테네스 체와 다른 구현이다.

Python 3.10 이상과 표준 라이브러리만 있으면 패키지 폴더에서 다음 검사를 실행할 수 있다.

python3 code/check_independent.py data/square.json
python3 code/check_independent.py data/square.csv
python3 code/test_negative.py

검사 대상에는 배열의 크기와 정수 형식, 소수 100개의 중복·누락, 22개 합이 모두 들어간다. 일부러 틀리게 만든 입력 9개도 시험했으며, 두 대각선 중 하나만 맞는 준마방진을 각각 포함해 모두 거부됐다. 원래 생성기와 검사기는 같은 AI 연구 세션에서 만들어졌고, 공개 전에는 별도 Codex 문맥에서 이 검사를 다시 실행하면서 2부터 검사할 수보다 1 작은 값까지 직접 나눠 보는 추가 검사기도 작성·실행했다. 이 추가 검사기는 생성기나 원래 검사기 코드를 가져다 쓰지 않는다.

탐색까지 재현하려면 C++17을 지원하는 g++도 필요하다. python3 reproduce.py --out ../PRIME_MAGIC_REPLAY_NEW는 새 출력 폴더에서 탐색과 수선을 수행하고, 소수 목록·중간 배열·최종 배열 등 수치 파일 8개를 보관된 파일과 바이트 단위로 비교한다. 원래 실행 환경의 기록에서는 8개가 모두 같았다. 다만 확률적 교환에 쓰인 부동소수점 계산은 컴파일러나 수학 라이브러리에 따라 탐색 경로를 바꿀 수 있다. 고정된 최종 배열의 정확성은 그 경로와 별도로 확인할 수 있다.

공개된 결과와 남아 있는 범위

배열과 구성 코드, 검사기, 실행 기록을 짧은 증명과 함께 정리해 2026년 9월 5일 Zenodo에 기술 노트 v1.0.0으로 공개했다. CSV·JSON의 전체 배열뿐 아니라 출발점으로 쓴 기존 배열의 출처도 패키지에 남겼다.

이 결과는 TheoremDB P11344가 묻는 특정 소수 묶음에 대한 정확한 구성이다. OEIS A192087의 모든 적격 소수 묶음이 마방진으로 배치될 수 있는지는 별개의 문제이며, 이번 공개를 세계 최초 구성이나 신기록으로 주장하지 않는다.

TheoremDB에 보낸 자료는 9월 5일 자동 검토에서 두 차례 changes_requested 응답을 받았다. 두 응답의 사유는 출처 뒷받침 부족과 불확실성이었고, 수학적 범위와 계산 자료 항목의 확인이 전체 채택으로 이어진 것은 아니었다. Zenodo 공개와 이 특정 배열의 정확한 확인은 남아 있지만, 그 두 검토 응답을 외부 수학 인증으로 볼 수는 없다.

연구 목록 · 작업 목록

Research index · Work index

Woong Works에서 더 알아보기

지금 구독하여 계속 읽고 전체 아카이브에 액세스하세요.

계속 읽기