自由なn-ポリオミノを持つn X n正方形の個別のタイルの数


17

最新の「素敵な」 OEISシーケンスであるA328020は、数分前に公開されました。

自由なn-ポリオミノを持つn X n正方形の個別のタイルの数。

このシーケンスは、正方形の対称性までのタイルをカウントします。シーケンスには6つの用語がありますが、ここの人々がさらに拡張できるかどうかを確認したいと思います。

ためn=4OEISからこのイメージに示すように、このようなグリッド22があります。 クレジット:Jeff Bowermaster、A328020(4)のイラスト。A328020(4)

チャレンジ

この前の課題と同様に、この課題の目標は、このシーケンスで可能な限り多くの項を計算することです1, 1, 2, 22, 515, 56734。n項は、nポリオミノを含むn X nグリッドのタイル数です。

好きなだけコードを実行します。このチャレンジの勝者は、シーケンスのほとんどの用語とそれを生成するコードを投稿するユーザーです。2人のユーザーが同じ数の用語を投稿すると、最後の用語を最も早く投稿した人が勝者となります。


3
これは、正方形のモジュロ対称性ですか?
ピーターテイラー

@PeterTaylor、そうです。質問でこれを明確にしました。
ピーターKagey

単純に言えば、n番目のエントリはnumber_of_fixed_n_polyominoes ^(n -1)演算を計算するために必要だと思います。したがって、n = 7の場合、760 ^ 6≈2 ^ 57.4の操作になります。...おそらく、その多く減らすことができ、それはで開始する大きな数だ
G. Sliepen

@Sliepen、バックトラックするだけでかなり削減できると期待しています。特に、角に配置できない固定多項式がたくさんあり、有効なポリオミノどこかに配置されると、その隣に配置できるものが大きく制約されます。
ピーターケイジー

@PeterKagey、あなたは正しい。既にm個のnポリオミノを配置しているので、次の位置を選択して、可能な限り最悪の位置にポリオミノを配置しようとすると、かなり削減できると思います。
G.スリーペン

回答:


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に到達するのに十分だとは思わなかったので、それを実装することはありませんでした。
グリムミー

14

C、7項

7番目の用語は19846102です。(最初の6つは、質問で述べたように、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.171秒かかり、N = 7は2m23秒かかりました。N = 8には数週間かかります。


3
これは素晴らしい!それをOEISに追加したい場合(その結果、自分でdoxxを実行する場合があります)、または追加したい場合はお知らせください。
ピーターカゲィ

@PeterKagey(それを追加すること自由に感じなさい:
Grimmy

魅力的なcheck_symmetry関数。私はアプローチに慣れていないので、簡単な説明をお願いします。
ジョンリース

1
@JohnRees現在のボードがそのすべての対称性に対して辞書式に≤であることを単純にテストします。したがって、対称的なボードのセットでは、正確に1つ、つまり辞書編集上の最小数がカウントされます。
グリムミー

ソリューションを1つずつ列挙するよりも良い結果を得るには、ある種の中間的な出会いが必要です。問題は、重要なクラスタリングを得るものを分割する方法がないように見えることです。たとえば、この回答と同じ標準的な順序を使用して、3つのヘキソミノを配置すると、マスクごとに平均で約3.7セットのヘキソミノが得られます。私は、このアプローチが破られる可能性は低いと結論付けています。
ピーターテイラー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.