Подсчет полистрипов

18

Полистрипсы - это подмножество полиомино, соответствующих следующим правилам:

  • каждая часть состоит из 1 или более клеток
  • ни одна клетка не может иметь более двух соседей
  • клетки не должны закрывать отверстие

Свободные полиомино отличаются, когда ни одно из них не является жестким преобразованием (перемещение, вращение, отражение или скользящее отражение) другого (фрагменты, которые можно подбирать и переворачивать). Перевод, вращение, отражение или скольжение, отражающее свободное полиомино, не меняет его форму ( Википедия )

Например, существует 30 свободных гептастрипов (полистрипов длиной 7). Вот все они, упакованные в сетку 14x15.

Heptastrips

Изображение предоставлено Мирославом Вичером

Цель

Напишите программу / функцию, которая принимает положительное целое число в nкачестве входных данных и перечисляет различные свободные n-полистрины.

  • n = 1 -> 1 (один квадрат)

  • n = 2 -> 1 (возможна только одна двухсторонняя полоса из 2 квадратов)

  • n = 3 -> 2 (один состоит из 3 квадратов, соединенных в линию, а другой - в форме буквы L)

  • n = 4 -> 3 (одна прямая, одна L-образная и одна Z-образная)

  • , , ,

Тестовые случаи:

n   polystrips

1   1
2   1
3   2
4   3
5   7
6   13
7   30
8   64
9   150
10  338
11  794
12  1836
13  4313
14  10067
15  23621

счет

Это , поэтому чем короче код, тем лучше. Я был бы очень признателен за подробные объяснения алгоритма и кода.

Частичная справочная реализация в J

Я решил описать каждую часть в «векторном» формате, и мне нужны только n-2 блока для описания части с n-полистрипами (есть только 1 2-полистрица, и она возвращается явно). Блоки описывают относительное направление: 0 - без изменений; 1 - повернуть налево; 2 - поверните направо. Неважно, в каком направлении вы начнете, нужно только указать, куда следует поместить следующую ячейку. Может быть любое количество последовательных нулей, но 1 и 2 всегда одинарные. Эта реализация является частичной, потому что она не учитывает дырки - решения для n> 6 также учитывают куски с дырками.

Попробуйте онлайн!

Гален Иванов
источник
1
Соответствующий OEIS. (Но не исключает дыр.)
Мартин Эндер
@ Мартин Эндер Спасибо, я этого не знал.
Гален Иванов
2
Просто чтобы быть уверенным, я предполагаю, что если вы заполняете сетку 3х3, за исключением центра и одного угла, который также считается дырой ( 101010в вашей выборочной записи)?
Тон Хоспел
@Ton Hospel Да, именно - это единственный кусок гепатастрип с отверстием.
Гален Иванов
1
Возможно, хороший вопрос по математике.
Иона

Ответы:

12

Python 3 , 480 433 406 364 309 299 295 байт

Выглядело как хороший момент для начала моей карьеры PPCG (или нет?).

def C(s):
 S,*a={''},0,1;n=d=r=1
 for c in s:d=c*d*1jor d;n+=d;a+=n,;r*=not{n}&S;x,*a=a;S|={x+t+u*1jfor t in A for u in A}
 return r
from itertools import*;A=-1,0,1;n,y=int(input())-2,0;x={*filter(C,product(*[A]*n))}
while x:s=x.pop();S=*(-u for u in s),;x-={s[::-1],S,S[::-1]}-{s};y+=1
print(y)

Попробуйте онлайн!

Редактирование:

  • Inlined Dи X, и немного подправлен в некоторых местах для гольфа.
  • Применено больше трюков, в основном связанных с множеством.
  • Изменен в форму программы и заменен на использование комплексных чисел вместо произвольных m. (Комплексные числа действительно сильны, но часто игнорируются в гольфе; адаптированы из решения xnor для другой задачи )
  • Изменили LFRстроковое представление на -1,0,1кортежи и пожертвовали временем выполнения для сумасшедшего сокращения байтов (!). Теперь решение теоретически правильно, но время ожидания до выдачи результата на 15.
  • Благодаря одной линии, благодаря Джонатану Фреху, я нашел гораздо лучшую альтернативу для расчета r. НАКОНЕЦ ПОД 300 БАЙТОВ !!!
  • Удивительно, но 1jможет придерживаться чего-либо еще, не путая синтаксический анализатор (-2B), и notимеет безумно низкий приоритет (-2B).

Устаревшая версия (480 байт):

def C(s):
 m=999;a=[0,1];n=d=1
 D={'F':{},'L':{1:m,m:-1,-1:-m,-m:1},'R':{1:-m,-m:-1,-1:m,m:1}}
 X=lambda x:{x+~m,x-m,x-m+1,x-1,x,x+1,x+m-1,x+m,x-~m}
 for c in s:
  d=D[c].get(d,d);n+=d;a+=n,
  if n in set().union(*map(X,a[:-3])):return 0
 return 1
def f(n):
 if n<3:return 1
 x={*'LF'}
 for _ in range(3,n):x={s+c for s in x for c in({*'LRF'}-{s[-1]})|{'F'}}
 y={*x}
 for s in x:
  if s in y:S=s.translate(str.maketrans('LR','RL'));y-={s[::-1],S,S[::-1]}-{s}
 return sum(map(C,y))

Попробуйте онлайн!

Раскрытое решение с комментариями:

t = str.maketrans('LR','RL')

