すべてのサイクルを見つける


9

私は有限集合、関数、およびS上の全順序を持っています。Sの異なるサイクルの数を調べたい。f S S < SSfSS<SS

与えられた要素s \ in Sに対して、sSフロイドのアルゴリズム(またはブレントのアルゴリズムなど)を使用して、fを繰り返し適用してsf送信するサイクルの長さを見つけることができます。もう少し努力すれば、このサイクルを特定できます(たとえば、<-最小要素によって)。問題を解決するための悪い方法は、この各要素を繰り返し、重複を破棄して結果の最小要素をソートし、カウントを返すことです。ただし、これには、同じ要素に対する多くのパスと大きなスペース要件が含まれる可能性があります。s<

時間と空間のパフォーマンスが優れているのはどの方法ですか?必要なスペースを測定する最良の方法が何であるかさえわかりませんfが恒等関数である場合、すべてのサイクルを格納するメソッドはすべてΩスペースを使用します。


4
スペースを測定する自然な方法の1つは、Sをnビット文字列のセットと見なし、fをオラクルと見なすことです。次に、説明した単純なアルゴリズムには指数空間が必要です。多項式空間のみを使用するアルゴリズムを探すこともできますが、これは私には可能性が高いとは思えません。
伊藤剛

それが「空間を測る最良の方法がわからない」という意味です。おそらく、私はO(poly(n)+ y)をターゲットにする必要があります。ここで、yは出力であり、yが十分に小さい限り、使用されるスペースは多項式になります。
Charles

関数fには使用可能なプロパティがありますか?アルゴリズムがSのカーディナリティの順序で時間とスペースの両方を必要とするという実際的な答えがある場合、アルゴリズムが入力サイズを表現する好ましい方法で多項式または指数関数であるかどうかは、いくぶん疑わしいでしょう。
Niel de Beaudrap、2011

@ニール・ド・ボードラプ:どんなプロパティが役に立つかわかりません。ただし、異なるサイクルの数は少ないと思いますが、おそらくです。そのため、単にnではなくとnの関数を提案しました。必要に応じて、出力のビット数にスペース指数を使用してもかまいません。yO(n3)ynn
チャールズ

回答:


7

あなたがしたいすべてがサイクル数を数えることであるなら、あなたはこれを2で行うことができます| S | ビット(および変更)分のスペース。Sfに特に便利なプロパティがなければ、もっとうまくできるとは思えません。

整数{0,1,2}を格納する配列Aから開始します— Sの要素ごとに1つ—ゼロに初期化されます。我々は、これらを示します(unexplored)(partially explored)(fully explored)。サイクルカウンターをゼロに初期化します。各要素についてS  ∈  Sの順序で、次の操作を行います。

  1. A [ s ] =  場合は(fully explored)、手順6に進みます。
  2. A [ s ]←  (partially explored)を設定し、イテレータj  ←  f(s)を設定します。
  3. A [ j ] =の間  (unexplored)、A [ j ]←  (partially explored)を設定し、j  ←  f(j)を設定します。
  4. A [ j ] =の  場合(partially explored)、新しいサイクルを閉じています。cを1 ずつインクリメントします(このサイクルの代表の記録を保持したい場合は、jの現在の値が任意の選択として機能します。もちろん、これは必ずしも優先サイクルの最小要素ではありません。順序<。)それ以外の場合は、A [ j ] =  (fully explored)になります。これは、すでにカウントされたサイクルで終了する事前に探索された軌道を発見したことを意味します。cを増分しません。
  5. sで始まる軌道が完全に探索されたことを示すには、j  ←  sを設定します。
    A [ j ] =の間  (partially explored)、A [ j ]←  (fully explored)を設定し、j  ←  f(j)を設定します。
  6. 次の要素に進んでS  ∈  S

したがって、fによって引き起こされる軌道間の各サイクルは1回カウントされます。そして、あなたが代表として記録するあらゆる要素は、別個のサイクルの要素になります。メモリ要件は2です| S | 配列Aの場合、サイクルカウントのO(log | S |)、およびその他のオッズとエンド。

各要素S  ∈  Sは二倍、少なくとも訪問される:Aの値は[場合一度S ]から変更された(unexplored)(partially explored)とへの変更のために一度(fully explored)(fully explored)上記に制限された後にノードが再訪問される合計回数は、そうしないことに失敗した新しいサイクルを見つける試みの回数によって制限されます。S | — Sのすべての要素を反復するメインループから発生します。したがって、このプロセスには最大で3 | S | ノードトラバーサル。ノードが訪問または再訪問されるすべての時間をカウントします。

サイクルの代表的な要素を追跡し、それらを最小要素にしたい場合は、ノードの訪問数を4に制限できます。S |、ステップ4で追加の「サイクルの周回」を追加して、ループを閉じる場所よりも小さい代表を見つける場合。(fの下の軌道がサイクルのみで構成されている場合、この余分な作業は回避できますが、任意のfには当てはまりません。)


O|S|ログ|S|<

多項式以上のスペースを使用せずに総サイクル数が少ない場合に、より少ないスペースを使用する方法があるのでしょうか。ああ、関係ありません。これは私のニーズのために行います。
Charles

1
これは#L(行列の電力供給を使用)にあるはずです。これは#L-hardですか?
カヴェ

