フロイドの循環検出法のソースを表示
←
フロイドの循環検出法
ナビゲーションに移動
検索に移動
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループに属する利用者のみが実行できます:
登録利用者
。
このページのソースの閲覧やコピーができます。
'''フロイドの循環検出法'''([[英語|英]]: '''Floyd's cycle-finding algorithm''')とは、任意の数列に出現する循環を検出する[[アルゴリズム]]である。任意の数列とは、例えば[[擬似乱数]]列などであるが、単方向[[連結リスト]]とみなせる構造のようなもののループ検出にも適用できる。[[ロバート・フロイド]]が1967年に発明した<ref>{{cite journal|last=Floyd|first=R.W.|authorlink=ロバート・フロイド|title=Non-deterministic Algorithms|journal=J. ACM|pages=pp. 636-644|month=10|year=1967|url=http://doi.acm.org/10.1145/321420.321422}}</ref>。「速く動く」と「遅く動く」という2種類のインデックス(ポインタ)を使うことから、'''ウサギとカメ'''のアルゴリズムといった愛称もある。 グラフの[[最短経路問題]]を解く[[ワーシャル–フロイド法]]とは(同じ発案者に由来するので同じ名前がある、という点以外は)無関係である。 == アルゴリズム == 単方向[[連結リスト]]のループ検出なども典型的なのであるが、形式的(フォーマル)な説明には数列のほうが向いているのでここでは[[擬似乱数]]列生成器の例で説明する。[[ポラード・ロー素因数分解法]]などで擬似乱数列生成器の分析が重要なため、といったこともある。 通常、擬似乱数列生成器は決定的な動作をするのであるから、生成器の内部状態がもし以前と同一になれば、そこから先はその以前と同一の列が再生成される。一般に内部状態の数は有限であるから<ref>理論的には、たとえば円周率を計算し続けるプログラムは、無限の内部状態を持つ擬似乱数列生成器とみなせるが、ここではそういったものは考えない(円周率の小数展開が本当に乱数的かはさておき(まだ決定的な理論は無い))。</ref>、いつかは[[鳩の巣原理]]によって、以前に出現したどこかからと同一の列が再現されるはずである。この時「どこかから」というのが曲者で、調査を始めた列の、必ず先頭からであるとは限らないのが難しい所である。例えば理想的な擬似乱数列生成器であれば全ての内部状態を経てから必ず最初に戻るが(そして、そのようになる条件が明らかな生成器の族もあるが)、数列を生成する任意の関数にそのような期待はできない。 ここでは具体的な擬似乱数列生成器として、[[線形合同法]]のような、通常、内部状態をそのまま出力とする擬似乱数列生成器を考える(もし、内部状態のごく一部のみが出力されるような擬似乱数列生成器を対象とする場合は、当然のことだが、出力される列ではなく、内部状態の列について考えなければならない)。 関数 <math>f</math> を擬似乱数列生成器とする、次のような擬似乱数列 <math>a_i</math> を考える。 :<math>a_{i+1}=f(a_i)</math> ナイーブな方法の一例は、数列をいちいち記録していって、並びが同じ部分を総当り的に探すことである。このとき必要な記憶領域は <math>O(\mu + \lambda)</math> であり、<math>\mu</math> は[[閉路グラフ|循環部分]]の長さ、<math>\lambda</math> は循環していない部分の長さである。 ここで注目すべきは、2つの要素に以下の関係が成り立つとき :<math>a_i=a_{j}</math> その間の要素数 <math>|i - j|</math> は循環の長さの整数倍となる。なぜなら、循環数列の定義から、次が成り立つからである。 :<math>a_{\lambda+m}=a_{\lambda+m+k\mu}.</math> この2つの要素のインデックスの差は <math>k\mu</math> であり、循環部分の長さの整数倍となっている。フロイドの循環検出法は、2つのインデックス変数を並行して増やしていき(ただし、一方はもう一方の2倍の速度で増やす)、このように一致する場合を探すのである。すなわち一方のインデックスを1ずつ増やし、もう一方を2ずつ増やしていく。すると、ある時点で次のようになる。 :<math>a_m=a_{2m}</math> ここで、<math>2m - m = m</math> を割り切れる任意の数が循環の長さとなる(循環に入る前の部分 <math>\lambda</math> は、<math>\lambda > m - \mu</math> でかつ <math>\lambda <= m</math> の任意の値と考えられる)。 一致が見つかったら、<math>\lambda</math> を決定する。これは、一方は <math>a_m</math> から、もう一方は数列の最初から値を求めて比較していくことで分かる(共に1つずつ進めて行く)。<math>a_m</math> は循環の先頭地点から <math>m - \lambda</math> ステップ進んだ地点であり、そこから <math>\lambda</math> ステップ進むと循環の先頭地点からは <math>m</math> ステップの地点である。<math>m</math> は <math>\mu</math> の整数倍であるから、それはつまり、循環の先頭地点に他ならない。従って、<math>\lambda</math> ステップ後に両者は循環の先頭地点に到達し、そこまでの繰り返し回数が <math>\lambda</math> となる。 <math>\lambda</math> が分かれば、巡回の開始地点から第一段階と同じアルゴリズムを繰り返すことで <math>\mu</math> を見つけることができる。この場合、<math>\lambda</math> が <math>0</math> なので、新たな <math>m</math> は <math>\mu</math> の整数倍ではなく、<math>\mu</math> そのものであることが保証される。 === アルゴリズムの可視化 === [[ファイル:CycleFindingNew.png|right]] このアルゴリズムを可視化する最善の方法は、単方向連結リストのループ検出の場合の図(グラフ(ネットワーク)構造)を作ることである。それはちょうどギリシア文字の <math>\rho</math> に似ている。列は、<math>\rho</math> の字の伸びた尻尾の先から始まり、上に登っていき、時計周りに回る。具体的には右図の場合、アルゴリズム中の6回目の繰り返しで <math>a_6</math> の位置で一致が検出される。そのまま続けると、さらに6回繰り返したときに、同じ要素で再び一致する。巡回の長さも 6 であるため、その後も常に同じ結果となる。 == 性能 == このアルゴリズムの第一段階は、最小で <math>\max( \lambda, \mu )</math>、最大で <math>\lambda + \mu</math> の比較演算をする必要がある。循環のスタート地点を探すには <math>\lambda</math> 回の比較が必要である。循環の長さを知るには <math>\mu</math> 回の比較が必要であるが、<math>m</math> が <math>\mu</math> の整数倍であることを利用することで節約が可能である。 このアルゴリズムは <math>O(1)</math> の領域を使用する。 == バリエーション == フロイドの循環検出法のバリエーションとして最も知られているのは、擬似乱数列を使った[[素因数分解]]アルゴリズムである[[ポラード・ロー素因数分解法]]であろう。また、フロイドの循環検出法に基づいて[[離散対数]]を計算するアルゴリズムもある。 == その他の循環検出法 == フロイドのアルゴリズム以外の循環検出法のひとつに、ゴスパーによるものがある(空間計算量が <math>O(1)</math> ではない)。詳細は英語版 [[:en:Cycle detection#Gosper's algorithm]] などを参照のこと。 == 脚注 == {{Reflist}} == 外部リンク == * [http://en.literateprograms.org/Category:Floyd%27s_cycle_finding_algorithm Literate implementations of Floyd's cycle-finding algorithm in various languages] on LiteratePrograms {{DEFAULTSORT:ふろいとのしゆんかんけんしゆつほう}} [[Category:アルゴリズム]] [[Category:不動点]] [[Category:数学に関する記事]] [[Category:エポニム]]
このページで使用されているテンプレート:
テンプレート:Cite journal
(
ソースを閲覧
)
テンプレート:Reflist
(
ソースを閲覧
)
フロイドの循環検出法
に戻る。
ナビゲーション メニュー
個人用ツール
ログイン
名前空間
ページ
議論
日本語
表示
閲覧
ソースを閲覧
履歴表示
その他
検索
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
特別ページ
ツール
リンク元
関連ページの更新状況
ページ情報