我保证,这将是我最后一次挑战迪阿美的倾斜(至少在一段时间内)。好的一面是,这个挑战与ASCII艺术没有任何关系,也不是一个代码高尔夫,所以这实际上是完全不同的。
因此,作为提醒,每个六边形都可以用三颗不同的钻石命名:

一个有趣的问题是,对于给定的六边形大小,这些倾斜物中有多少存在。这些数字似乎已经相当深入地研究过,可以在OEIS A008793中找到。
然而,如果我们问到旋转和反射有多少个倾斜点,问题就会变得更加棘手。例如,对于边长N= 2,存在以下20个加法:
____ ____ ____ ____ ____ ____ ____ ____ ____ ____
/\_\_\ /\_\_\ /\_\_\ /\_\_\ /_/\_\ /_/\_\ /\_\_\ /_/\_\ /_/\_\ /_/\_\
/\/\_\_\ /\/_/\_\ /\/_/_/\ /\/_/\_\ /\_\/\_\ /\_\/_/\ /\/_/_/\ /\_\/\_\ /\_\/_/\ /_/\/\_\
\/\/_/_/ \/\_\/_/ \/\_\_\/ \/_/\/_/ \/\_\/_/ \/\_\_\/ \/_/\_\/ \/_/\/_/ \/_/\_\/ \_\/\/_/
\/_/_/ \/_/_/ \/_/_/ \_\/_/ \/_/_/ \/_/_/ \_\/_/ \_\/_/ \_\/_/ \_\/_/
____ ____ ____ ____ ____ ____ ____ ____ ____ ____
/_/_/\ /\_\_\ /_/\_\ /_/_/\ /_/\_\ /_/\_\ /_/_/\ /_/_/\ /_/_/\ /_/_/\
/\_\_\/\ /\/_/_/\ /_/\/_/\ /\_\_\/\ /\_\/_/\ /_/\/_/\ /_/\_\/\ /\_\_\/\ /_/\_\/\ /_/_/\/\
\/\_\_\/ \/_/_/\/ \_\/\_\/ \/_/\_\/ \/_/_/\/ \_\/_/\/ \_\/\_\/ \/_/_/\/ \_\/_/\/ \_\_\/\/
\/_/_/ \_\_\/ \_\/_/ \_\/_/ \_\_\/ \_\_\/ \_\/_/ \_\_\/ \_\_\/ \_\_\/ 但其中许多在旋转和反射下是相同的。如果考虑到这些对称性,只剩下6种不同的倾斜现象:
____ ____ ____ ____ ____ ____
/\_\_\ /\_\_\ /\_\_\ /_/\_\ /_/\_\ /_/\_\
/\/\_\_\ /\/_/\_\ /\/_/_/\ /\_\/_/\ /\_\/_/\ /_/\/\_\
\/\/_/_/ \/\_\/_/ \/\_\_\/ \/\_\_\/ \/_/\_\/ \_\/\/_/
\/_/_/ \/_/_/ \/_/_/ \/_/_/ \_\/_/ \_\/_/
2 2 6 6 1 3其中数字表示每个瓷砖的多重性。请注意,对于较大的六边形,也有多重数为4和12的倾斜。
看来,向对称方向倾斜的次数没有得到充分的研究。OEIS条目A066931只列出以下五个术语:
1, 1, 6, 113, 20174其中第一个项表示边长N = 0,最后一个项表示边长N = 4。
我相信我们还能做得更好!
您的任务是计算给定边长的倾斜数。
我是最快代码。您的分数将是最高边长的N,您的代码将在30分钟内在我的机器上产生正确的结果。在领带的情况下,我将接受提交的结果,为该N最快。
像往常一样,你不能硬编码结果,你已经知道,以赢得平局.求解N = 3的算法应与求解N = 5的算法相同。
您提交的内存不得超过4GB。如果你的操作接近这个极限,我会给你一些回旋余地,但是如果你一直高于这个限制,或者如果你明显地超过了这个极限,我不会把你提交的N计算在内。
我将测试所有的提交在我的Windows 8机器,所以确保您的语言选择是免费提供在Windows上。唯一的例外是Mathematica (因为我碰巧有它的许可证)。请包括有关如何编译/运行代码的说明。
当然,你可以在你自己的时间内计算更多的术语(对于科学,以及其他人对照他们的数字),但是你的答案的分数将在30分钟内决定。
发布于 2015-06-12 22:44:47
六边形的对称群是12阶的二面体群,它是由60度旋转和一个镜面翻转在直径之间产生的。它有16个子群,但其中一些子群是非平凡共轭群(只有反射的子群有3个轴的选择),因此,六边形的平铺可以有10个根本不同的对称:

