正確性の観点からは、数字の 2 を使用する必要がある理由はありません。どのステップ サイズを選択しても機能します (もちろん 1 つを除く)。ただし、サイズ 2 のステップを選択すると、効率が最大化されます。
これを理解するために、そもそもフロイドのアルゴリズムが機能する理由を見てみましょう。アイデアは、リストの先頭から始めて次に最後まで歩き続けます。リストにサイクルが含まれていない場合、これらの値はすべて別個のものです。ただし、サイクルが含まれている場合、このシーケンスは際限なく繰り返されます。
フロイドのアルゴリズムを機能させる定理は次のとおりです。
任意の正の整数 k に対して x j = x jkとなるような正の整数 j が存在する場合にのみ、連結リストに循環が含まれます。
これを証明してみましょう。それほど難しくありません。「if」の場合、そのような aj が存在する場合、k = 2 を選択します。次に、正の j の場合、x j = x 2jおよび j ≠ 2j であるため、リストにはサイクルが含まれます。
もう一方の方向については、位置 s から始まる長さ l のサイクルがリストに含まれていると仮定します。j を s より大きい l の最小倍数とします。次に、任意の k について、x jと x jkを考慮すると、j はループ長の倍数であるため、x jkは、リスト内の位置 j から始まり、j ステップ k-1 を取ることによって形成される要素と考えることができます。回。しかし、j のステップを実行するたびに、j はループの長さの倍数であるため、リストの最初の場所に戻ってしまいます。したがって、 x j = x jkです。
この証明により、各反復で一定数のステップを実行すると、実際に遅いポインターに到達することが保証されます。より正確には、各反復で k ステップを実行している場合、最終的に点 x jと x kjが見つかり、サイクルが検出されます。直感的には、実行時間を最小化するために k = 2 を選択する傾向があります。これは、反復ごとのステップ数が最も少ないためです。
次のように、ランタイムをより形式的に分析できます。リストにサイクルが含まれていない場合、n はリスト内の要素の数です。それ以外の場合、2 つのポインターは、低速ポインターが j ステップを実行した後に一致します。j は、s より大きい l の最小の倍数であることに注意してください。s ≤ l の場合、j = l; それ以外の場合、s > l の場合、j は最大で 2s になるため、j の値は O(s + l) になります。l と s はリスト内の要素の数を超えることはできないため、これは j = O(n) を意味します。ただし、低速ポインターが j ステップを実行した後、高速ポインターは、低速ポインターが実行した j ステップごとに k ステップを実行するため、O(kj) ステップを実行することになります。j = O(n) であるため、正味の実行時間は最大でも O(nk) です。これは、高速ポインターで実行するステップが多いほど、アルゴリズムが完了するまでに時間がかかることを示していることに注意してください (ただし、比例的にのみ)。したがって、k = 2 を選択すると、アルゴリズムの全体的な実行時間が最小限に抑えられます。
お役に立てれば!