현수선 그리기
Balloon Platform Defense에서 풍선 사이에 줄을 만들면서, 두 점 사이에 늘어진 줄, 흔히 현수선이라고 부르는 모양을 그리는 방법을 알아내야 했습니다. 인터넷에서 찾은 예는 모두 두 점이 같은 높이에 있다는 식으로 문제에 제약을 두거나, 줄이 시작하는 각도처럼 여기서는 알 수 없는 정보를 안다고 가정했습니다. 알고 있는 것은 줄의 시작점과 끝점, 그리고 줄의 길이뿐입니다. 위키백과에 현수선 방정식에 관한 정보가 많이 있어서 그것을 출발점으로 삼았습니다. 현수선의 방정식은 다음과 같습니다.y=a\cosh \left (\frac{x}{a} \right )=\frac{a\left ( e^{\frac{x}{a}}+e^{-\frac{x}{a}} \right )}{2}
이 식은 줄의 가장 낮은 점이 y축과 만나는 곳에 있다고 가정합니다. 실제로는 x와 y에 각각 상수가 더해지는데, 가장 낮은 점이 어디일지 모르기 때문에 두 상수 모두 아직 정해지지 않았습니다. 이것이 이 문제가 교과서의 어떤 예보다도 복잡한 이유 가운데 하나입니다. (현실의 수학 문제는 늘 교과서 예제보다 훨씬 복잡하다는 걸 느끼신 적 있나요? 저는 해석적으로 풀 수 있는 적분이 나오는 현실 문제를 아직도 기다리고 있습니다.) 하지만 그건 나중 일입니다. 먼저 곡선이 얼마나 좁고 넓은지를 사실상 정하는 상수 a의 값을 구해야 합니다. 두 점의 x좌표가 같으면 a는 무한대가 되므로, 여기까지 오기 전에 그 경우를 처리해 두어야 합니다. 두 점이 아주 가까워 a가 계산할 수 없을 만큼 커지는 경우도 마찬가지입니다. (여기서 '계산할 수 없을 만큼 크다'는 것은, 지수 함수의 인수로 쓰면 사용하는 부동 소수점 정밀도에서 NaN이나 무한대가 나오는 수를 말합니다.) 물론 두 점 사이의 거리가 줄의 길이보다 길 때도 코드가 여기까지 오지 않게 하세요. 결과가 엉망이 됩니다.
배율 구하기
a를 구하기 위해 위키백과는 다음 방정식을 제시합니다(cosh는 곡선의 위치를, sinh는 곡선의 길이를 준다는 현수선의 성질에 바탕을 둔 식입니다).\sqrt{s^{2}-v^{2}}=2a\sinh \left ( \frac{h}{2a} \right )
여기서 s는 줄의 길이이고, h와 v는 시작점과 끝점 사이의 가로와 세로 거리(절댓값)입니다. 모두 알고 있는 값이므로 미지수는 하나만 남지만, 이것은 수치적으로 풀어야 합니다. 처음에는 초깃값을 그다지 신경 쓰지 않고 시도했더니 수렴할지 말지가 운에 달려 있었습니다. 다행히 좋은 초깃값은 쉽게 찾을 수 있습니다. 쌍곡사인의 테일러 급수는 다음과 같습니다.
\sinh x=x+\frac{x^{3}}{3!}+\frac{x^{5}}{5!}+\cdots
u = \frac{1}{4a^{2}}, c = \sqrt{s^{2}-v^{2}}로 치환하면 풀어야 할 식은 다음과 같이 됩니다.
c=\frac{1}{\sqrt{u}}\sinh \left ( h\sqrt{u} \right )
sinh의 테일러 급수에서 처음 세 항을 취해 정리하면 다음과 같습니다.
c=\frac{1}{\sqrt{u}}\left (h\sqrt{u}+\frac{\left ( h\sqrt{u} \right )^{3}}{3!} +\frac{\left ( h\sqrt{u} \right )^{5}}{5!} \right )
c=\frac{1}{\sqrt{u}}\left (h\sqrt{u}+\frac{h^{3}u\sqrt{u}}{3!} +\frac{h^{5}u^{2}\sqrt{u}}{5!} \right )
c=h+\frac{h^{3}u}{3!} +\frac{h^{5}u^{2}}{5!}
이를 정리하면:
\frac{h^{5}}{120}u^{2}+\frac{h^{3}}{6}u+\left ( h-c \right )=0
이것은 u에 관한 간단한 이차 방정식이므로, 계수를 학교에서 배운 근의 공식 x = \frac{-b\pm \sqrt{b^{2}-4ac}}{2a}
에 대입하면 u의 좋은 초깃값을, 따라서 a의 좋은 초깃값을 얻을 수 있습니다.
u = \frac{-\frac{1}{6}h^{3}+ \sqrt{\frac{1}{36}h^{6}-\frac{1}{30}h^{5}\left ( h-c \right )}}{\frac{1}{60}h^{5}}, 여기서 a=\frac{1}{2\sqrt{u}}
이 초깃값이면 뉴턴의 방법으로 해를 구하기에 충분히 가깝습니다. f\left ( a \right )=0
의 꼴로 쓰면 f\left ( a \right )=2a \sinh \left ( \frac{h}{2a} \right )-c
이고 {f}'\left ( a \right )=2 \sinh \left ( \frac{h}{2a} \right )-\frac{h}{a}\cosh \left ( \frac{h}{2a} \right )
입니다.
a_{n+1}=a_{n}-\frac{f\left ( a_{n} \right )}{{f}'\left ( a_{n} \right )}
이므로,
a_{n+1}=a_{n}-\frac{2a \sinh \left ( \frac{h}{2a} \right )-c}{2 \sinh \left ( \frac{h}{2a} \right )-\frac{h}{a}\cosh \left ( \frac{h}{2a} \right )}=a_{n}-\frac{a \sinh \left ( \frac{h}{2a} \right )-0.5c}{ \sinh \left ( \frac{h}{2a} \right )-\frac{h}{2a}\cosh \left ( \frac{h}{2a} \right )}
지금은 수열이 이전 값과 0.001 이내로 수렴했는지를 확인하는데, 보통 2~4번의 반복이면 되지만 x좌표가 가까울 때는 열 번이 넘게 걸리기도 합니다. 계산의 대부분은 쌍곡선 함수를 구하는 데 들고, 함수를 더 미분해도 그 배수가 늘어날 뿐이므로, 고차 Householder 방법도 계산량을 크게 늘리지 않고 쓸 수 있을 것입니다. 이제 배율을 알았으니 평행 이동을 구할 수 있습니다.
평행 이동 구하기
표준 방정식y=a\cosh \left (\frac{x}{a} \right )
은 현수선이 y축에 대해 대칭이고 가장 낮은 점이 (0,a)에 있다고 가정하지만, 우리가 그리는 곡선은 화면의 어디에나 있을 수 있습니다. 따라서 올바른 위치로 옮기는 평행 이동 (p,q)를 구해야 하고, 방정식은 다음과 같이 됩니다.
y-q=a\cosh \left (\frac{x-p}{a} \right )
(다시 말해 이 시점에서 a는 알고 있고 x와 y는 변수이므로, 모르는 값은 p와 q뿐입니다.) 처음에는 이 식에 x와 y의 알려진 값, 즉 줄의 두 끝점을 대입하려고 했습니다. 이론적으로는 되지만, 아무리 식을 간단히 해도 계산 도중에 배정밀도 부동소수점수로는 다룰 수 없는 값이 자주 나왔습니다. 결국 다른 방법을 찾아야 했는데, 오히려 그쪽이 더 효율적이었습니다.
현수선 방정식에서 cosh를 sinh로 바꾸면 곡선의 길이가 나온다는 것을 떠올려 보세요. 이 꼴에 왼쪽과 오른쪽 끝점의 x좌표와, 그리려는 줄의 알려진 길이(s)를 대입하면 다음 방정식을 얻습니다.
a\sinh \left (\frac{x_{right}-p}{a} \right )-a\sinh \left (\frac{x_{left}-p}{a} \right )=s
쌍곡선 함수의 항등식 가운데 하나는 다음과 같습니다.
\sinh x - \sinh y=2\cosh\left ( \frac{x+y}{2} \right ) \sinh \left ( \frac{x-y}{2} \right )
따라서:
\sinh \left (\frac{x_{right}-p}{a} \right )-\sinh \left (\frac{x_{left}-p}{a} \right )=2\cosh\left ( \frac{x_{right}+x_{left}-2p}{2a} \right ) \sinh \left ( \frac{x_{right}-x_{left}}{2a} \right )
cosh의 인수에서는 오른쪽과 왼쪽 x좌표를 더해 2로 나누므로 가운데 점의 좌표가 되니, 그것으로 바꾸어도 됩니다. sinh의 인수에 있는 뺄셈은 앞에서 h라고 부른 값과 같으므로 이 sinh도 알려진 양으로 계산할 수 있고, 처음 방정식은 다음과 같이 정리됩니다.
\cosh\left ( \frac{x_{middle}-p}{a} \right ) = \frac{s}{2a\sinh \left ( \frac{h}{2a}\right )}
이제 역쌍곡선 코사인(arcosh)으로 p를 구할 수 있지만 조심해야 합니다. cosh는 짝함수이므로 늘 양의 답이 나오지만 음의 답도 있어서, p의 값은 두 가지가 될 수 있습니다.
p =x_{middle}\pm a\cosh^{-1}\left ( \frac{s}{2a\sinh \left ( \frac{h}{2a}\right )} \right )
어느 값을 쓸지는 끝점의 y좌표를 보면 알 수 있습니다. 실 한 가닥으로 실험해 보면 알 수 있듯이, 줄의 길이와 x좌표가 같을 때 오른쪽 y좌표가 왼쪽보다 낮으면 곡선의 가장 낮은 점은(그것이 두 x좌표 사이에 있다면) 가운데보다 오른쪽에, 즉 양의 arcosh에서 나온 쪽에 있고, 그 반대의 경우는 반대입니다.
또 하나 생길 수 있는 문제는, 두 y좌표가 같을 때 곡선의 가장 낮은 점이 정확히 가운데에 있어야 하므로 cosh의 인수는 0이고, arcosh를 취하는 값은 1이어야 한다는 점입니다. 그런데 부동 소수점 오차 때문에 1보다 조금 작게 나오는 일이 많아, arcosh를 구할 때 오류가 납니다. 이 계산을 시작하기 전에 두 y좌표가 같은지(또는 거의 같은지) 확인해 두는 것이 현명합니다. 계산도 줄이고 이 오류도 막을 수 있습니다. 그 경우에는 p가 두 끝점의 정확히 가운데에 있다고 보면 됩니다.
a와 p를 알았으니, 끝점 하나의 x와 y 값을 방정식
y-q=a\cosh \left (\frac{x-p}{a} \right )
에 대입하고 다음과 같이 정리하기만 하면 q를 구할 수 있습니다.
q=y-a\cosh \left (\frac{x-p}{a} \right )
이것으로 q를 구하면 곡선을 그릴 최종 방정식을 얻습니다.
y=a\cosh \left (\frac{x-p}{a} \right )+q
이제 곡선을 그리는 것은 간단합니다. 매개변수를 구하는 대부분의 단계에서는 오차가 어떻게 전파될지 가늠하기 어려웠기 때문에 쌍곡선 함수를 최대한 정확하게 계산했습니다. 하지만 계산의 대부분을 차지하는 이 마지막 단계에서는 쓰는 인수의 크기와 견딜 수 있는 오차를 알고 있으므로, 어느 차수까지의 테일러 급수면 충분히 정확할지 예측할 수 있습니다. 제 경험으로는 대개 쓸 만한 가치가 있습니다.