三角格的一个子集的金刚石倾斜数可以是作为行列式计算,所以我最初的方法是为六边形的每个对称建立一个行列式,计算出至少有这些对称的倾斜数;然后在其偏序集的关联代数中使用M bius反演(基本上是包含排除原理的推广),计算出对称群恰好是这10个情形中每一个的倾斜数。然而,有些对称有着令人讨厌的边缘条件,所以我不得不对许多决定因素进行指数相加。幸运的是,为n < 10获得的值为我提供了足够的数据,能够识别出OEIS中的相关序列,并将一个封闭形式(对于允许有限乘积的“封闭”值)拼凑在一起。在正式写作中有一些关于序列的讨论,以及对证明的引用,这是我准备用来证明OEIS序列更新的理由。
一旦处理了重复计算,结果是十个值中有四个完全抵消了,所以我们只需要计算剩下的六个,然后做加权和。
在我的机器上,这段代码用于N=1000所需的时间不到30秒。
import java.math.BigInteger;
public class OptimisedCounter {
private static int[] minp = new int[2];
public static void main(String[] args) {
if (args.length > 0) {
for (String arg : args) System.out.println(count(Integer.parseInt(arg)));
}
else {
for (int n = 0; n < 16; n++) {
System.out.format("%d\t%s\n", n, count(n));
}
}
}
private static BigInteger count(int n) {
if (n == 0) return BigInteger.ONE;
if (minp.length < 3*n) {
int[] wider = new int[3*n];
System.arraycopy(minp, 0, wider, 0, minp.length);
for (int x = minp.length; x < wider.length; x++) {
// Find the smallest prime which divides x
for (wider[x] = 2; x % wider[x] != 0; wider[x]++) { /* Do nothing */ }
}
minp = wider;
}
BigInteger E = countE(n), R2 = countR2(n), F = countF(n), R3 = countR3(n), R = countR(n), FR = countFR(n);
BigInteger sum = E.add(R3);
sum = sum.add(R2.add(R).multiply(BigInteger.valueOf(2)));
sum = sum.add(F.add(FR).multiply(BigInteger.valueOf(3)));
return sum.divide(BigInteger.valueOf(12));
}
private static BigInteger countE(int n) {
int[] w = new int[3*n];
for (int i = 0; i < n; i++) {
for (int j = i + 1; j <= i + n; j++) w[j]--;
for (int j = i + n + 1; j <= i + 2*n; j++) w[j]++;
}
return powerProd(w);
}
private static BigInteger countR2(int n) {
int[] w = new int[3*n];
for (int i = 0; i < n; i++) {
w[3*i+2]++;
for (int j = 3*i + 1; j <= 2*i + n + 1; j++) w[j]--;
for (int j = 2*i + n + 1; j <= i + n + n; j++) w[j]++;
}
return powerProd(w);
}
private static BigInteger countF(int n) {
int[] w = new int[3*n];
for (int i = 0; i < n; i++) {
for (int j = 2*i + 1; j <= 2*i + n; j++) w[j]--;
for (int j = i + n + 1; j <= i + 2*n; j++) w[j]++;
}
return powerProd(w);
}
private static BigInteger countR3(int n) {
if ((n & 1) == 1) return BigInteger.ZERO;
return countE(n / 2).pow(2);
}
private static BigInteger countR(int n) {
if ((n & 1) == 1) return BigInteger.ZERO;
int m = n / 2;
int[] w = new int[3*m-1];
for (int i = 0; i < m; i++) {
for (int j = 1; j <= 3*i+1; j++) w[j] += 2;
for (int j = 1; j <= i + m; j++) w[j] -= 2;
}
return powerProd(w);
}
private static BigInteger countFR(int n) {
if ((n & 1) == 1) return BigInteger.ZERO;
int m = n / 2;
int[] w = new int[3*n-2];
for (int j = 1; j <= m; j++) w[j]--;
for (int j = 2*m; j <= 3*m-1; j++) w[j]++;
for (int i = 0; i <= 2*m-3; i++) {
for (int j = i + 2*m + 1; j <= i + 4*m; j++) w[j]++;
for (int j = 2*i + 3; j <= 2*i + 2*m + 2; j++) w[j]--;
}
return powerProd(w);
}
private static BigInteger powerProd(int[] w) {
BigInteger result = BigInteger.ONE;
for (int x = w.length - 1; x > 1; x--) {
if (w[x] == 0) continue;
int p = minp[x];
if (p == x) result = result.multiply(BigInteger.valueOf(p).pow(w[p]));
else {
// Redistribute it. This should ensure we avoid negatives.
w[p] += w[x];
w[x / p] += w[x];
}
}
return result;
}
}https://codegolf.stackexchange.com/questions/51266
复制相似问题