Кількість чітких вирівнювань n X n квадрата з вільними n-поліомінами


17

Найновіша «приємна» послідовність OEIS - A328020 , була опублікована лише кілька хвилин тому.

Кількість чітких вирівнювань n X n квадрата з вільними n-поліомінами.

Ця послідовність підраховує нахили до симетрії квадрата. Послідовність має шість термінів, але я хотів би побачити, чи можуть люди тут продовжити її.

Приклад

Бо n=4існує 22 таких сітки, як показано на цьому зображенні від OEIS. Кредит: Джефф Бауермайстер, ілюстрація A328020 (4).A328020 (4)

Виклик

Як і цей попередній виклик , мета цієї задачі - обчислити якомога більше термінів у цій послідовності, яка починається 1, 1, 2, 22, 515, 56734і де n-й член - це кількість нахилів сітки n X n з n-поліоміно.

Запускайте код так довго, як хочете. Переможцем цього виклику стане користувач, який опублікує більшість умов послідовності, а також їх код для його генерування. Якщо два користувачі розміщують однакову кількість термінів, той, хто опублікує свій останній термін, виграє.


3
Так це модульна симетрія квадрата?
Пітер Тейлор

@PeterTaylor, саме так. Я не розігрував це в питанні.
Пітер Кагей

Наївно я хотів би сказати , що n - й вхід буде приймати number_of_fixed_n_polyominoes ^ ( п операції -1) для обчислення. Отже, для n = 7, це займе 760 ^ 6 ≈ 2 ^ 57.4 операцій. Ви можете, мабуть, скоротити це дуже багато, але для початку це велика кількість ...
Г. Сліпен

@Sliepen, я очікую, що ви можете скоротити це досить багато, просто за допомогою зворотного треку. Зокрема, є багато постійних поліноми , які не можуть бути розміщені в куті, і коли дійсний Полімін буде розміщений де - то, це дуже обмежує то , що може бути вміщено поруч з ним.
Пітер Кагей

@PeterKagey, ти маєш рацію. Я думаю, це допомагає, якщо, враховуючи, що ви вже розмістили m n-поліоміно, ви виберете наступне положення, щоб спробувати помістити поліоміно в найгірше можливе положення, щоб ви могли його скоротити.
Г. Сліпен

Відповіді:


9

Розширення до коду @ Grimy отримує N = 8

Це просто підкреслює, що @Grimy заслуговує на винагороду:

Я міг би обрізати дерево пошуку, розширивши код, щоб перевірити, після кожного готового поліоміно, що залишився вільний простір не розділений на компоненти розміру, що не ділиться на N.

На машині, де початковий код зайняв 2m11s за N = 7, це займає 1m4s, а N = 8 було обчислено в 33h46m. Результат - 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 :)
Ануш

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,171 с, а N = 7 займало 2 м23. N = 8 зайняло б кілька тижнів.


3
Це чудово! Повідомте мене, чи хочете ви додати його до ОЕІС, що може призвести до того, що ви змусите себе, або якщо ви хочете, щоб я його додав.
Пітер Кагей

@PeterKagey Будь ласка, додайте його (:
Grimmy

Захоплююча функція перевірки симетричності. Скажіть, будь ласка, коротке пояснення, оскільки я не знайомий з підходом?
Джон Різ

1
@JohnRees Він просто перевіряє, що поточна плата лексикографічно ≤ для всіх її симетрій. Таким чином, у будь-якому наборі симетричних дощок враховується рівно одна: лексикографічний мінімум.
Гріммі

1
Щоб зробити краще, ніж перераховувати рішення по одному, потрібна якась зустріч у середині. Проблема полягає в тому, що, мабуть, не існує способу поділу речей, що набуває значної групування. Наприклад, використовуючи те ж канонічне впорядкування, що і ця відповідь, розміщуючи 3 гексоміно, я отримую в середньому близько 3,7 набору гексоміносів на маску. Я роблю висновок, що цей підхід, швидше за все, не обіграє.
Пітер Тейлор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.