====== 요세푸스 문제 ======
* [[wp>Josephus problem]]과 [[https://cp-algorithms.com/others/josephus_problem.html]]을 참고하자.
* 제외되는 번호를 순서대로 나열한 요세푸스 순열을 구하는 문제와, 마지막으로 제외되는 번호를 구하는 문제가 있다.
* 1~n번까지 번호가 있을때, 매 k번째 번호를 제외시킨다
===== 요세푸스 순열을 모두 나열하기 =====
* 그냥 하나씩 삭제하는 과정을 그대로 시뮬레이션을 해보면서 삭제되는 원소를 출력하는 식으로 구현한다.
* k가 작을 때는 deque를 이용해서 시뮬레이션 한다. 맨 앞의 원소를 삭제하고, 그다음 k-1개의 원소를 맨 뒤로 rotate 시키는 것을 반복한다. 이러면 O(kn)에 처리할 수 있다.
* k가 크면, x번째 원소를 삭제하고, 남은 원소중에서 (x + k - 1) % {남은 원소의 수} 번째 원소를 삭제하는 것을 반복하는 식으로 처리한다. x번째 원소를 찾고 삭제하는 것은, [[ps:Order Statistic Tree]]를 이용해서 O(logn)에 처리할 수 있다. 따라서 전체 시간복잡도는 O(nlogn)이 된다
==== 관련 문제 ====
* [[ps:problems:boj:2161]] : k=2인 경우. deque 사용
* [[ps:problems:boj:1168]] : k≤n≤100,000. Order Statistic Tree 사용
===== 마지막으로 남는 번호를 찾는 문제 =====
* 위의 방법처럼 전체 수열을 다 구해서, 마지막 숫자만 출력해도 되긴 하지만, 좀더 효율적으로 풀수 있는 방법들이 있다.
* k=2일때는 마지막 숫자를 closed form으로 구할 수 있다.
* $ J_{n,2} = 1+2(n-2^{\lfloor{log_{2}n}\rfloor}) $
* 비트 연산자를 활용하면 아래처럼 구현할 수 있다.
*
def josephus(n):
return ~(1 << n.bit_length()) & ((n << 1) | 1)
* 일반적으로는 아래 점화식을 이용해서 O(n)에 풀 수 있다
* $ J_{n,k} = (J_{n-1,k}+k){\bmod n},{\text{ with }}J_{1,k}=0 $
* 이 식은 0-base 기준으로 계산한 값이라서, 계산이 끝나면 1을 더해줘야 한다
* 코드는 이런식이 된다
*
def josephus(n, k):
answer = 0
for i in range(2, n + 1):
answer = (answer + k) % i
return answer + 1
* k가 작을 때에는 O(klogn)에 계산할 수 있는 다른 점화식이 있다
* $ {\displaystyle g(n,k)={\begin{cases}0&{\text{if }}n=1\\(g(n-1,k)+k){\bmod {n}}&{\text{if }}1
def josephus(n, k):
def _josephus(n):
if n == 1:
return 0
if k > n:
return (_josephus(n - 1) + k) % n
res = _josephus(n - n // k) - n % k
return res + (n if res < 0 else res // (k - 1))
return (n if k == 1 else _josephus(n) + 1)
==== 관련 문제 ====
* [[ps:problems:boj:2164]] : k=2 인 경우.
* [[ps:problems:boj:11025]]: k≤n≤5,000,000. O(n) 알고리즘 필요
* [[ps:problems:boj:1179]] : n≤10*15, k≤90. O(klogn) 알고리즘 필요