@Charles:# cycles∈o| S |)を知っている場合に改善をもたらす私の最近の回答を参照してください。ポリログより多くを使用しています| S | スペース、しかしあなたがスペースと時間のトレードオフをいとわないなら、それはあなたにとってより良いかもしれません。
Niel de Beaudrap 2011

@ニール・ド・ボードラ:ありがとう!両方とも+1。このアルゴリズムは、データがメモリ内に収まる限り最適です。こぼれたらもう一方の使い方を見ていきます。(すべてをキャッシュに収めることができれば、もう一方がより良い可能性もありますが、それは面倒すぎるかもしれません。)
Charles

5

サイクルが非常に少ない場合は、使用するスペースは少なくなりますが、終了するまでにかなり時間がかかるアルゴリズムを次に示します。

[編集]私の以前のランタイム分析では、訪問するノードが以前にサンプリングされたノードの中にあるかどうかを判断するための重要なコストを逃しました。この回答はこれを修正するために多少修正されています。

Sのすべての要素を繰り返します。我々は要素の軌道探求として  ∈  Sを、我々は再び彼らに遭遇しているかどうかを確認することができるようにするために、私たちが訪問したことのノードからのサンプル。また、以前にアクセスした「コンポーネント」(共通のサイクルで終了する(したがって、サイクルと等しい)軌道の和集合)のサンプルのリストも維持します。

コンポーネントの空のリストを初期化しますcomplist。各コンポーネントは、そのコンポーネントのサンプルのコレクションによって表されます。また、samplesコンポーネントなどのサンプルとして選択されたすべての要素を格納する検索ツリーも維持します。LET Gは最大の整数のシーケンスであるn個のメンバーシップは、いくつかのブール述語を計算することによって効率的に決定されています; 例えば、2つのまたは完璧なのべき乗のp 番目のいくつかの整数のための力のp。それぞれについて、S  ∈  S、次の操作を行います。

  1. sがにある場合は、samples手順5に進みます。
  2. 空のリストcursample、イテレーターj  ←f(s)、およびカウンターt  ←1を初期化します。
  3. 一方でjはしていないsamples
    -もしT  ∈  G、挿入jの両方にcursamplesamples
    tをインクリメントし、j  ←  f(j)を設定します。
  4. jが内にあるかどうかを確認してくださいcursample。そうでない場合は、以前に調査したコンポーネントに遭遇しました。jが属するコンポーネントを確認し、のすべての要素cursampleをの適切な要素に挿入して、complistそれを拡張します。それ以外の場合は、現在の軌道から要素に再遭遇しました。つまり、以前に発見されたサイクルの代表に遭遇することなく、少なくとも1回サイクルをたどったことを意味cursamplecomplistます。新しく見つかったコンポーネントからのサンプルのコレクションとして、をに挿入します。
  5. 次の要素に進んでS  ∈  S

以下のためのn  = | S |、X(n)を期待されるサイクル数を表す単調増加関数(たとえば X(n)n 1/3)とし、Y(n) = y(n)  log(n)∈Ω(X(n)  log(n))は、メモリ使用量のターゲットを決定する単調増加関数です(たとえば、 y(n)n 1/2)。我々は必要とY(N)  ∈Ω(X(N) )が少なくとも取るためX(n)は、  ログ(nは各コンポーネントからの1個のサンプルを格納するために)スペースを。

  • サンプリングする軌道の要素が多いほど、軌道の終わりのサイクルでサンプルをすばやく選択して、そのサイクルをすばやく検出する可能性が高くなります。漸近的な観点からは、メモリの境界が許す限り多くのサンプルを取得することは理にかなっています。Gnより小さい期待されるy(n)要素を持つように設定することもできます。— Sの軌道の最大長がLであると予想される場合、GL  /  y(n)の整数倍にすることができます。—予想される長さが存在しない場合、n  /  y(n)ごとに1回サンプリングするだけです。

    要素; これはいずれにしても、サンプル間の間隔の上限です。

  • 新しいコンポーネントを探す際に、以前にアクセスしたSの要素をトラバースし始める場合(新しいコンポーネントが発見されているか、またはターミナルサイクルが既に見つかっている古いコンポーネントから)、最大でn  /  y( n)以前にサンプリングされた要素に遭遇するための反復。これは、回数の上限です。新しいコンポーネントを見つけるたびに、冗長ノードをたどります。このような試みをn回行うので、Sの要素を合計で最大n 2  /  y(n)回冗長に訪問します。

  • のメンバーシップをテストするために必要な作業samplesはO(y(n)  log  y(n))であり、これは訪問ごとに繰り返されます。このチェックの累積コストはO(n 2  log  y(n))です。また、サンプルをそれぞれのコレクションに追加するコストもあり、累積的にO(y(n)  log  y(n))になります。最後に、以前に発見されたコンポーネントに再遭遇するたびに、最大X(n)  log *  y(n)の時間を費やして、どのコンポーネントを再発見したかを判別する必要があります。これはn回まで発生する可能性があるため、関連する累積作業はn X(n)  log y(n)によって制限されます  。

したがって、アクセスするノードがサンプルの中にあるかどうかをチェックする際に実行される累積的な作業がランタイムを支配します。これにはO(n 2  log y(n))がかかります  。次に、y(n)をできるだけ小さくする必要があります。つまり、O(X(n))です。

したがって、O(n2)  log  X(n))を取って、O(X(n)  log(n))空間でサイクル数(これらのサイクルで終了するコンポーネントの数と同じ)を列挙できます。そうする時間が、ここでX(n)は、サイクルの予想される数です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.