Підрахунок полістрипів


18

Полістрипсис - це підмножина поліоміно, що відповідає наступним правилам:

  • кожен шматок складається з 1 або більше комірок
  • жодна клітина не може мати більше двох сусідів
  • клітини не повинні закривати отвір

Вільні поліоміно є виразними, коли жодне не є жорсткою трансформацією (переклад, обертання, відображення або відбиття ковзання) іншого (шматки, які можна підібрати і перевернути). Переклад, обертання, віддзеркалення або ковзання, що відображає вільне поліоміно, не змінюють своєї форми ( Вікіпедія )

Наприклад, існує 30 вільних гептастрипів (полістрипи довжиною 7). Ось усі вони, упаковані в сітку 14х15.

Гептастрипи

Кредитна графіка: Мирослав Вічер

Мета

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

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

  • n = 2 -> 1 (Існує лише одна можлива 2-полістилка з 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-polystrip і повертається явно). Блоки описують відносний напрямок: 0 - без змін; 1 - повернути вліво; 2 - повернути праворуч. Не має значення, в якому напрямку ви почнете, а лише вказати, куди слід поставити наступну клітинку. Тут може бути будь-яка кількість послідовних 0, але 1 і 2 завжди одиничні. Ця реалізація є частковою, оскільки вона не враховує отвори - рішення для n> 6 також рахують шматки з отворами.

Спробуйте в Інтернеті!


1
Відповідна OEIS.(Але не виключає дірок.)
Мартін Ендер

@ Martin Ender Дякую, я цього не знав.
Гален Іванов

2
Щоб переконатися, я припускаю, що якщо заповнити сітку 3х3 за винятком центру та одного кута, який також вважається отвором ( 101010у вашій примітці зразка)?
Євангеліє Тон

@Ton Hospel Так, саме так - це єдиний гептастріп-шматок з отвором.
Гален Іванов

1
Можливо, хороше запитання для math.SE
Йона

Відповіді:


12

Пітон 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)

Спробуйте в Інтернеті!

Зміни:

  • Накреслив 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, правда? :)
Ерік Аутгольфер

1
@Erik Цей рядок був напівжартовим :) Але так, рішення мене навіть дивує - я ніколи не сподівався, що я вийму ~ 36% зменшення від початкового подання.
Бубон


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)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.