この解決策を私にとって意味のあるものにした重要な洞察は次のとおりです。 の結果は、ヨセフスの生存者である数としてではなく、ヨセフスの生存者である数のインデックスjosephus(n, k)
と考えるのが最善です。たとえば、電話をかけると、5 つの輪の中から生き残った人のインデックスがわかります。josephus(5, 2)
その直感を念頭に置いて、具体的な例を見て、ヨセフス問題がどのように機能するかを考えてみましょう。知りたいとしましょうjosephus(n, 2)
。次のように n 人が並んでいると想像できます。
1 2 3 4 5 ... n
最初に起こることは、ここに示されているように、人物 1 が人物 2 を殺すことです。
1 X 3 4 5 ... n
ここで、次の形式の部分問題が残ります: n-1 人が残っており、1 人おきに殺され、最初に刺すのは人 3 です。つまり、次のような形をした人々の輪が残っています。
3 4 5 ... n 1
josephus(n - 1, 2)
ここで、 n - 1 人がいるので、 を再帰的に呼び出すとします。これにより、n - 1 人の列で誰が生き残るかのインデックスが返されます。生き残る人のインデックスがあり、最初の人が誰であるかもわかっている場合、どの人が残るかを判断できます。その方法は次のとおりです。
この行の開始者は、最後に処刑された人の直後に来る人です。josephus(n - 1, 2)
これは人 3 になります。4 人の輪の中の生存者の 1 インデックス位置は josephus(n - 1, 2) - 1
したがって、必要に応じてリングを一周して、最終的な位置に到達するために、前方の位置を歩くことができます。つまり、生存者は位置によって与えられます
(3 + josephus(n - 1, 2) - 1) % n
ただし、この上記の式には問題があります。実際に1インデックスを使用している場合、最後の生存者が位置nにある場合はどうなりますか? その場合、誤って回答として位置 0 が返されますが、実際には位置 n が必要です。これを修正するために、mod を使用して 1 インデックスでラップアラウンドするトリックを使用します。内側の量 (1 インデックスの位置) を取り、1 を引いてゼロ インデックスの位置を取得します。その量を n で変更して、インデックスがゼロの位置をラップします。最後に、ラップアラウンドされた 1 つのインデックス付きの位置を取得するために 1 つ追加します。それは次のようになります。
(3 + josephus(n - 1, 2) - 2) % n + 1
したがって、ここでの -2 項は、2 つの独立した -1 から来ています。最初の -1 はjosephus(n - 1, 2)
、1 つのインデックス付きインデックスを返すためです。したがって、適切な数の位置だけ前進するには、前進する必要がありjosephus(n - 1, 2) - 1
ます。2 番目の -1 は、0 インデックスではなく 1 インデックスを使用しているという事実から来ています。
これを一般化して、k = 2 だけでなく、任意の k に対しても機能するようにしjosephus(n, k)
ます。その場合、人 1 は人 k を刺し、次のような配列を残します。
1 2 3 ... k-1 X k+1 ... n
基本的に、人 k+1 が最初に来るという部分問題を解決する必要があります。
k+1 k+2 ... n 1 2 ... k-1
したがってjosephus(n - 1, k)
、n - 1 人の輪の 1 インデックスの生存者を計算して、その数のステップだけ前方にシフトします。
(k+1 + josephus(n - 1, k) - 1)
ラップアラウンドするケースを考慮する必要があるため、n で mod する必要があります。
(k+1 + josephus(n - 1, k) - 1) % n
ただし、1 インデックスなので、内側の量から 1 を引き、最後に 1 を加えるというトリックを使用する必要があります。
(k+1 + josephus(n - 1, k) - 2) % n + 1
これは次のように単純化されます
(k-1 + josephus(n - 1, k)) % n + 1
これはと同等です
(josephus(n - 1, k) + k-1) % n + 1
ソリューションコードのように。
要約すると、k-1 項は、位置 k+1 から開始しjosephus(n - 1, k) - 1
、適切な量を前方にシフトするために追加し、次に 1 を減算し、最後に 1 を追加して正しいラップアラウンドを行うことから生じます。
お役に立てれば!