2

「オン」と「オフ」の 2 つの状態を持つ四角形のグリッドを使用しています。すべての「ON」コンポーネントを見つけるかなり単純な接続コンポーネントラベリングアルゴリズムがあります。通常、常にではありませんが、「ON」コンポーネントは 1 つだけです。

オン/オフ セルのマトリックス、コンポーネントのラベリング (おそらくセルのハッシュセットのリストとしてフォーマットされている)、およびラベリングが形成されてから変更されたセルのリストを入力として取り、出力するアルゴリズムを構築したいと考えています。新しいラベリング。明白な解決策は、ゼロから再計算することですが、これは効率的ではありません。一般に、変更されたセルのリストは小さくなります。

変更リストがオンになっているセルのみの場合、これは簡単に実行できます。

Groups G;
Foreach changed cell C:
  Group U = emptygroup;
  U.add(C);
  Foreach Group S in G:
    if (S contains a cell which is adjacent to C)
      G.Remove(S);
      U.UnionWith(S);
  G.add(C);

ただし、変更にオフになっているセルが含まれている場合、どうすればよいかわかりません。すべての ON セルは、1 つのグループのメンバーでなければならないことに注意してください。したがって、1 つの解決策は、新しくオフになったセルに隣接する各セルを取得し、それらが互いに接続されているかどうかを確認することです (たとえば、パスファインディングを使用)。これにより、1 ~ 4 個の連続したグループが生成されます (セルがそのグループ内の唯一のセルであり、チェックする隣接セルが 0 である場合を除きます。この場合、0 個のグループが生成されます)。ただし、これはゼロから始めるよりも少しだけ良いにすぎません。なぜなら、通常 (常にではありませんが) これらの隣接する正方形を一緒に接続することは、隣接するグループを見つけるのと同じくらい難しいからです (誰かがそれを行うためのスマートな方法を提案していない限り)。また、変更されたセルがたくさんある場合は少し怖いです...ただし、通常はそうではないことは認めます。

文脈、なぜ私がこれをやっているのかを知りたがっている方のために:ぬりかべパズルのルールの 1 つは、連続する壁のグループは 1 つだけだということです。私が解決しようとしている問題を単純化して、速度を上げる (そしてパスファインディングで遊ぶ) ことは上記のとおりです。基本的には、以前のこのようなテストで得られた情報を無駄にすることなく、隣接する壁をチェックしたいと考えています。O(f(Δ)) が O(f(Δ)) (N はパズルのサイズで、Δ はアルゴリズムが最後に実行されてから行われた変更です)。

プロファイリング、このアルゴリズムを改善すると実行時間に違いが生じることを示していますが、これは利益のためではなく楽しみのためのプロジェクトであるため、変更が何らかの影響を与えたかどうかを測定できることを除いて、それほど重要ではありません.

注: 現在のアルゴリズムの説明は省略しましたが、基本的には、最初に見つかった ON の正方形に対してスタックベースのフラッド フィルアルゴリズムを実行し、さらに ON の正方形があるかどうかを確認します (つまり、複数の正方形があることを意味します)。グループであり、調べる必要はありません)。

編集: 機能強化のアイデア: Yairchu と John Kugelman の提案は、私の頭の中でこの改善に具体化されました。これは、実際にはこの問題自体の解決策ではありませんが、コードのこの部分と他のいくつかのコードの実行を高速化する可能性があります。

現在のループ:

foreach (Square s in m.Neighbors[tmp.X][tmp.Y])    
{
    if (0 != ((byte)(s.RoomType) & match) && Retval.Add(s)) curStack.Push(s);
}

改善案:

foreach (Square s in m.NeighborsXX[tmp.X][tmp.Y])    
{
    if (Retval.Add(s)) curStack.Push(s);
}

これには、いくつかの m.NeighborsXX インスタンス (強化する必要のあるマッチの種類ごとに 1 つ) を維持し、正方形が変更されるたびにすべてを更新する必要があります。これが実際に役立つかどうかを確認するには、これをベンチマークする必要がありますが、速度を犠牲にしてメモリを交換する標準的なケースのようです。

4

3 に答える 3

1

これは、囲碁(日本の囲碁)のゲームで接続された石のストリングを計算する(グリッド上で4つの接続性を想定)のと同じ問題であり、それを段階的に行うことは、高性能の囲碁プレイアルゴリズムの1つの鍵です。

