이 글은 요세푸스 문제(Josephus Problem)가 무엇인지, 그리고 요세푸스 문제의 해법에 대한 내용을 대략적으로 정리해둔 글이다.

 

1. 유래

요세푸스 문제는 1세기 로마시대의 유대인 역사가인 플라비우스 요세푸스(Titus Flavius Josephus)의 이름을 따 이름붙은 문제로 그 유래는 다음과 같다.

유대-로마 전쟁이 일어나던 시기, 요세푸스와 40명의 동료가 로마군에게 포위되었다. 동료들은 로마군에게 죽을 바에는 스스로 목숨을 끊기로 결정한다. 그러나 유대교에서 자살은 불명예스러운 죽음이므로, 서로 대신 죽여주기로 결정하였다.
구체적인 방법은 전해지지 않지만 그 방법에 대한 야사가 존재한다. 요세푸스와 동료들은 원형으로 둘러앉아, 앉은 순서대로 3명에 한 명씩 규칙적으로 죽여나갔다는 것이다. 그러나 다른 동료들과 달리 죽고싶지 않았던 요세푸스와 그의 친구는 기지를 발휘해 그 둘이 마지막까지 생존할 자리를 계산했고, 그 둘은 결국 죽지 않고 살아남게 된다. 이후 둘은 로마군에 투항하게 된다.

요세푸스와 그 친구가 앉아있던 위치는 어디였을까? 이 글을 읽고 문제를 해결해보자.

 

2. 요세푸스 문제(Josephus problem)와 요세푸스 순열(Josephus permutation)

이 글에서 요세푸스 문제(Josephus problem)는 다음과 같은 문제를 의미한다.

두 양의 정수 n과 k가 주어진다.
0부터 n - 1까지의 양의 정수를 시계방향으로 차례대로 원형으로 배치하고 0부터 시작해 시계방향으로 k-1개의 수를 건너뛰고 k번째 수를 제거하는 것을 하나의 정수가 남을 때까지 반복할 때, 마지막으로 남은 정수는 무엇일까?

정수의 index가 1이 아닌 0부터 시작함에 유의하자. 1이 아닌 0부터 시작하는 것으로 문제를 정의한 이유는 풀이를 더욱 간결하게 서술하기 위해서이다.

 

유래의 상황은 n = 41이고 k = 3인 요세푸스 문제의 상황과 매우 유사하다는 점을 관찰할 수 있다. (유래에서는 총 두 명이 살아남는다는 점이 다르다.)

 

또한, 위의 과정(단, 여기에서는 모든 정수가 제거될 때까지 반복한다)에서 제거되는 정수들을 차례로 나열해 얻는 순열을 요세푸스 순열(Josephus permutation)이라 한다.

 

다음 문단부터는 요세푸스 순열을 구하는 알고리즘과 요세푸스 문제를 해결하는 알고리즘에 대해 알아보자.

 

3. 요세푸스 순열을 구하는 알고리즘

3.1. O(nk) 해법

가장 단순한 풀이 중 하나로 큐(queue) 자료구조를 이용해 요세푸스 문제의 시행을 직접 시뮬레이션하는 풀이가 존재한다. 구체적으로, 다음과 같이 문제를 해결할 수 있다.

 

(1) 1부터 n까지의 양의 정수가 차례대로 들어있는 큐를 준비한다.

(2) 큐의 앞에 있는 원소 x를 빼고, 다시 x를 큐에 넣는 것을 k-1번 반복한다.

(3) 큐의 앞에 있는 원소 x를 뺀다. 이 x가 다음으로 제거되는 수이다.

(4) 큐에 원소가 남아있다면 2로 돌아간다.

 

큐의 앞에 있는 원소를 빼고 넣는(O(1)) 작업을 k번 반복해 다음 수를 얻는 것을 n번 시행해 문제를 해결하므로 이 해법의 시간복잡도는 O(nk)이다.

 

3.2. O(nlgn) 해법

위 방법보다 조금 더 빠른 풀이로 세그먼트트리를 이용한 방법이 있다.

 

구체적으로, 구간합을 다루는 세그먼트트리를 이용해 남아있는 수중 다음 k번째 수가 무엇인지를 세그먼트트리 위에서의 이진탐색으로 찾아내고 해당 수를 0으로 업데이트하는 것을 반복해 문제를 해결할 수 있다. 각 작업의 시간복잡도는 O(lgn)이므로 전체 시간복잡도는 O(nlgn)이 된다.

 

4. 요세푸스 문제를 해결하는 알고리즘

두 양의 정수 n과 k에 대하여 J(n, k)를 n과 k에 대한 요세푸스 문제의 해답으로 정의하자.

