εn=axn−1>−1임을 관찰하자. 모든 n≥1에 대해 εn은 양수이고, 점화식으로부터 εn+1≤min{2εn2,2εn}를 만족한다. 임의의 M>1에 대해, m이 충분히 클 때 εm<M1이므로, 이러한 εm에서 시작하는 바빌로니아 수열에 대해 εm+n은 O(M−2n)이다. 0≤n→∞limεn≤n→∞limM−2n=0이므로 εn→0이고, 바빌로니아 수열 xn=a(1+εn)는 a로 수렴한다.
정수 바빌로니아 법
이 글의 제목인 "정수 바빌로니아 법"The integral Babylonian method은 임의로 지어낸 말로, problem-solving에서 간간히 필요로 하는 계산인 음이 아닌 정수의 제곱근의 바닥floor을 구하는 방법이다. 가급적이면 실수 계산을 피하고 싶어 만들게 되었는데, 이 절에서 정수 자료형과 바빌로니아 법을 사용하여 음이 아닌 정수의 제곱근의 바닥을 계산하는 C++ 함수와 그 정당성의 증명을 제시한다.
정수 바빌로니아 법의 C++ 구현
다음 integral_babylonian 함수는 정수형 입력 n에 대해 n이 음이 아닌 정수라면 ⌊n⌋를 계산하여 반환한다. n이 음의 정수라면 −1을 반환한다.
int integral_babylonian(int n) {
if (n < 0) return -1;
if (n == 0) return 0;
int p = n, x = (n + 1) / 2;
while (p > x) p = x, x = (x + n / x) / 2;
return p;
}
왜 이런 코드가 나왔는지 모르겠지만 어쨌든 잘 동작한다.
정당성과 시간 복잡도
위 알고리즘은
x0=N,xn+1=f(xn;N):=⌊21(xn+⌊xnN⌋)⌋
으로 정의되는 수열을 계산한다.
N=0일 때, 함수는 0을 반환한다. 이는 원하는 결과이다.
N=1일 때, f(1;1)=1이므로 while 구문 내의 코드가 한 번 실행된 후 1을 반환한다. 이는 원하는 결과이다.
양의 정수 N에 대해, M=⌊N⌋이라고 하자. 양의 정수 e≥1에 대해 x=M+e라고 쓸 수 있을 때, ⌊21(x+⌊xN⌋)⌋는 더 작은 오차를 가짐을 보일 것이다.
N>1일 때 N<N이므로 M<N이다. 따라서 첫 번째 항은 x=M+e0, e0=N−M과 같이 쓸 수 있다.
이다. 또한 M+e+⌊M+eN⌋≥M+e+M−e=2M이므로 f(M+e;N)≥M이고, 계산 결과는 M+e′, e′≥0과 같이 나타낼 수 있다. 오차의 상한 en+1=2en+1의 일반항은 en=2ne0+2n2n−1<2ne0+1이고, 이는 1으로 수렴한다. 따라서 x←f(x;N)을 반복하다 보면 M 또는 M+1에 도달하게 된다.
M2≤N<M2+2M인 경우, x←f(x;N)은 M으로 도달하고, f(M+1;N)=f(M;N)=M이 성립한다. N=M2+2M일 때,
f(M;M2+2M)=M+1,f(M+1;M2+2M)=M
위 계산이 고리를 만들게 된다. while문의 조건을 p > x와 같이 씀으로써 p=M,x=M+1인 경우에 탈출할 수 있다.
알고리즘 정당성에서 사용한 부등식 en≤2nN−M+1을 사용하면, f(−;M)를 N에 O(logN)번 취하였을 때 M을 얻을 수 있음을 알 수 있다. 따라서 정수 바빌로니안 법의 시간 복잡도는 O(logN)이다.
정수 바빌로니아 법의 효용
naïve한 선형 탐색은 O(N) 시간 복잡도를 갖는다. 정수 바빌로니아 법은 O(logn) 시간 복잡도를 가지므로 선형 탐색보다 빠르게 수행될 수 있다.
이분 탐색을 사용해도 O(logn) 시간 복잡도로 음이 아닌 정수의 제곱근의 바닥을 계산할 수 있다.
int sqrt_floor(int n) {
if (n < 0) return -1;
int l = 0, r = n + 1;
while (l + 1 < r) {
int m = (l + r) / 2;
if ((long long) m * m > N) l = m;
else r = m;
}
return l;
}
이 이분 탐색은 [l,r) 구간에서 적당히 중간값인 m=(l+r)/2에 대해 m2과 n의 크기를 비교하여 ⌊n⌋이 [l,m) 와 [m,r) 중 어디에 포함되는지를 판정하는 것을 반복한다. m * m을 계산할 때 오버플로우가 발생할 수 있음에 주의하자.
이분 탐색은 편리하고 범용성이 있지만, 제곱근 바닥을 계산하기 위해서 정수 바빌로니아 법을 사용하였을 때 다음과 같은 효용이 있을 것이다. 첫째로, 계산 식이 x = (x + n / x) / 2;와 같이 간단하기 때문에 구현을 암기하기 쉽다. 또한, m의 제곱을 계산할 때 발생할 수 있는 오버플로우 문제 또는 이분 탐색에서 흔히 범하는 탐색 범위를 설정하는데 발생할 수 있는 실수를 피할 수 있을 것이다. 그러나 여전히 while문의 조건을 헷갈릴 수 있다.