# hole checking function
def check(s):
    m = 999   # (imaginary) board size enough to fit all generated polyominoes
    a = [0,1] # previous path
    n = 1     # current cell
    d = 1     # current direction
    # dict for direction change
    D = {'F':{}, 'L':{1:m, m:-1, -1:-m, -m:1}, 'R':{1:-m, -m:-1, -1:m, m:1}}
    # used to 'blur' all cells in path into 3x3
    X = lambda x: {x-m-1,x-m,x-m+1,x-1,x,x+1,x+m-1,x+m,x+m+1}
    for c in s:
        d = D[c].get(d,d) # change direction
        n += d            # move current cell
        # the polyomino has a hole if the current cell touches previous cells (including diagonally; thus the blurring function)
        if n in set().union(*map(X,a[:-2])): return False
        a.append(n)       # add current cell to the path
    return True

# main function
def f(n):
    if n < 3: return 1
    x = {*'LF'}
    # generate all polystrips using the notation similar to the reference
    for _ in range(3, n): x = {s+c for s in x for c in ({*'LRF'}-{s[-1]})|{'F'}}
    y = {*x}
    # remove duplicates (mirror, head-to-tail, mirror of head-to-tail) but retain self
    for s in x:
        if s in y:
            S = s.translate(t)
            y -= {s[::-1], S, S[::-1]} - {s}
    # finally filter out holey ones
    return sum(map(check,y))

Попробуйте онлайн!

m = 999выбран потому, что для подсчета всего требуется экспоненциальное время, а для расчета уже требуется ~ 8 с n = 1..15. Может быть, это нормально, чтобы сохранить 1 байт, используя вместо 99. Нам это больше не нужно, и теперь он гарантированно будет правильным для произвольного размера ввода благодаря встроенному комплексному числу.

фонтанчик для питья
источник
5
Добро пожаловать в PPCG! Определенно впечатляющий способ начать карьеру PPCG. :)
Мартин Эндер
3
Добро пожаловать в PPCG и спасибо за это решение! Я уже сдался, ожидая увидеть решение :)
Гален Иванов
3
Выглядело как хороший момент для начала моей карьеры PPCG (или нет?) . Что ж, это удивительно короткое решение для большинства из нас, о котором большинство из нас даже не подозревало бы, даже версия без заглядывания выглядит удивительно простой, но, может быть, это обычный способ начать карьеру PPCG, верно? :)
Эрик Outgolfer
1
@Erik Эта строка была наполовину шуткой :) Но да, решение даже удивляет меня - я никогда не ожидал, что получу сокращение на ~ 36% от первоначального предложения.
Bubbler
1
Возможные 303 байта .
Джонатан Фрех
4

APL (Dyalog Unicode) , 70 65 байт

+/{∧/2≤|(⊢-¯3↓¨,\)+\0 1\0j1*⍵}¨∪{(⊃∘⍋⊃⊢)(⊢,⌽¨)⍵(-⍵)}¨2-,⍳2↓⎕⍴3

Попробуйте онлайн!

Полная версия программы с кодом ниже, благодаря Adám.


APL (Dyalog Unicode) , 70 байт

{+/{∧/2≤|(⊢-¯3↓¨,\)+\0 1\0j1*⍵}¨∪{(⊃∘⍋⊃⊢)(⊢,⌽¨)⍵(-⍵)}¨2-,⍳3⍴⍨0⌈⍵-2}

Попробуйте онлайн!

Как это устроено

Код выше эквивалентен следующему определению:

gen←{2-,⍳3⍴⍨0⌈⍵-2}
canonicalize←{(⊃∘⍋⊃⊢)(⊢,⌽¨)⍵(-⍵)}
test←{(∧/⊢{∧/2≤|⍺-¯3↓⍵}¨,\)+\0 1\0j1*⍵}
{+/test¨∪canonicalize¨gen⍵}

Это работает как решение Python, но в другом порядке. Это generates LFR-strips длины n-2, canonicalizeс каждой полосой, принимает Nique полоски, testс каждой полоской , если она затрагивает себя (1 , если не касаясь, 0 в противном случае), и суммирует +/на логический результате.

gen

{2-,⍳3⍴⍨0⌈⍵-2}
{            }   ⍵←input number n
        0⌈⍵-2    xmax(0, n-2)
     3⍴⍨         x copies of 3
   ,⍳            multi-dimensional indexes; x-th cartesian power of [1,2,3]
                 (`⍳` gives x-dimensional hypercube; `,` flattens it)
 2-              compute 2-k for each k in the array

 in each strip, ¯1, 0, 1 corresponds to R, F, L respectively

canonicalize

{(⊃∘⍋⊃⊢)(⊢,⌽¨)⍵(-⍵)}
{                  }   ⍵←single strip
              ⍵(-⍵)    nested array of  and its LR-flip
        (⊢,⌽¨)         concatenate their head-to-tail flips to the above
 (⊃∘⍋  )               find the index of the lexicographically smallest item
     ⊃⊢                take that item

test

{(∧/⊢{∧/2≤|⍺-¯3↓⍵}¨,\)+\0 1\0j1*⍵}
{                                  }   ⍵←single strip
                              0j1*⍵    power of i; direction changes
                            ×\         cumulative product; directions
                        0 1,     initial position(0) and direction(1)
                      +\         cumulative sum; tile locations
 (  ⊢{           }¨,\)    test with current tile(⍺) and all tiles up to ⍺(⍵):
             ¯3↓⍵         x←⍵ with last 3 tiles removed
           ⍺-             relative position of each tile of x from 
        2≤|               test if each tile of x is at least 2 units away
      ∧/                  all(...for each tile in x)
  ∧/         all(...for each position in the strip)
фонтанчик для питья
источник
-5
Адам