C
介绍
正如David Carraher所评论的那样,分析六边形拼贴的最简单方法似乎是利用3维杨氏图的同构性,本质上是一个x,y正方形,其中填充了整数高度条,其z高度必须保持相同或增加接近z轴时。
我以一种算法为基础,该算法用于查找总数,该算法比已发布的算法更适合于对称计数,该算法基于对三个笛卡尔轴之一的偏向。
算法
我首先用1填充x,y和z平面的像元,而其余区域包含零。完成此操作后,我将逐层构建图案,其中每一层都包含与原点具有共同3D曼哈顿距离的像元。如果单元格下面的三个单元格也包含1,则该单元格只能包含1。如果其中任何一个包含0,则该单元格必须为0。
以这种方式建立图案的优势在于,每一层都围绕x = y = z线对称。这意味着可以独立检查每一层的对称性。
对称检查
实体的对称性如下:围绕x = y = z线旋转3倍->围绕六边形中心旋转3倍;关于包含x = y = z线的3个平面和每个轴x,y,z的3个x反射->关于穿过六边形角的线的反射。
这总共只增加了6倍的对称性。为了获得六边形的完全对称性,必须考虑另一种对称性。每个实体(从1构造)具有互补的实体(从0构造)。当N为奇数时,互补实体必须与原始实体不同(因为它们不可能具有相同数量的立方体)。然而,当互补实体旋转时,会发现它的2D表示为钻石拼贴,与原始实体相同(除了2倍对称操作)。在N为偶数的情况下,固体可能是自逆的。
这可以在问题的N = 2的示例中看到。如果从左侧看,第一个六边形看起来像是带有8个小立方体的实心立方体,而最后一个六边形看起来像是带有0个小立方体的空壳。如果从右边看,则相反。第3,第4和第5六角形以及第16、17和18六角形看起来包含2个或6个立方体,因此它们在3个维度上互补。它们通过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种对称形式中的任何一种(Schoenflies表示法)
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,我的计数似乎略有下降。在与Peter Taylor的讨论中,似乎我没有检测到仅具有穿过六边形边缘的直线对称性的平铺。大概是因为我没有针对除(反转)x(同一性)以外的操作测试自补(反对称)。针对操作数(反转)x(反射)和(反转)x(3倍旋转)的自补测试)可能会发现缺失的对称性。然后,我希望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分钟减少到大约一秒钟。结果,输出的第一行变成了总垃圾(总总数也是如此),但是如果已经从OEIS中得知了总垃圾,那么可以重新构造非对称平铺的数量,至少对于奇数N是可以的。
但是,即使是N,也会丢失自补体的不对称(根据c3v对称性)实体的数量。对于这种情况,可能需要一个单独的程序,该程序专门用于具有(N ** 3)/ 2个单元格正好为1的实体。使用此功能(并正确计数),可以尝试N = 6,但是运行将花费很长时间。
实现对单元格的计数,以减少对多达(N ^ 3)/ 2个立方体的搜索。
未完成,预计节省的成本很小
对包含正好(N ^ 3)/ 2个立方体的图案实施对称性(互补固体)检查。
完成,但似乎有遗漏,请参阅N = 4。
找到一种从不对称的数字中选择词法最低的数字的方法。
节省不会那么大。抑制不对称图形可以消除大多数情况。唯一检查的反射是通过y轴的平面(x和z随后乘以3计算得出。)仅具有旋转对称性的图形都以其两种对映体形式计数。如果只计算一个,则运行速度可能快将近两倍。
为了简化此过程,可能会改进列出每一层中的坐标的方式(它们由简并的6或3组组成,在该层的正中央可能有1组)。
有趣,但网站上可能还有其他问题需要探讨。
N = 6
输出的结果大于10 ^ 12,因此几乎肯定需要采用非建设性的解决方案。