유리 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-polyominoes가있는 n x n 격자의 타일링 수입니다.

원하는 기간 동안 코드를 실행하십시오. 이 도전의 승자는 시퀀스의 가장 많은 용어를 코드와 함께 게시하여 생성하는 사용자입니다. 두 명의 사용자가 같은 수의 용어를 게시하면 마지막 용어를 가장 먼저 게시 한 사람이 승리합니다.


3
이것이 사각형의 모듈로 대칭입니까?
피터 테일러

@PeterTaylor, 맞습니다. 나는 질문에서 이것을 명확히했다.
피터 카게이

순진하게 나는 n 번째 항목이 number_of_fixed_n_polyominoes ^ ( n -1) 연산을 계산 해야한다고 말하고 싶습니다 . 따라서 n = 7의 경우 760 ^ 6 ≈ 2 ^ 57.4 연산이 필요합니다. 아마 많이 삭감 할 수는 있지만 시작
하기에는

@ Sliepen, 나는 당신이 단지 역 추적에 의해 그것을 많이 줄일 수 있다고 기대합니다. 특히, 구석에 놓을 수없는 고정 된 다항식이 많이 있으며, 유효한 폴리 노 미노 어딘가에 놓이면 옆에 놓을 수있는 것을 크게 제한합니다.
피터 카게이

@PeterKagey, 당신 말이 맞아요. 이미 m n-polyominoes를 배치했다면 다음 위치를 선택하여 polyomino를 최악의 위치에 배치하려고 시도하면 많이 줄일 수 있습니다.
G. Sliepen

답변:


9

@Grimy 코드의 확장은 N = 8을 얻습니다.

이것은 @Grimy에게 현상금이 필요하다는 것을 강조합니다.

각 완료된 polyomino 후에 남은 여유 공간이 N으로 나눌 수없는 크기의 구성 요소로 분할되지 않았는지 확인하기 위해 코드를 확장하여 검색 트리를 정리할 수 있습니다.

원래 코드가 N = 7 인 경우 2m11s 인 기계에서는 1m4s가 걸리고 N = 8은 33h46m에 계산되었습니다. 결과는 23437350133입니다.

diff로 추가 한 내용은 다음과 같습니다.

--- 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);

온라인으로 사용해보십시오!


이것은 매우 좋군요.
Anush

우리가 지금 필요한 것은 멀티 스레드 시뮬레이션 버전입니다. :)
Anush

1
정말 멋지다! 실제로이 최적화를 고려했지만 적절한 시간에 N = 8에 도달하기에 충분하지 않다고 생각했기 때문에 구현을 귀찮게하지 않았습니다.
Grimmy

14

C, 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에 추가하고 싶으면 알려주십시오. 이로 인해 스스로가 떨 리거나 추가 할 수 있습니다.
피터 카게이

@PeterKagey 자유롭게 추가하십시오 (:
Grimmy

매혹적인 check_symmetry 함수. 접근 방식에 익숙하지 않은 간단한 설명을 해 주시겠습니까?
John Rees

1
@JohnRees 단순히 현재 보드가 사 전적으로 모든 대칭에 ≤인지 테스트합니다. 따라서 모든 대칭 보드 세트에서 사전 식 최소값이 정확히 1 개로 계산됩니다.
Grimmy

솔루션을 하나씩 열거하는 것보다 낫게하려면 중간 정도의 모임이 필요합니다. 문제는 중요한 클러스터링을 얻는 것들을 나눌 수있는 방법이 없다는 것입니다. 예를 들어이 답변과 동일한 정식 순서를 사용하여 3 개의 헥소 미노를 배치하면 마스크 당 평균 약 3.7 세트의 헥소 미노를 얻습니다. 나는이 접근법이 맞지 않을 것이라고 결론 지었다.
피터 테일러
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.