《한서》 이야기를 합니다.

《구장산술》, 유클리드 호제법, 파이선

《구장산술》, 유클리드 호제법, 파이선

주아
주아 《한서》 파는 사람

이 포스트는 2018년에 작성했던 글을 수정하고 보충한 것입니다.


가 종종 하는 말은 《한서》에는 의 학위논문만 빼고 모든 것이 들어있다는 것입니다. 그렇게 《한서》에서 온갖 것을 찾아대던 시절이었습니다. 갑자기 한나라에 프로그래밍에 상응하는 것이 있었는지가 궁금해졌습니다. 그러다가 《구장산술》이 떠올랐습니다.

《구장산술》이란 어떤 책일까요? 《구장산술》은 제목 그대로 9장으로 이루어진 산술에 관한 책입니다.

유클리드의 기하원본에 견줄 정도로 막강한 고전이 동양산학에도 있다. 이름하여 ‘구장산술’이 그것이다.

동양산학 최고의 고전인 구장산술은 중국 산학의 효시로 그후의 발달을 결정지었다고 해도 과언이 아니다. 뿐만 아니라 우리나라, 일본, 베트남은 물론 심지어 중국과 쌍벽을 이루는 수학을 발달시킨 인도에도 커다란 영향을 미쳤다.

또 놀랍게도 동양 고대수학서인 구장산술의 내용이 중·고교 교육과정과 상당히 일치할 뿐 아니라 그것을 능가하기까지 한다.

차종천. 〈우리민족 산학의 뿌리 ‘구장산술’: 고전을 읽으면 수학해법이 보인다〉. 《과학동아》 2010년 01월.

이 《구장산술》은 바로 한나라 때 편찬된 것으로 추정됩니다.

『구장산술』은 전한시대(前漢時代, 서기전 206∼서기 8)에 편찬된 것으로 추정되며, 유휘(劉徽)가 263년,『구장산술』에 주를 달면서 쓴 서문을 통해 장창(張蒼)과 경수창(耿壽昌) 등에 의하여 완성된 것을 알 수 있다.

구장산술〉, 《한국민족문화대백과사전》.

《구장산술》을 완성한 인물로 꼽히는 장창과 경수창의 이름에 주목해 봅시다. 사형 집행 직전 드러난 맨몸이 뽀얗고 토실해서 풀려난 장창은 진·한 교체기 사람이고, 150여 년 뒤 소망지에게 비난받은 경수창은 전한시기 후반 사람입니다. 다시 말해 《구장산술》의 내용이 전한시대 전반에 걸쳐 꾸준히 쌓여 왔다고 볼 수 있습니다. 《한서》의 세계에서 중요한 책이라고 할 수 있겠습니다.

그런데 이 책과 프로그래밍이 무슨 상관이 있을까요? 이 물음에 답하기 위해, 《구장산술》 1권 〈방전〉에 나오는 약분술을 읽어봅시다.

최대공약수를 구할 때 흔히 사용되는 방법이 유클리드 기하원본에 소개된 호제법이다. 놀랍게도 구장산술 첫장에 나오는 단순한 분수 계산의 풀이법에 이런 유클리드 호제법과 동일한 방법이 소개돼 있다. [중략]

“반으로 나눌 수 있는 것은 반으로 나누면 되고, 반으로 나눠지지 않는 것은 분모·분자의 수를 다른 곳에 놓고, 큰 것에서 작은 것을 빼고 나서, 다시 서로서로 빼주는 일을 되풀이해 그것들의 등수(等數)를 구해 등수로 그것들을 나눈다.” [可半者半之。不可半者,副置分母,子之數,以少減多,更相減損,求其等也。以等數約之。]

차종천. 〈구장산술에도 유클리드 호제법 나온다〉. 《과학동아》 2010년 02월.

이 알고리듬에서는 조건문과 재귀함수를 찾을 수 있습니다. 반으로 나눌 수 있는 경우[可半者]와 그렇지 않은 경우[不可半者]에 각기 다른 계산을 하는 것은 조건문으로 구현할 수 있습니다. 다시 서로서로 빼 주는 일을 되풀이하는[更相減損] 계산은 함수를 재귀적으로 호출하는 것으로 구현할 수 있습니다. 그렇다면 이것은 프로그래밍 언어로 작성할 수 있습니다.

그래서 에게 익숙한 언어인 파이선에서 약분 함수를 정의했습니다. 구장산술 원문의 표현에 맞게 변수명을 짓고, 어순에 맞게 코드를 배치했습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def 約分(, ):
  ,  = max(, ), min(, )

  # 可半者: 반으로 나눌 수 있는 것은
  if  ==  * 2:
    # 半之: 반으로 나누면 되고
    return 2, 1 

  # 不可半者: 반으로 나눠지지 않는 것은
  def 相減(, ):
    # 副置分母子之數: 분모와 분자의 수를 다른 곳에 놓고  
    # 以少減多: 큰 것에서 작은 것을 빼고 나서
     =  - 
    if  == 0:
      return  # 최대공약수
    elif  == 1:
      return 1 # 서로소인 경우
    else:
      # 更相減損: 다시 서로서로 빼주는 일을 되풀이해
      return 相減(max(, ), min(, ))

  # 求其等也: 그것들의 등수를 구해
  等數 = 相減(, )

  # 以等數約之: 등수로 그것들을 나눈다
  return  // 等數,  // 等數

아니면 재귀함수를 쓰지 않고 while 문을 사용할 수도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def 約分(, ):

  # 可半者: 반으로 나눌 수 있는 것은
  if  ==  * 2:
    # 半之: 반으로 나누면 되고
    return 2, 1

  # 不可半者: 반으로 나눠지지 않는 것은
  else:
    # 副置分母子之數: 분모와 분자의 수를 다른 곳에 놓고  
    ,  = max(, ), min(, )
    # 以少減多: 큰 것에서 작은 것을 빼고 나서
     =  - 
    while  > 1:
      # 更相減損: 다시 서로서로 빼주는 일을 되풀이해
      ,  = max(, ), min(, )
       =  - 
    
    # 求其等也: 그것들의 등수를 구해
    等數 = 1 if  else 
    
    # 以等數約之: 등수로 그것들을 나눈다
    return  // 等數,  // 等數

역시 《한서》의 세계에는 없는 것이 없습니다.


2018-11-14 16:23