4.1. 요세푸스 순열을 구해 해결하는 방법

위의 3문단에서 구한 요세푸스 순열의 마지막 수는 요세푸스 문제의 해답과 같다. 따라서 3문단에서 소개한 방법들을 이용해 요세푸스 문제를 O(nk)또는 O(nlgn)에 해결할 수 있다.

 

4.2. O(n) 해법

두 양의 정수 n과 k에 대하여 J(n, k)에 대해 다음과 같은 점화관계가 성립함을 관찰할 수 있다.

J(n, k) = (J(n-1, k) + k) % n

이와 같은 점화관계를 이용해 요세푸스 문제를 O(n)에 해결할 수 있다.

 

4.3. O(klgn) 해법

k가 n에 비해 크기가 많이 작은 경우 O(n)보다 더욱 빠르게 작동하는 풀이 또한 존재한다. 이 방법은 위의 점화관계를 차례대로 한 단계씩 계산하는 대신 한 바퀴 단위로 계산하는 것으로 효율의 상승을 꾀한다.

 

구체적으로, 두 양의 정수 n과 k에 대하여 n을 k로 나눈 몫을 p라 정의하자. 이 때, 다음과 같은 점화관계가 성립함을 관찰할 수 있다.

J(n, k) = (J(n-p, k) + pk) % n

단, p = 0인 경우 위의 식은 더 이상 사용할 수 없으므로 4.2의 식을 이용해 마저 계산을 이어나가자.

 

아래는 위 점화식을 이용해 요세푸스 문제를 해결할 때의 시간복잡도를 계산하는 과정이다:

두 양의 정수 n과 k에 대하여, 위와 같은 단계를 x회 거친 뒤 남는 정수의 개수는 대략 n*(1 - 1/k)^x로 계산해낼 수 있다. 남는 정수의 개수가 하나가 될 때까지 이를 반복한다고 이상적으로 생각하면 대략적으로 n*(1 - 1/k)^x = 1와 같은 관계식을 얻을 수 있고, 이 식으로부터 x = - lgn / lg(1 - 1/k)를 얻어낼 수 있다. 따라서 이 과정의 시간복잡도는 O(x) = O(klgn)과 같다는 것을 알 수 있다. p = 0 이후의 계산 횟수는 O(k)이므로 이 값은 시간복잡도에 영향을 주지 않음을 확인하자.

 

4.4. k=2인 경우(O(1) 해법)

유래의 상황과 같이 k=2인 경우에는 더욱 빠른 풀이가 존재한다. 바로 공식을 이용하는 것이다.

 

구체적으로, k=2인 경우 다음과 같은 공식으로 답을 구할 수 있다:

n = 2^m+l, l<2^m을 만족하는 음이 아닌 정수 m과 l에 대하여, J(n, 2) = 2*l + 1이다.

증명은 귀납법을 이용하여 간단히 할 수 있다.

 

위 공식을 활용하면 n의 이진표기에서 최상위 비트(msb;most-significant bit) 1을 지우고 최하위 비트(lsb;least-significant bit) 1을 하나 추가하는 것으로 문제를 해결할 수 있다는 것을 알 수 있다.

 

5. 연습문제 추천

이제 개념 연습문제를 풀면서 요세푸스 문제와 그 풀이를 더 깊이 이해하고 PS/CP 연습문제를 풀면서 코드로 직접 구현해보자. ★이 붙은 문제는 꼭 풀어보고 넘어가자.

 

5.1. 개념 연습문제

★(1) 1부터 41까지의 양의 정수를 시계방향으로 차례대로 원형으로 배치하고 1부터 시작해 시계방향으로 두 개의 수를 건너뛰고 세번째 수를 제거하는 것을 두 개의 정수가 남을 때까지 반복할 때, 마지막으로 남은 정수 두 개는 무엇일까?

(2) 세 양의 정수 n, m과 k가 주어질 때, 다음과 같은 문제의 해답을 J'(n,m,k)라 하자. (단, m<n)

0부터 n - 1까지의 양의 정수를 시계방향으로 차례대로 원형으로 배치하고 0부터 시작해 시계방향으로 k-1개의 수를 건너뛰고 k번째 수를 제거하는 것을 m개의 정수가 남을 때까지 반복할 때, 남겨진 정수들의 집합을 구하시오.

J'(n,m,k)의 값을 O(nm)에 찾아내는 알고리즘을 설계해보자.

