- 1969년 고등학생 Jim Storer가 만든 첫 Lunar Landing 게임에서 최적 착륙 연료 스케줄을 검증하던 중, 실제로는 착륙해야 할 순간을 게임이 비행 중으로 잘못 판정하는 버그가 드러남
- 문제의 전략은 70초간 엔진을 끄고, 10초 동안 164.31426784 lbs/sec, 이후 최대 200 lbs/sec로 태우는 suicide burn이며, 게임은 거친 착륙과 미착륙 상승 사이의 부드러운 착륙을 놓침
- 원래 코드는 단순 Euler 적분이 아니라 Tsiolkovsky 로켓 방정식과 Taylor 급수를 사용해 10초 턴의 운동을 계산했으며, 1969년 PDP-8 환경의 고등학생 작업으로는 상당히 정교함
- 버그의 원인은 지면 접촉 전 궤적의 최저점을 근사하는 식에서 제곱근 내부 분모의 2로 나누기가 빠진 것으로, 최저점 도달 시간이 일관되게 과소추정됨
- 빠진 factor of two를 고치고 0.05초 보정을 제거하면 suicide burn 결과가 1.66 MPH까지 개선되지만, 1 MPH 미만의 완벽한 착륙에는 Taylor 급수 2항 근사와 착륙 시점 재계산 한계가 남아 있음
1969년 Lunar Landing과 최적 착륙 탐색
- Jim Storer는 Neil Armstrong의 달 착륙 몇 달 뒤, Massachusetts의 Lexington High School 학생으로 첫 Lunar Landing 게임을 작성함
- 1973년에는 이 게임이 “by far and away the single most popular computer game”으로 불릴 정도로 널리 퍼짐
- 게임은 텍스트 기반이며, 달 착륙선의 모든 운동은 수직 방향으로만 진행됨
- 플레이어는 시뮬레이션상 매 10초마다 태울 연료량을 정하고, 달 표면에 최대한 부드럽게 착륙해야 함
- 최적 연료 스케줄을 찾는 과정에서 이론적으로 가장 좋은 전략이 게임 안에서는 제대로 동작하지 않았음
- 실제로는 착륙선이 표면에 닿는 상황임
- 게임은 표면에 닿지 않았다고 잘못 판정함
- 최종 원인은 거의 55년 동안 눈에 띄지 않은 누락된 divide by two였음
최소 연료 착륙과 suicide burn
- 최소 연료로 착륙하려면 가능한 한 짧은 시간 안에 내려와야 함
- 최적 전략은 처음에는 엔진을 꺼 속도를 키우고, 마지막 가능한 순간에 최대 추력으로 감속해 표면에 닿는 순간 속도를 0에 가깝게 만드는 방식임
- Kerbal Space Program 커뮤니티는 이런 전략을 suicide burn이라고 부름
- 타이밍이 매우 빡빡하고 오류 여지가 거의 없기 때문임
- 시행착오와 수동 이진 탐색으로 찾은 스케줄은 다음과 같음
- 70초 동안 연료를 태우지 않음
- 다음 10초 동안 164.31426784 lbs/sec로 연료를 태움
- 이후 최대치인 200 lbs/sec로 연료를 태움
- 게임은 1 MPH 미만을 완벽한 착륙으로 간주함
- 이 스케줄에서는 3.5 MPH 이상으로 착륙해 “could be better” 판정을 받음
- 그런데 연료량을 0.00000001 lbs/sec만 더 태우면 표면에 닿지 않고 114 MPH로 상승함
- 즉 거친 착륙과 미착륙 상승 사이에 있어야 할 부드러운 착륙 판정이 사라져 있었음
예상보다 정교했던 물리 계산
- 처음에는 오늘날 게임에서도 흔한 Euler 적분을 예상함
- 시간 구간 시작점의 힘을 계산함
- F=ma로 가속도를 구함
- 해당 시간 구간 동안 가속도가 일정하다고 가정함
- 실제 Lunar Landing 코드는 이보다 더 정교했음
- Jim Storer는 Tsiolkovsky rocket equation의 정확한 해를 사용함
- 로그 계산에는 Taylor 급수 전개를 적용함
- 인자의 최대값은 0.1212임
- 5개 항으로 6자리 이상 정확도를 냄
- 대수적 단순화로 반올림 오차도 줄임
- Jim Storer는 당시 미적분과 Taylor 급수 같은 개념에 익숙했고, 물리학자였던 아버지가 방정식 유도에 도움을 준 것으로 기억함
- suicide burn이 최적이 되는 이유도 이 로켓 방정식에서 나오며, 이 부분은 버그의 원인이 아니었음
지면 접촉 판정이 까다로운 이유
- 로켓 방정식은 착륙선이 지면에 닿기 전까지는 잘 동작함
- 고체 물체 간 충돌은 동역학 엔진에서 어려운 영역이며, Lunar Landing도 지면 접촉 판정에서 가장 큰 난관을 만남
- 10초 턴의 시작과 끝만 확인해서는 충분하지 않음
- 시작 시점에는 하강 중일 수 있음
- 끝 시점에는 상승 중일 수 있음
- 중간에 표면 아래로 내려갔다가 다시 올라왔을 수 있음
- 이 경우 프로그램은 시간을 되감아 더 이른 접촉 시점을 찾아야 함
- 자연스러운 검사 지점은 속도가 0이 되는 궤적의 최저점임
- 로켓 방정식에서 이 최저점을 기본 수학 함수만으로 닫힌 형태로 표현할 수는 없음
- 각주에서는 Lambert W가 필요하다고 설명함
- 로그의 Taylor 급수 앞 몇 항만 쓰면 근사할 수 있음
- 처음 두 항만 사용하면 문제가 2차 방정식으로 단순화됨
- 고등학교 수준의 quadratic formula를 사용할 수 있음
- 10초 턴 범위에서는 약 0.1% 이내 정확도를 기대할 수 있음
대체 2차 공식과 수치 안정성
- Jim Storer의 코드에는 제곱근이 분자가 아니라 분모에 있는 형태가 등장함
- 이는 일반적인 2차 공식이 아니라, 제곱근이 아래에 오는 대체 형태의 quadratic formula와 일치함
- 이 대체 형태에는 중요한 수치적 장점이 있음
- 지면 접촉을 감지한 뒤 실제 접촉 시간을 찾을 때도 Taylor 급수를 잘라 2차 방정식으로 근사함
- 일반 형태는 2차항 계수가 0일 때 0으로 나누는 문제가 생김
- 로켓 추력이 중력과 정확히 균형을 이루면 이런 상황이 발생함
- 표면 근처에서 떠 있거나 천천히 내려오는 플레이어에게 흔할 수 있음
- 추력이 중력에 가까우면 일반 형태는 분자에서 catastrophic cancellation이 생기고, 작은 분모가 오차를 키움
- 대체 형태는 2차항이 0인 선형 방정식 상황에서도 잘 동작함
- 1969년 고등학생이 이런 형태를 재유도했거나 배웠다는 점은 당시 환경을 고려하면 인상적임
실제 버그: 빠진 factor of two
- 식을 직접 유도해 비교하자 Jim Storer의 코드와 거의 같았지만, 제곱근 내부 분모에 있어야 할 2가 빠져 있었음
- 이 누락은 식을 유도하는 과정이나 컴퓨터에 입력하는 과정에서 생긴 단순 오류일 가능성이 있음
- 당시 MACSYMA는 막 시작된 지 1년밖에 되지 않았고, 고등학교에서 사용할 수 있는 환경도 아니어서 유도는 종이와 연필로 해야 했음
- 이 버그 때문에 최저점까지의 시간이 일관되게 과소추정됨
- 코드는 두 가지 방식으로 이를 보정함
- 0.05초를 추가함
- 새롭고 더 가까운 위치에서 다시 추정함
- 하지만 특정 suicide burn 상황에서는 이 보정 때문에 착륙 시점을 놓침
- 첫 추정은 착륙선이 표면 위에서 아직 하강 중인 시점임
- 두 번째 추정은 최저점을 지나 상승 중인 시점임
- 두 시점 사이가 0.05초보다 짧아질 수 있음
수정 후 결과와 남는 한계
- 누락된 factor of two를 추가하고 0.05초 보정을 제거하면 suicide burn 결과가 개선됨
- 수정 후 최선의 suicide burn은 1.66 MPH 착륙 속도를 보임
- 1 MPH 미만의 완벽한 착륙까지는 약 3/4 지점까지 가까워짐
- 완벽하지 않은 이유는 여전히 Taylor 급수의 처음 두 항만 사용하기 때문임
- 최저점이 표면 아래라고 판단한 뒤에는, 처음 표면에 닿는 시간을 다시 찾아야 함
- 이 과정도 유사한 근사를 사용함
- 추가 반복이 도움이 될 수 있음
- 버그를 고친 상태에서는 시간을 과대추정하므로, 시간을 되돌아가야 할 수 있음
- 이 경우 2차 방정식의 다른 해를 선택해야 할 수도 있음
- 더 단순하게는 Taylor 급수 한 항만 써서 Newton’s method와 비슷한 방식으로 처리할 수 있음
- 속도 크기가 특정 임계값 아래로 내려가면 멈추고, 그때의 고도로 착륙 여부를 판단하는 방법도 가능함
- 다만 이런 변경은 코드를 더 복잡하게 만들며, 원래 게임은 이미 충분히 재미있게 플레이 가능했음
버그가 오래 남을 수 있었던 이유
- 부드러운 착륙 자체는 가능함
- 14번째 턴을 낮은 고도와 낮은 속도로 끝냄
- 15번째 턴에서 낮은 추력을 사용함
- 150초 이후 어딘가에서 착륙함
- 문제가 되는 것은 약 148초에 끝나는 이론적 최대 추력 suicide burn임
- 전체적으로 이 코드는 1969년 PDP-8에서 18세 고등학생이 작성한 작업으로 매우 인상적임
- 당시에는 고등학교에서 컴퓨터 과학을 가르치기 전이었고, Newton 방법으로 추정을 반복 개선하거나 catastrophic cancellation을 걱정하는 수치 계산 개념도 널리 알려져 있지 않았음
- 버그가 거의 55년간 눈에 띄지 않은 이유는, 버그가 있어도 게임이 어렵고 재미있으며 부드러운 착륙도 가능했기 때문임
- 단순히 이기는 것을 넘어 최적 전략을 찾으려는 시도가 작은 불일치를 이해하는 과정으로 이어짐