そうは言っても、このドメインでは、1つのグリッド要素をオンにする(ボードに石を追加する)場合も簡単です。これは、以前に接続されていないコンポーネントのみを結合できるためです。問題のあるケースは、1つのグリッド要素をオフにした場合(アルゴリズムの取り消しのために石を削除した場合)、1つのコンポーネントが2つの切断されたコンポーネントに分割される可能性があるためです。

この問題についての私の限られた理解に基づいて、要素をオンにしてラベル付きグループをマージするときはunion-findを使用し、グリッド要素をオフにするときは関連するグループを最初から再計算することをお勧めします。これを最適化するために、グリッド要素をオンとオフの両方に切り替えるときは常に、最初にオフの場合を処理して、ユニオン検索操作が無駄にならないようにします。より高度なインクリメンタルアルゴリズムが必要な場合は、要素ごとにインクリメンタル接続データの維持を開始できますが、ほとんどの場合、効果はありません。

于 2009-06-27T04:15:43.303 に答える
1

完全な解決策ではありませんが、次のようになります。

  • 接続されたコンポーネントごとに、スパニング ツリーをメモリに保持します。
    • ツリー プロパティ A: スパニング ツリーには、どのノードがどの「上」にあるかという概念があります (検索ツリーのように)。どちらが上であるかの選択は任意です
  • エッジの削除と追加について説明しましょう
  • エッジを追加する場合:
    • ツリーのルートが同じかどうかを確認して、2 つのノードが同じコンポーネントにあるかどうかを確認します。
      • ツリー プロパティ B: ツリーは密集している必要があるため、このチェックは O(log n) になります。
    • 同じグループの場合は何もしない
    • それらが異なるグループに属している場合は、新しいエッジでツリーを結合します。
      • これには、新しいエッジが「上」になるように、ツリーの 1 つの「形状」(誰が誰の上にあるかの定義) を変換する必要があります。
  • エッジを削除する場合:
    • このエッジがグループのスパニング ツリーに参加していない場合は、何もしません。
    • その場合、グループがまだ接続されているかどうかを確認する必要があります
      • 1 つのグループから DFS を実行して、他のグループに到達しようとします
      • 小さい方からやったほうがいい
        • ツリー プロパティ C: ツリー内の各ノードについて、そのサブツリーのサイズを維持します
        • プロパティ C を使用して、両方のグループのサイズを知ることができます
      • プロパティ B のため: 通常、小さいグループは非常に小さく、大きいグループは非常に大きくなります。
      • グループが接続されている場合、接続エッジを追加したかのように動作します
      • グループが接続されていない場合は、ツリーを登ってプロパティ C を維持する必要があります (祖先のサブツリーのサイズから以前に接続されたサブツリーのサイズを差し引きます)。
  • 問題: プロパティ B (ツリーが密集している) をどのように維持しますか?

これが理にかなっていることを願っています:)

于 2009-06-27T01:00:07.207 に答える
0

面白い問題!これが私の最初の考えです。うまくいけば、私はもっと多くのことをして、この回答を更新します...

[更新 2] 1 つのグループだけに関心があるため、A* 検索が理想的です。A* 検索と再ラベル付けのプロファイルを作成しましたか? よく書かれた A* 検索は、フラッド フィルよりも高速であると考えなければなりません。そうでない場合は、最適化のために実際のコードを投稿できますか?

[更新 1]新しくオフになったセルCが groupGにあることがわかっている場合は、CCL アルゴリズムを再実行できますが、 group のセルにラベルを付け直すだけで済みGます。他の ON セルは、既存のラベルを保持できます。グリッドの残りの部分を調べる必要がないため、グリッド全体の初期 CCL と比較して大幅な節約になる可能性があります。(私自身、熱心なぬりかべソルバーとして、これは解決済みのパズルで少なくとも 33% の節約になるはずであり、進行中のパズルでは非常に大幅な節約になりますよね? "33%" は、パズルを解決した私の推測では約 2 です。 /3 黒と 1/3 白。)

これを行うには、各グループに含まれるセルのリストを保存して、グループ内のセルをすばやく反復処理し、Gそれらのセルのみにラベルを付け直す必要があります。

于 2009-06-26T23:05:30.217 に答える