(3) 임의의 짝수 2n에 대하여 1부터 2n까지의 양의 정수를 시계방향으로 차례대로 원형으로 배치하고 1부터 시작해 시계방향으로 k - 1개의 수를 건너 뛰고 k번째 수를 제거하는 것을 n개의 정수가 남을 때까지 반복하자. 이 때, (a) n+1 이상 2n 이하의 정수들만 남게 하는 k가 존재할까? 또한 (b) 1 이상 n 이하의 정수들만 남게 하는 k가 존재할까? 이유와 함께 설명해보자.

 

5.2. PS/CP 연습문제(BOJ)

11866번 문제: 요세푸스 문제 0

1158번 문제: 요세푸스 문제

1168번 문제: 요세푸스 문제 2

11025번 문제: 요세푸스 문제 3

1179번 문제: 마지막 요세푸스 문제

20301번 문제: 반전 요세푸스

6602번 문제: Eeny Meeny Moo

7580번 문제: Team Selection

 

6. 인용

365일 수학 - 요세푸스 문제

cp-algorithms.com - Josephus Problem

위키백과 - Josephus Problem

728x90

네 자리 정수(leading zero 포함) K에 다음과 같은 연산을 반복하면 어떻게 될까?

1. K의 각 자리로 이루어진 가장 큰 수 K1과 가장 작은 수 K2를 구한다.
2. K1과 K2의 차를 구해 새로운 K로 한다.

 

위 연산에 대해 단순히 추측해보면, K는 (비둘기집의 정리에 의해) 일정 주기로 같은 순서를 반복하게 될 것이라고 기대할 수 있다.

 

하지만 네 자리 정수에 한정하면 더욱 강력한 특징이 있다. 바로, K의 모든 자릿수가 동일(1111 등)하지 않다면 K는 7번 이하의 연산을 적용하면 항상 6174가 된다는 것이다.

 

위의 연산은 Kaprekar's routine이라고 불리는 연산이다. 그리고 (모든 자릿수가 동일하지 않은) 각 자리 수에 대하여 Kaprekar's routine을 반복 적용했을 때 최종적으로 나올 수 있는 수를 Kaprekar's constant라 한다. 이와 같은 이름은 이러한 개념을 탐구한 인도의 수학자 D. R. Kaprekar의 이름에서 따왔다.

 

세 자리 수에도 마찬가지로 네자리 수의 6174와 같은 수가 존재한다. 바로 495이다. 임의의 (모든 자릿수가 동일하지 않은) 세 자리수로 시작해 Kaprekar's routine을 반복 적용하면 그 세자리수는 6번 이하의 연산을 적용하면 항상 495가 된다.

 

Kaprekar's constant는 모든 자릿수에 대해 항상 존재할까? 아쉽지만 그렇지는 않다. 예를 들어, 다섯자리 수의 경우 Kaprekar's routine을 적용하다보면 각 수는 (53955와 59994를 반복하는 사이클), (61974, 82962, 75933, 63954를 반복하는 사이클), (62964, 71973, 83952, 74943을 반복하는 사이클)의 세 가지 사이클중 하나로 수렴하게 된다.

 

Kaprekar's routine은 세 자리 수와 네 자리 수를 한 가지 수로 수렴시키는 재미있는 특징이 있고 그 과정이 단순해 코딩 입문자들에게 구현 예제로 추천할만한 주제 중 하나이다. 관심이 있다면 아래의 문제를 해결해보자.

https://www.acmicpc.net/problem/9047 

 

9047번: 6174

입력은 표준입력(standard input)을 통해 받아들인다. 입력의 첫 줄에는 테스트 케이스의 개수 T(1 ≤ T ≤ 20)가 주어진다. 각 테스트 케이스마다 한 줄에 네 자리 수(1000~9999)가 하나씩 주어진다. 단,

www.acmicpc.net

 

위 문제의 풀이는 다음 글에 적어두었으니 관심이 있다면 읽어보자.

 

이 글은 다음 글을 참조하여 작성하였다. 더 많은 정보를 알고싶다면 다음 글을 읽어보자:

https://en.wikipedia.org/wiki/Kaprekar%27s_routine 

 

728x90

0. 필요한 배경지식

 

 - 가장 긴 단조증가하는 부분수열

 - 비둘기집의 원리(pigeonhole principle)

 

 

1. 서론

 

가장 긴 단조증가하는 부분수열(LIS)를 찾는 문제는 이산수학에서 유명한 문제로, 길이 N인 수열의 LIS의 길이를 구하는 문제나 그런 길이의 단조증가 부분수열을 직접 찾는 문제는 모두 O(NlogN)에 해결할 수 있다.

심지어는 길이가 N인 수열의 서로 다른 LIS가 몇 개 존재하는지 찾는 문제 또한 O(NlogN)에 해결할 수 있다.

 

