web-dev-qa-db-ja.com

対称性の下で一意である、n x n x nの立方格子のm点のすべてのセットを生成するアルゴリズム

かなり計算が複雑になるアルゴリズムを実装しています。不要な作業を行わないようにしたいと考えています。

N x n x nの立方格子があります。 n = 2の場合、これは(0,0,0)、(0,1,0)、(1,0,0)、(1,1,0)、(0,1,1)、(0、 0,1)、(1,0,1)、(1,1,1)。

このラティスから、mポイントのすべてのセットを再帰的に生成します。

solve(set_of_points) {
     if set_of_points.size = m, finish

     do some useful computation here

     for each point in lattice not in set_of_points:
         solve(set_of_points + new_point);
}

これは、空のset_of_pointsから開始して呼び出すことができます。

問題の性質は、私が実際に必要としないようなものですevery mポイントの順列、立方体の自然な対称性の下でユニークなものだけ。

たとえば、2x2x2の立方体を取り、1点のすべてのセットが必要だとします。上記の基本的なアルゴリズムの下では、1点の8つの異なるセットがあります。

ただし、立方体の対称性を使用すると、元の8のすべてが立方体の対称性の下で等価であるため、これを1つの一意の1ポイントセットに減らすことができます(この場合、それらはすべて「コーナー」です)。

立方体が2x2x2でm = 2の場合、基本アルゴリズムには28セットありますが、対称性の下ではこれは3に減少します(例:{(0,0,0)、(1,0,0)}、{(0 、0,0)、(1,1,0)}、{(0,0,0)、(1,1,1)})

3セットのポイントで計算を行うのは明らかに28よりも優れているので、私の質問は、すでに生成されたセットと対称的に等しいポイントのセットを生成しないようにするにはどうすればよいですか?または、これが不可能な場合は、少なくともセットの数を少し減らすにはどうすればよいですか。

(注-m = 1の場合、これは比較的簡単です。他のどの頂点よりも(0,0,0)に近い点を選択し、境界を少しぼかします。m> 1の場合、本当の問題になる)

10
rbennett485

上記の答えの記法を取ります。

最初に、関数rotate(direction、number_of_time)によって提案される対称性を定義します

解決:

(1)それぞれのフラグが0の順列のすべてのセットのハッシュを作成します。たとえばn = 2、m = 2の場合000,001 = 0 000,010 = 0 000,011 = 0 ect '...

(2)initセットから開始します(例:i = 000,001)

(3)たとえば、回転関数(またはその他の対称性)を使用してセットiを全方向に回転します。回転の順列ごとに、回転関数を24回呼び出す必要があります。

enter image description here

説明:1から6までの任意の数字が目の前にあり、それぞれの数字が4回転できるため、6 * 4 = 24

(4)組み合わせから取得した各セットについて、ハッシュのフラグを1に設定します(すでに対称的なセットがあります)

(5)iを次のセットに更新します(例:i = 000,010)

(6)ハッシュ内のセットiがすでにマークされている場合は(5)に進み、そうでない場合は(3)に進む

すべてのハッシュが1とマークされたら完了です。

1
pio

注:ここでは、回転対称性ではなく、ミラー対称性についてのみ考えます。

d次元の(ハイパー)キューブがあり、各n単位が長いとします(ルービックキューブはd = 3、n =になります) =)。

素朴なアルゴリズムはn ^ dポイントの組み合わせを生成し、他のすべてとの対称性の衝突をそれぞれチェックします。

ポイントの組み合わせをビットベクトルn ^ dビット長として表す場合、マップ(ビットベクトル->ブール値)を使用して、ビットベクトルのすべての対称性をtrue。マップで既にマークされている場合は、その組み合わせをスキップできます。

このアプローチは非常にスペース効率が悪いです。それは2 ^(n ^ d)エントリを持つマップ、つまりこれだけ多くのビットを持つビットマップを取ります。 (ルービックキューブの場合、2 ^ 27 = 128Mビット= 16Mバイトになります。)

n ^ d-bit unsigned Wordとして表される場合、正規表現、つまり最小の整数値を持つビットベクトルのみを覚えることができます。ポイントの新しい順列を生成するとき、そのすべての対称性を生成し、最小の数値で対称性を見た場合にのみチェックします。 2 ^ dの対称性があるので、これは2 ^ nビット(ルービックキューブの場合は1バイトのみ)でのみマップを格納できるようにします。ただし、各ステップでこれらの2 ^ d対称性を生成するため、O(2 ^(d ^ n + d) O(2 ^(d ^ n )* 2 ^ d))==時間。まだ貧しい。

