Số lượng các góc riêng biệt của một hình vuông n X n với các đa giác tự do


17

Trình tự OEIS "đẹp" mới nhất , A328020 , vừa được xuất bản vài phút trước.

Số lượng gạch riêng biệt của một hình vuông n X n với n-polyominoes miễn phí.

Trình tự này đếm nghiêng đến đối xứng của hình vuông. Trình tự có sáu điều khoản, nhưng tôi muốn xem liệu mọi người ở đây có thể mở rộng nó hơn nữa không.

Thí dụ

n=4có 22 lưới như vậy, như thể hiện trong hình ảnh này từ OEIS. Tín dụng: Jeff Bowermaster, Minh họa của A328020 (4).A328020 (4)

Thử thách

Giống như thử thách trước đó , mục tiêu của thử thách này là tính toán càng nhiều thuật ngữ càng tốt trong chuỗi này, bắt đầu 1, 1, 2, 22, 515, 56734và trong đó thuật ngữ thứ n là số độ nghiêng của lưới n X n với n-polyominoes.

Chạy mã của bạn miễn là bạn muốn. Người chiến thắng trong thử thách này sẽ là người dùng đăng hầu hết các điều khoản của chuỗi, cùng với mã của họ để tạo ra nó. Nếu hai người dùng đăng cùng một số điều khoản, thì bất cứ ai đăng kỳ hạn cuối cùng của họ đều thắng.


3
Vậy đây là đối xứng modulo của hình vuông?
Peter Taylor

@PeterTaylor, đúng vậy. Tôi đã định nghĩa điều này trong câu hỏi.
Peter Kagey

Chắc chắn tôi sẽ nói rằng mục thứ n sẽ lấy các phép toán number_of_fixed greepolyominoes ^ ( n -1) để tính toán. Vì vậy, với n = 7, sẽ có các thao tác 760 ^ 6 ≈ 2 ^ 57.4. Bạn có thể có thể cắt giảm rất nhiều, nhưng đó là một con số lớn để bắt đầu với ...
G. Sliepen

@Sliepen, tôi hy vọng rằng bạn có thể cắt giảm điều đó khá nhiều chỉ bằng cách quay lại. Đặc biệt, có rất nhiều đa thức cố định không thể được đặt trong góc và một khi polyomino hợp lệ được đặt ở đâu đó, nó sẽ hạn chế rất nhiều những gì có thể đặt bên cạnh nó.
Peter Kagey

@PeterKagey, bạn nói đúng. Tôi đoán nó có ích nếu, nếu bạn đã đặt m n-polyominoes, bạn chọn vị trí tiếp theo để cố gắng đặt một polyomino ở vị trí xấu nhất có thể, rằng bạn có thể cắt nó xuống rất nhiều.
G. Sliepen

Câu trả lời:


9

Một phần mở rộng cho mã @ Grimy được N = 8

Điều này chỉ nhấn mạnh rằng @Grimy xứng đáng nhận tiền thưởng:

Tôi có thể cắt tỉa cây tìm kiếm bằng cách mở rộng mã để kiểm tra, sau mỗi polyomino đã hoàn thành, không gian trống còn lại không được phân chia thành các thành phần có kích thước không chia hết cho N.

Trên máy có mã gốc mất 2m11s cho N = 7, quá trình này mất 1m4 và N = 8 được tính trong 33h46m. Kết quả là 23437350133.

Đây là bổ sung của tôi như là một khác biệt:

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

Hãy thử trực tuyến!


Điều này là rất tốt đẹp.
Anush

Tất cả những gì chúng ta cần bây giờ là một phiên bản simd đa luồng :)
Anush

1
Ôi thật tuyệt! Tôi thực sự đã xem xét tối ưu hóa này, nhưng không nghĩ rằng nó sẽ đủ để đạt N = 8 trong một thời gian hợp lý, vì vậy tôi đã không bận tâm đến việc thực hiện nó.
Grimmy

14

C, 7 điều khoản

Nhiệm kỳ thứ bảy là 19846102 . (Sáu đầu tiên là 1, 1, 2, 22, 515, 56734, như đã nêu trong câu hỏi).

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

Hãy thử trực tuyến! (với N = 6, vì N = 7 sẽ hết thời gian.)

Trên máy của tôi, N = 6 mất 0,171 và N = 7 mất 2m23. N = 8 sẽ mất một vài tuần.


3
Đây là điều tuyệt vời! Hãy cho tôi biết nếu bạn muốn thêm nó vào OEIS, điều này có thể dẫn đến việc bạn tự làm bản thân mình hoặc nếu bạn muốn tôi thêm nó.
Peter Kagey

@PeterKagey Xin vui lòng thêm nó (:
Grimmy

Chức năng check_symmetry hấp dẫn. Bạn có thể vui lòng giải thích ngắn gọn vì tôi không quen với cách tiếp cận này?
John Rees

1
@JohnRees Nó chỉ đơn giản kiểm tra rằng bảng hiện tại là từ vựng cho tất cả các đối xứng của nó. Do đó, trong bất kỳ tập hợp các bảng đối xứng, chính xác một bảng được tính: tối thiểu từ vựng.
Grimmy

Để làm tốt hơn là liệt kê từng giải pháp một, một số loại đáp ứng ở giữa là bắt buộc. Vấn đề là dường như không có bất kỳ cách nào để phân chia mọi thứ mà được phân cụm đáng kể. Ví dụ: sử dụng thứ tự chính tắc như câu trả lời này, đặt 3 hình lục giác tôi nhận được trung bình khoảng 3,7 bộ hexominos trên mỗi mặt nạ. Tôi kết luận rằng phương pháp này không có khả năng bị đánh bại.
Peter Taylor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.