LIS를 구하다보면 LIS가 전체 수열의 길이에 비해 특히 짧은 경우들을 자주 만날 수 있다.

이런 경우를 만나면 LIS와 반대방향인, 가장 긴 단조감소하는 부분수열(이후 LDS라 칭한다)의 길이가 길어지는 것이 자주 눈에 띄지 않는가?

 

Erdős–Szekeres Theorem은 이러한 관점에서 바라볼 때, 주어진 모든 원소가 서로 다른(distinct) 수열이 주어졌을 때 LIS의 길이와 LDS의 길이 사이의 상관관계를 보여주는 정리이다.

 

 

2. 정리 소개

 

Erdős–Szekeres theorem는 다음과 같이 서술할 수 있다.

 

두 수 r과 s에 대하여, 모든 원소가 서로 다른(distinct) 길이가 rs + 1인 수열은

길이 r + 1인 LIS를 포함하거나 길이 s + 1인 LDS를 포함한다.

 

rs + 1보다 더 긴 길이의 수열은 길이가 rs + 1인 부분수열을 포함하므로 위의 성질을 만족한다.

 

증명을 하기 전에, 길이가 (r - 1)(s - 1)인 수열중에서 길이가 r + 1인 단조증가 부분수열과 길이가 s + 1인 단조감소 부분수열이 둘 다 존재하지 않는 예시를 직접 찾아보자. 아래 접은 글로 그러한 수열의 예시를 적어두었다.

더보기

다음과 같은 수열은 길이가 rs이지만 LIS의 길이가 r이고 LDS의 길이가 s이다.

s, s-1, s-2, ..., 2, 1, 2s, 2s-1, 2s-2, ..., s+2, s+1, 3s, 3s-1, ..., rs, rs-1, rs-2, ..., (r-1)s+2, (r-1)s+1

 

 

3. 증명

 

이 글에서는 Seidenberg의 증명을 소개한다.

 

길이가 rs + 1이고 모든 원소가 서로 다른 임의의 수열 a_i를 생각하자.

또한, m_i를 a_i서부터 시작되는 부분수열의 LIS의 길이, n_i를 a_i서부터 시작되는 부분수열의 LDS의 길이로 정의하자.

 

임의의 i < j에 대하여, 다음이 성립한다는 것을 쉽게 관찰할 수 있다.

 - a_i < a_j 이면 m_i > m_j 이다.

 - 반대로 a_i > a_j 이면 n_i > n_j 이다.

 

따라서, 각 i에 순서쌍 (m_i, n_i)를 대응시키면, 수열 a_i는 길이가 rs + 1이므로 서로 다른 rs + 1개의 순서쌍 (m_i, n_i)를 얻을 수 있다.

 

만약 a_i의 LIS의 길이가 r이고 LDS의 길이가 s이면, 만들 수 있는 서로 다른 순서쌍 (m_i, n_i)의 개수는 많아야 rs개이다.

따라서, 수열의 항이 rs개보다 많으므로, 비둘기집의 원리에 의해 이는 모순임을 알 수 있다.

 

따라서, LIS의 길이가 r보다 크거나, LDS의 길이가 s보다 클 수밖에 없음을 알 수 있다.

이것으로 Erdős–Szekeres Theorem이 증명되었다.

 

 

4. 응용

 

Erdős–Szekeres Theorem을 기하학적으로 생각해보자.

 

위 증명에서의 수열의 각 항 a_i를 2차원 좌표평면 위의 점 (i, a_i)에 대응시켜 생각을 해보면, Erdős–Szekeres theorem은 좌표평면 위의, 같은 x좌표 또는 y좌표를 가지지 않는 rs + 1개의 점에서 기울기가 연속적으로 양수인 r + 1개의 점 또는 기울기가 연속적으로 음수인 s + 1개의 점을 항상 찾을 수 있다는 것을 말해준다.

 

 

5. 여담

 

글쓴이는 다음 문제를 풀고 관련된 내용을 찾아보다가 위의 내용을 정리하게 되었다.

관심이 있다면 다음 문제를 풀어보자.

www.acmicpc.net/problem/1201

 

1201번: NMK

첫째 줄에 세 정수 N, M, K가 주어진다.

www.acmicpc.net

 

 

6. 참고한 내용

위키백과 Erdős–Szekeres Theorem: en.wikipedia.org/wiki/Erd%C5%91s%E2%80%93Szekeres_theorem

Seidenberg, A. (1959), "A simple proof of a theorem of Erdős and Szekeres"

728x90

+ Recent posts