[Python] ヨセフスの問題を循環リストで解く

ヨセフスの問題


 人の人間がを描くように並び、処刑されるのを待っている。最初の人をスキップし、さらに \( k-2 \) 人をスキップし(つまり、\( k-1 \) 人をスキップして \(k \)番目の人に到達する)、\(k\)番目の人を処刑する。そしてそこから、再度 \( k-1 \) 人をスキップして \(k \)番目の人を処刑する。これを延々と続け(円は徐々に小さくなっていく)、最後に残った1人を釈放する。


出典: フリー百科事典『ウィキペディア(Wikipedia)』

再帰を使った考え方は、以下の説明がとても分かりやすかったです。

Explaining the Josephus Algorithm

ここでは、循環リストを使って愚直に考えてみます。

循環リスト


循環リスト(circularly-linked list)では、先頭と最後尾のノードを相互に連結する。循環リストには片方向のものも双方向のものもある。循環リストを辿る場合、任意のノードから出発して、好きな方向にたどっていき、最初のノードに戻ってくるまで続ける。


出典: フリー百科事典『ウィキペディア(Wikipedia)』

ヨセフスの問題を、循環リストを使いそのまま表現します。

コード

def get_Josephus_position(n, m):
    # 循環リストを構成するノード
    class Node:
        def __init__(self, data=None, next=None):
            self.set_data(data)
            self.set_next(next)

        def set_data(self, data):
            self.data = data

        def get_data(self):
            return self.data

        def set_next(self, next):
            self.next = next

        def get_next(self):
            return self.next

        
    answer = []

    # 循環リストを作成する。
    head = Node(0)
    prev = head
    for i in range(1, n):
        current_node = Node(i)
        prev.set_next(current_node)
        prev = current_node
        prev.set_next(head)
    
    # 最後の一つになるまで、循環リストからノードを一つずつ取り除く。
    current_node = head
    counter = 0
    while current_node.get_next() != current_node:
        counter += 1
        if counter == m:
            counter = 0
            prev.set_next(current_node.next)
            answer.append(current_node.get_data())
        else:
            prev = current_node
        current_node = current_node.get_next()

    answer.append(current_node.get_data())

    return answer

print(get_Josephus_position(41, 3))