前の段落のアイデアを1次元のケースに適用できます。長さdのベクトルですべての組み合わせを生成するには、すべての0sから開始して、2進数dビット長をインクリメントできます。ベクトルを2つのd/2-longセグメントに分割してみましょう。左右。左側のセグメントの1ビットごとに、右側のセクションの対称位置に1ビットがある組み合わせのみを確認する必要があることがわかります。そうでなければ、ビットの位置が入れ替えられ、01の前に来たときに、以前に対称的な組み合わせをすでに生成していたでしょう。このように、右半分のすべてのビット位置(r)、および左半分の対称位置(l)には、3つの組み合わせのみを生成する必要があります。- (l = 0、r = 0);(l = 1、r = 1);(l = 1、r = 0)。したがって、長さdのベクトルの2 ^(d/2)順列を生成するだけでよく、各順列に対して3つの組み合わせが生成されます。

d次元の立方体はn ^(d-1)ベクトルで構成できます。上記のトリックにより、ベクターは単純なアプローチよりも安価になります。キューブを生成するには、O(n ^(d-1)* 2 ^(d/2))時間が必要です。

1次元ベクトルの次元に沿って立方体を見ると、この次元に沿って対称性を確認する必要がないことがわかります。立方体を生成する際に、関係する各ベクトルの対称性を排除します。

acrossこの次元を見ると、同じトリックを再利用できます。

全体を見るとき、たとえば特定の平面を作るベクトルの最初のビット。これらのビットは、1次元のビットベクトルを表します。上記のように、対称性の理由からビットのほとんどの組み合わせを排除できます。したがって、立方体の特定の1次元ベクトル(たとえば、一番左上の)を選択すると、特定のビットの値に基づいて、同じ平面(たとえば、一番上)の多くのベクトルを削除できます。したがって、平面上の鏡面対称の位置にあるベクトルの場合、すべての組み合わせの生成を回避して、そのビットが設定(または設定解除)される可能性があり、特定の平面に対して生成する必要のあるベクトルの数を大幅に削減できます。除去された各ビットは、ミラー反射位置で可能なベクトルの数を半分にします。これにより、各次元に沿って対称的な対応のない一連の平面が得られます。

このトリックを適用して、3番目の次元に沿って続く平面の順列の生成をさらに制限することができます。

完全なアルゴリズムではありませんが、これが役立つことを願っています。

1
9000

基本的な概念:

(1)点(0,0,0)を単純に000と見なすことができます。ラティスの各点は単純なシーケンスに分類されます。最初のポイントは000、次に001、次に010 011 100 101 110および111です。これは、ポイントセットにポイントを追加しようとする順序です。

(2)同様に、セット{(0,0,0)、(0,0,1)、(0,1,0)}は、単に000001010と見なすことができ、セット{(0,0,0) 、(0,1,0)、(0,0,1)}は単に000010001と見なすことができます。2つの異なるセットに同じシーケンスを含めることはできません。000001010は、数字またはアルファベット順で000010001より小さいと簡単に表示できます。これをセット値と呼びましょう。 Nポイントの可能なすべてのセットにセット値が設定され、Nポイントのすべての可能なセットが単純な順序付けされたリストに含まれるようになりました。

(3)点集合の同型グループにはすべて、最小の設定値を持つメンバーが1つだけあります。これらは、実際に「有用な計算」を行う唯一のものです。

(4)重要な作業が必要になる部分は次のとおりです。 solve(set_of_points + new_point)を実行する前に、同型がset_of_points + new_pointの設定値を下げるかどうかを確認する必要があります。同形がセット値を下げる場合、これは同形セットの最小値メンバーではありません。このnew_pointでの作業はスキップします。また、このsolve(set_of_points、candidate_point)の内部で実行したすべての再帰的な作業をスキップしています。

solve(set_of_points,new_point) {
 set_of_points = set_of_points + new_point
 do some useful computation here
 if set_of_points.size = m, compute how many isomophisms exist, apply that multiple, finish
 for(candidate_point = new_point+1 to last_point) { /skips point-permutations for free!/
  if ISOMORPH_TESTS_CANNOT_LOWER_VALUE_OF(set_of_points+candidate_point) {
   solve(set_of_points,candidate_point);
  }
 }
}
1
Guest