具有自由n-多胺基的n X n正方形的不同平铺数目


17

几分钟前刚刚发布了最新的“不错的” OEIS序列A328020

带有自由n-多胺基的n X n正方形的不同平铺数目。

此序列将平铺计数到正方形的对称性。该序列有六个术语,但是我想看看这里的人是否可以进一步扩展它。

因为n=4有22个这样的网格,如OEIS的此图所示。 图片来源:Jeff Bowermaster,A328020(4)插图。A328020(4)

挑战

之前的挑战一样,此挑战的目标是按此顺序计算尽可能多的项,该项开始1, 1, 2, 22, 515, 56734于第n个项,即具有n个多氨基酸的n X n网格的平铺数。

只要您愿意,就可以运行您的代码。挑战的胜者将是发布序列中最多术语的用户,以及生成该序列的代码的用户。如果两个用户发布相同数量的条款,则以最早发布最后一个条款的人为准。


3
所以这是平方的模对称性?
彼得·泰勒

@PeterTaylor,是的。我已经在问题中消除了歧义。
彼得·卡吉

天真的,我想说第n个条目将使用number_of_fixed_n_polyominoes ^(n -1)个运算来计算。因此,对于n = 7,这将需要760 ^ 6≈2 ^ 57.4运算。您可能可以减少很多,但是首先要考虑的是很多
G. Sliepen

@Sliepen,我希望您可以通过回溯将其减少很多。特别是,有很多固定的多项式不能放置在角落里,有一次有效的四角放置的地方,有着难以言喻的限制什么可以被旁边放置它。
彼得·卡吉

@PeterKagey,你是对的。如果您已经放置了m个n-多米诺骨牌,而您选择了下一个位置尝试将多米诺骨牌放置在最坏的位置,那么您可以将其削减很多,那我认为这很有帮助。
G. Sliepen

Answers:


9

@Grimy代码的扩展名得到N = 8

这只是强调了@Grimy应该得到的赏金:

我可以通过扩展代码来修剪搜索树,以便在每个完成的多米诺骨牌之后检查剩余的可用空间是否未划分为大小不能被N整除的分量。

在原始代码中N = 7花费2m11s的机器上,这花费了1m4s,并且在33h46m中计算了N = 8。其结果是23437350133。

这是我作为差异添加的内容:

--- tilepoly.c  2019-10-11 12:37:49.676351878 +0200
+++ tilepolyprune.c     2019-10-13 04:28:30.518736188 +0200
@@ -51,6 +51,30 @@
     return 1;
 } 

+static int check_component_sizes(u64 occupied, u64 total){
+    u64 queue[N*N];
+    while (total<N*N){
+        u64 count = 1;
+        u64 start = ctz(~occupied);
+        queue[0] = start;
+        occupied |= 1ul << start;
+        for(u64 current=0; current<count; ++current){
+            u64 free_adjacent = adjacency_matrix[queue[current]] & ~occupied;
+            occupied |= free_adjacent;
+            while (free_adjacent){
+                u64 next = ctz(free_adjacent);
+                free_adjacent &= ~(1ul << next);
+                queue[count++] = next;
+            }
+        }
+        if (count % N){
+            return 0;
+        }
+        total += count;
+    }
+    return 1;
+}
+
 static void recurse(u64 mino, u64 cell, u64 occupied, u64 adjacent, u64 forbidden)
 {
     if (cell >= N) {
@@ -61,6 +85,9 @@
             return;
         }

+        if(!check_component_sizes(occupied,N*mino))
+            return;
+
         u64 next = ctz(~occupied);
         board[next] = mino;
         recurse(mino, 1, occupied | 1ul << next, adjacency_matrix[next], 0);

在线尝试!


很好
阿努什

现在我们需要的是多线程simd版本:)
Anush

1
哦,那真的很酷!我实际上考虑了这种优化,但是认为在合理的时间内达到N = 8还不够,因此我没有理会实现它。
Grimmy

14

C,7个学期

第七学期是19846102。(前六个是1、1、2、22、515、56734,如问题中所述)。

#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define N 7
#define ctz __builtin_ctzl

typedef uint64_t u64;

static u64 board[N*N] = { 0 };
static u64 adjacency_matrix[N*N] = { 0 };
static u64 count = 0;

static u64 check_symmetry()
{
    static const u64 symmetries[7][3] = {
        { 0,     +N, +1 },
        { N-1,   -1, +N },
        { N-1,   +N, -1 },
        { N*N-1, -1, -N },
        { N*N-1, -N, -1 },
        { N*N-N, +1, -N },
        { N*N-N, -N, +1 },
    };

    int order[N];

    for (u64 i = 0; i < 7; ++i) {
        u64 start = symmetries[i][0];
        u64 dcol = symmetries[i][1];
        u64 drow = symmetries[i][2];
        memset(order, 0xFF, N*sizeof(int));

        for (u64 row = 0, col = 0; col < N || (col = 0, ++row < N); ++col) {
            u64 base = board[col + N*row];
            u64 symmetry = board[start + dcol*col + drow*row];
            u64 lex = 0;

            while (order[lex] != symmetry && order[lex] != -1)
                ++lex;
            order[lex] = symmetry;

            if (lex < base)
                return 0;

            if (base < lex)
                break;
        }
    }

    return 1;
} 

static void recurse(u64 mino, u64 cell, u64 occupied, u64 adjacent, u64 forbidden)
{
    if (cell >= N) {
        ++mino;

        if (mino == N) {
            count += check_symmetry();
            return;
        }

        u64 next = ctz(~occupied);
        board[next] = mino;
        recurse(mino, 1, occupied | 1ul << next, adjacency_matrix[next], 0);
        return;
    }

    adjacent &= ~occupied & ~forbidden;
    while (adjacent) {
        u64 next = ctz(adjacent);
        adjacent &= ~(1ul << next);
        forbidden |= 1ul << next;
        board[next] = mino;
        recurse(mino, cell + 1, occupied | 1ul << next, adjacent | adjacency_matrix[next], forbidden);
    }
}

int main(void)
{
    for (u64 i = 0; i < N*N; ++i) {
        if (i % N)
            adjacency_matrix[i] |= 1ul << (i - 1);
        if (i / N)
            adjacency_matrix[i] |= 1ul << (i - N);
        if (i % N != N - 1)
            adjacency_matrix[i] |= 1ul << (i + 1);
        if (i / N != N - 1)
            adjacency_matrix[i] |= 1ul << (i + N);
    }

    recurse(0, 2, 3, 4 | 3 << N, 0);
    printf("%ld\n", count);
}

在线尝试!(对于N = 6,因为N = 7会超时。)

在我的机器上,N = 6花费了0.171s,N = 7花费了2m23s。N = 8将需要几个星期。


3
太好了!让我知道您是否想将其添加到OEIS中(这可能会导致您麻烦)或是否希望我添加它。
彼得·卡吉

@PeterKagey,请随时添加((
Grimmy

令人着迷的check_symmetry函数。由于我不熟悉该方法,请您简要说明一下?
约翰·里斯

1
@JohnRees它只是测试当前板在字典上是否符合其所有对称性。因此,在任何一组对称板中,仅计算一个:词典最小。
Grimmy

为了比逐一列举解决方案更好,需要某种中间相遇。问题在于,似乎没有任何方法可以拆分成重要的集群。例如,使用与该答案相同的规范顺序,放置3个六聚体,每个面罩平均可获得约3.7套六聚体。我得出结论,这种方法不太可能被击败。
彼得·泰勒
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.