파이썬 연습문제 – 누워있는 드럼통에 들어있는 기름의 부피

반지름이 R cm이고 높이가 H cm 인 드럼통에 기름이 들어있다. 이 드럼통 내의 기름의 높이가 A라 할 때 기름의 부피를 구하는 것은 원기둥의 부피를 구하는 것이므로 그리 어렵지 않다. 그런데 드럼통이 세워져 있지 않고 수평으로 누워서 설치되었을 때, 기름의 높이가 B 이면, 이 때 드럼 통 내의 부피를 계산해보자. 간단한(?) 적분 문제인데, 여기서는 구분구적법을 적용해서 근사치를 구해볼 것이다.

드럼통 내의 기름은 통이 수평으로 누워있거나 수직으로 누워있는 경우에는 단면이 일정한 기둥의 모양을 갖추게 된다.  만약 통이 세워져 있다면 단면은 원이 되어 비교적 간단하게 계산이 가능하다. 통이 누워있는 경우에는 기름의 표면을 기준으로 잘려진 원이 단면이된다. 이 일부가 잘려진 원의 넓이만 구하면 된다.

누워있는 기름통의 단면을 세로축을 기준으로 좌우가 대칭이고, 그 한쪽 절반을 보면 반원의 아래쪽 부분이 기름의 단면이 된다. 

위 그림에서와 같이 노란 부분의 넓이를 구하는 문제가 된다. 반지름의 크기가 r인 반원의 그래프는 y = \sqrt{r^{2} - x^{2}}이 되고, 그 아래의 넓이를 구하려면 이를 적분하면 된다. 원의 그래프를 이용해서 원의 넓이 공식을 증명하는 과정에서 이 식의 적분은 고등학교 수학 범위에서 등장한 적이 있지만, 그 문제는 전체 범위만을 고려하기 때문에 치환 적분을 적용할 수 있었지만, 이 문제는 그렇지 못하다.

하지만 걱정할 필요는 없다. 그래도 구분구적법을 통해서 넓이를 구할 수 있다. 예를 들어 원의 반지름이 1이고 바닥으로부터 1.4 까지 기름이 차있는 상태임을 가정하자.

위 그림과 같이 이 반원의 아래쪽에 대해서 너비가 0.2인 직사각형을 그 높이가 각각 왼쪽 모서리가 반원에 닿는 높이까지 이어보자. 약간의 공백이나 삐져나가는 부분이 있어서 정확하지는 않지만 어느정도 해당 영역의 넓이에 근사하는 것을 볼 수 있다. 만약 이 막대들의 폭을 점점 좁혀나가면 어떨까?

이와 같이 폭이 작아지면 작아질수록 그 오차가 점점 줄어서 본래의 반원에 근접하게 된다. 사실 고등학교에서 배우는 적분을 통한 그래프의 밑넓이도 이와 같은 방법을 통해서 막대의 폭이 0에 수렴할 때의 전체 막대넓이의 총 합을 구하는 방법으로 구하게 된다.

어차피 원기둥의 부피역시 십진법으로 표시하면 근사치를 사용해야 하므로 여기서도 이렇게 구분 구적법을 사용하여 특정 구간의 넓이를 구해보자.

편의상 반지름의 크기가 1이라고 하고, x의 구간은 0~2가 되므로 그래프의 식은 y = \sqrt{1 - (x - 1)^2}이 된다고 한다. 여기에 두 지점 a, b 사이의 밑넓이를 구하자면, a와 b 사이를 충분히 작은 간격으로 쪼개어 배열을 만들고, 배열의 각 원소를 그래프의 식에 넣은 값을 높이, 배열의 간격값을 너비로 하여 두 값을 곱하고 그 합계를 구한다.

즉 어떤 배열 A와 그 배열에 그래프 식을 맵핑한 B 그리고 A, B의 각 원소끼리를 곱해서 만든 리스트의 총합을 구한다. 파이썬 리스트로도 이 작업을 할 수 있지만, Numpy를 사용하면 보다 편리하게 구할 수 있다. 1

import numpy as np

def get_area(a=0, b=2, dw=0.001):
  xs = np.arange(a, b, dw)
  fx = lambda x: abs(1 - (1 - x)**2) ** 0.5
  ys = fx(xs) * dw
  return ys.sum()

  1. Numpy의 arange 같은 함수는 정수가 아닌 실수 범위와 실수 간격을 통한 배열을 만들 수 있다. 또한 Numpy의 배열은 파이썬의 리스트나 array 가 아닌 독립적인 자료 구조인데, 대부분의 연산이 element-wise하게 적용되는 일종의 벡터에 가까운 고속 연산 타입이다.