C
前書き
David Carraherがコメントしたように、六角形のタイルを分析する最も簡単な方法は、3次元Young Diagramの同型を利用することであると思われます。 z軸に近づくにつれて。
私は、3つのデカルト軸の1つへのバイアスに基づく公開されたアルゴリズムよりも、対称性のカウントに適応しやすい合計を見つけるためのアルゴリズムから始めました。
アルゴリズム
まず、x、y、z平面のセルに1を入力し、残りの領域にはゼロを含めます。それが終わったら、レイヤーごとにパターンを作成します。各レイヤーには、原点から共通の3Dマンハッタン距離を持つセルが含まれています。セルは、その下の3つのセルにも1が含まれている場合にのみ1を含むことができます。いずれかのセルに0が含まれている場合、セルは0でなければなりません。
この方法でパターンを作成する利点は、各レイヤーがx = y = z線に関して対称であることです。これは、各レイヤーの対称性を個別にチェックできることを意味します。
対称性チェック
ソリッドの対称性は次のとおりです。x= y = z線の周りの3回転->六角形の中心の周りの3回転。そして、x = y = zラインと各軸x、y、zを含む3つの平面に関する3 x反射->六角形の角を通るラインに関する反射。
これにより、合計で6回対称になります。六角形の完全な対称性を得るには、別の種類の対称性を考慮する必要があります。各ソリッド(1から構築)には補完的なソリッド(0から構築)があります。Nが奇数の場合、補完ソリッドは元のソリッドと異なる必要があります(同じ数のキューブを持つことはできないため)。しかし、相補的なソリッドが丸くなると、ダイアモンドタイリングとしての2D表現が元のソリッドと同じであることがわかります(2重対称操作を除く)。Nが偶数の場合、ソリッドが自己反転する可能性があります。
これは、質問のN = 2の例で見ることができます。左から見ると、最初の六角形は8個の小さな立方体のある立方体のように見え、最後の六角形は0個の小さな立方体のある空のシェルのように見えます。右側から見た場合、逆のことが当てはまります。3番目、4番目、5番目の六角形と16、17、18番目の六角形は、2個または6個の立方体を含むように見えるため、3次元で互いに補完します。これらは2次元対称操作(2回転、または六角形のエッジを通る軸の周りの反射)によって2次元で互いに関連しています。一方、9、10、11、12番目の六角形は3Dパターンを示し、独自の補数であるため、対称性が高くなります(したがって、これらは奇数の多重度を持つ唯一のパターンです)。
(N ^ 3)/ 2キューブを持つことは、自己補完するために必要な条件ですが、一般に、N> 2の場合は十分な条件ではないことに注意してください。このすべての結果は、奇数Nの場合、タイルは常にペア(N ^ 3)/ 2キューブで発生するため、慎重に検査する必要があります。
現在のコード(N = 1,2,3,5の正しい合計を生成します。N= 4で説明したエラー。)
int n; //side length
char t[11][11][11]; //grid sized for N up to 10
int q[29][192], r[29]; //tables of coordinates for up to 10*3-2=28 layers
int c[9]; //counts arrangements found by symmetry class. c[8] contains total.
//recursive layer counting function. m= manhattan distance, e= number of cells in previous layers, s=symmetry class.
void f(int m,int e,int s){
int u[64], v[64], w[64]; //shortlists for x,y,z coordinates of cells in this layer
int j=0;
int x,y,z;
for (int i=r[m]*3; i; i-=3){
// get a set of coordinates for a cell in the current layer.
x=q[m][i-3]; y= q[m][i-2]; z= q[m][i-1];
// if the three cells in the previous layer are filled, add it to the shortlist u[],v[],w[]. j indicates the length of the shortlist.
if (t[x][y][z-1] && t[x][y-1][z] && t[x-1][y][z]) u[j]=x, v[j]=y, w[j++]=z ;
}
// there are 1<<j possible arrangements for this layer.
for (int i = 1 << j; i--;) {
int d = 0;
// for each value of i, set the 1's bits of t[] to the 1's bits of i. Count the number of 1's into d as we go.
for (int k = j; k--;) d+=(t[u[k]][v[k]][w[k]]=(i>>k)&1);
// we have no interest in i=0 as it is the empty layer and therefore the same as the previous recursion step.
// Still we loop through it to ensure t[] is properly cleared.
if(i>0){
int s1=s; //local copy of symmetry class. 1's bit for 3 fold rotation, 2's bit for reflection in y axis.
int sc=0; //symmetry of self-complement.
//if previous layers were symmetrical, test if the symmetry has been reduced by the current layer
if (s1) for (int k = j; k--;) s1 &= (t[u[k]][v[k]][w[k]]==t[w[k]][u[k]][v[k]]) | (t[u[k]][v[k]][w[k]]==t[w[k]][v[k]][u[k]])<<1;
//if exactly half the cells are filled, test for self complement
if ((e+d)*2==n*n*n){
sc=1;
for(int A=1; A<=(n>>1); A++)for(int B=1; B<=n; B++)for(int C=1; C<=n; C++) sc&=t[A][B][C]^t[n+1-A][n+1-B][n+1-C];
}
//increment counters for total and for symmetry class.
c[8]++; c[s1+(sc<<2)]++;
//uncomment for graphic display of each block stacking with metadata. not recommended for n>3.
//printf("m=%d j=%d i=%d c1=%d-2*%d=%d c3=%d cy=%d(cs=%d) c3v=%d ctot=%d\n",m,j,i,c[0],c[2],c[0]-2*c[2],c[1],c[2],c[2]*3,c[3],c[8]);
//printf("m=%d j=%d i=%d C1=%d-2*%d=%d C3=%d CY=%d(CS=%d) C3V=%d ctot=%d\n",m,j,i,c[4],c[6],c[4]-2*c[6],c[5],c[6],c[6]*3,c[7],c[8]);
//for (int A = 0; A<4; A++, puts(""))for (int B = 0; B<4; B++, printf(" "))for (int C = 0; C<4; C++) printf("%c",34+t[A][B][C]);
//recurse to next level.
if(m<n*3-2)f(m + 1,e+d,s1);
}
}
}
main()
{
scanf("%d",&n);
int x,y,z;
// Fill x,y and z planes of t[] with 1's
for (int a=0; a<9; a++) for (int b=0; b<9; b++) t[a][b][0]= t[0][a][b]= t[b][0][a]= 1;
// Build table of coordinates for each manhattan layer
for (int m=1; m < n*3-1; m++){
printf("m=%d : ",m);
int j=0;
for (x = 1; x <= n; x++) for (y = 1; y <= n; y++) {
z=m+2-x-y;
if (z>0 && z <= n) q[m][j++] = x, q[m][j++] = y, q[m][j++]=z, printf(" %d%d%d ",x,y,z);
r[m]=j/3;
}
printf(" : r=%d\n",r[m]);
}
// Set count to 1 representing the empty box (symmetry c3v)
c[8]=1; c[3]=1;
// Start searching at f=1, with 0 cells occupied and symmetry 3=c3v
f(1,0,3);
// c[2 and 6] only contain reflections in y axis, therefore must be multiplied by 3.
// Similarly the reflections in x and z axis must be subtracted from c[0] and c[4].
c[0]-=c[2]*2; c[2]*=3;
c[4]-=c[6]*2; c[6]*=3;
int cr[9];cr[8]=0;
printf("non self-complement self-complement\n");
printf("c1 %9d/12=%9d C1 %9d/6=%9d\n", c[0], cr[0]=c[0]/12, c[4], cr[4]=c[4]/6);
if(cr[0]*12!=c[0])puts("c1 division error");if(cr[4]*6!=c[4])puts("C1 division error");
printf("c3 %9d/4 =%9d C3 %9d/2=%9d\n", c[1], cr[1]=c[1]/4, c[5], cr[5]=c[5]/2);
if(cr[1]*4!=c[1])puts("c3 division error");if(cr[5]*2!=c[5])puts("C3 division error");
printf("cs %9d/6 =%9d CS %9d/3=%9d\n", c[2], cr[2]=c[2]/6, c[6], cr[6]=c[6]/3);
if(cr[2]*6!=c[2])puts("cs division error");if(cr[6]*3!=c[6])puts("CS division error");
printf("c3v %9d/2 =%9d C3V %9d/1=%9d\n", c[3], cr[3]=c[3]/2, c[7], cr[7]=c[7]);
if(cr[3]*2!=c[3])puts("c3v division error");
for(int i=8;i--;)cr[8]+=cr[i];
printf("total =%d unique =%d",c[8],cr[8]);
}
出力
プログラムは、ソリッドの8つの対称性に従って、8エントリの出力テーブルを生成します。ソリッドには、次の4つの対称性があります(シェーン表記)
c1: no symmetry
c3: 3-fold axis of rotation (produces 3-fold axis of rotation in hexagon tiling)
cs: plane of reflection (produces line of reflection in hexagon tiling)
c3v both of the above (produces 3-fold axis of rotation and three lines of reflection through the hexagon corners)
さらに、ソリッドのセルのちょうど半分が1で、残りの半分が0である場合、すべての1と0を反転させてから、キューブ空間の中心で座標を反転させる可能性があります。これは私が自己補数と呼んでいるものですが、より数学的な用語は「反転中心に関して非対称」です。
この対称操作により、六角形のタイルの回転軸が2倍になります。
この対称性を持つパターンは、別の列にリストされています。Nが偶数の場合にのみ発生します。
N = 4の場合、私のカウントは少しずれているようです。ピーターテイラーとの議論では、六角形のエッジを通る線の対称性のみを持つタイルを検出していないようです。これは、(inversion)x(identity。)以外の操作の自己補数(反対称性)をテストしていないためと思われます。 )欠落している対称性が明らかになる場合があります。N = 4のデータの最初の行は次のようになります(c1で16少ない、C1で32多い)。
c1 224064/12=18672 C1 534/6=89
これにより、合計がPeterの回答およびhttps://oeis.org/A066931/a066931.txtに沿ったものになります。
現在の出力は次のとおりです。
N=1
non self-complement self-complement
c1 0/12= 0 C1 0/6= 0
c3 0/4 = 0 C3 0/2= 0
cs 0/6 = 0 CS 0/3= 0
c3v 2/2 = 1 C3V 0/1= 0
total =2 unique =1
non self-complement self-complement
N=2
c1 0/12= 0 C1 0/6= 0
c3 0/4 = 0 C3 0/2= 0
cs 12/6 = 2 CS 3/3= 1
c3v 4/2 = 2 C3V 1/1= 1
total =20 unique =6
N=3
non self-complement self-complement
c1 672/12=56 C1 0/6= 0
c3 4/4 = 1 C3 0/2= 0
cs 288/6 =48 CS 0/3= 0
c3v 16/2 = 8 C3V 0/1= 0
total =980 unique =113
N=4 (errors as discussed)
non self-complement self-complement
c1 224256/12=18688 C1 342/6=57
c3 64/4 =16 C3 2/2= 1
cs 8064/6 =1344 CS 54/3=18
c3v 64/2 =32 C3V 2/1= 2
total =232848 unique =20158
N=5
non self-complement self-complement
c1 266774112/12=22231176 C1 0/6= 0
c3 1100/4 =275 C3 0/2= 0
cs 451968/6 =75328 CS 0/3= 0
c3v 352/2 =176 C3V 0/1= 0
total =267227532 unique =22306955
予定リスト(更新済み)
現在のコードを整理します。
完了、多かれ少なかれ
現在のレイヤーの対称性チェックを実装し、前のレイヤーの対称性のパラメーターを渡します(最後のレイヤーが非対称かどうかをチェックするポイントはありません)。
完了、奇数Nの結果は公開データと一致
非対称図形のカウントを抑制するオプションを追加します(はるかに高速に実行されるはずです)
これは、再帰呼び出しに別の条件を追加することで実行できますif(s1 && m<n*3-2)f(m + 1,e+d,s1)
。N= 5の実行時間を5分から約1秒に短縮します。その結果、出力の最初の行は(全体の合計と同様に)合計ガベージになりますが、合計がOEISから既にわかっている場合は、少なくとも奇数Nの場合、非対称タイルの数を再構成できます。
しかし、Nであっても、自己補完的な非対称(c3v対称性による)ソリッドの数は失われます。この場合、1の正確に(N ** 3)/ 2セルを持つソリッド専用の別個のプログラムが役立つ場合があります。これが利用できる(そして正しくカウントされる)場合、N = 6を試すことが可能かもしれませんが、実行には長い時間がかかります。
セルのカウントを実装して、検索を最大(N ^ 3)/ 2キューブに減らします。
行われていない、節約はわずかであると予想される
正確に(N ^ 3)/ 2個の立方体を含むパターンの対称(相補ソリッド)チェックを実装します。
完了しましたが、省略されているようです。N= 4を参照してください。
非対称の数字から字句的に最も低い数字を選択する方法を見つけます。
節約はそれほど大きくないと予想されます。非対称の図形を抑制すると、このほとんどがなくなります。チェックされる唯一の反射は、y軸を通る平面です(xとzは、後で3を掛けて計算されます)。回転対称のみの図形は、両方の鏡像体でカウントされます。おそらく、1つだけがカウントされた場合、ほぼ2倍の速度で実行されるでしょう。
これを容易にするために、各レイヤーの座標がリストされる方法を改善する可能性があります(レイヤーの正確な中心に1のグループが含まれる可能性がある、6または3の縮退グループを形成します)。
興味深いですが、おそらくサイトには他にも質問があります。
N = 6
以上10 ^ 12の出力を与え、非建設的な解決策は、そこまでを取得するために、ほぼ確実に必要です。