Polystrips zählen

18

Polystreifen sind eine Untergruppe von Polyominoen, die den folgenden Regeln entsprechen:

  • Jedes Stück besteht aus einer oder mehreren Zellen
  • Keine Zelle kann mehr als zwei Nachbarn haben
  • Die Zellen sollten kein Loch einschließen

Freie Polyominoes unterscheiden sich, wenn keines eine starre Transformation (Translation, Rotation, Reflektion oder Gleitreflexion) eines anderen ist (Stücke, die aufgenommen und umgedreht werden können). Das Verschieben, Drehen, Reflektieren oder Gleiten eines freien Polyominos ändert seine Form nicht ( Wikipedia )

Zum Beispiel gibt es 30 freie Heptastrips (Polystrips mit der Länge 7). Hier sind alle in einem 14x15-Raster verpackt.

Heptastrips

Bildnachweis: Miroslav Vicher

Tor

Schreiben Sie ein Programm / eine Funktion, die eine positive Ganzzahl nals Eingabe verwendet und die verschiedenen freien nStreifen auflistet.

  • n = 1 -> 1 (Ein einzelnes Quadrat)

  • n = 2 -> 1 (Es gibt nur einen möglichen 2-Polystreifen aus 2 Quadraten)

  • n = 3 -> 2 (Eines besteht aus 3 in einer Linie verbundenen Quadraten und das andere ist L-förmig)

  • n = 4 -> 3 (eine gerade, eine L-förmige und eine Z-förmige)

  • . . .

Testfälle:

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

Wertung

Das ist , also ist der kürzere Code besser. Ich würde mich sehr über detaillierte Erklärungen des Algorithmus und des Codes freuen.

Teilweise Referenzimplementierung in J

Ich habe beschlossen, jedes Stück im "Vektor" -Format zu beschreiben, und ich benötige nur n-2 Blöcke, um ein n-Polystrip-Stück zu beschreiben (es gibt nur 1 2-Polystrip und es wird explizit zurückgegeben). Die Blöcke beschreiben die relative Richtung: 0 - keine Änderung; 1 - links abbiegen; 2 - rechts abbiegen. Es spielt keine Rolle, in welche Richtung man startet, sondern nur, um anzuzeigen, wo die nächste Zelle platziert werden soll. Es kann eine beliebige Anzahl aufeinanderfolgender Nullen geben, aber Einsen und Zweisen sind immer einzeln. Diese Implementierung ist teilweise, da sie die Löcher nicht berücksichtigt - Lösungen für n> 6 zählen auch die Teile mit Löchern.

Probieren Sie es online!

Galen Ivanov
quelle
1
Relevantes OEIS. (Aber schließt Löcher nicht aus.)
Martin Ender
@ Martin Ender Danke, ich wusste es nicht.
Galen Ivanov
2
Nur um sicher zu gehen, gehe ich davon aus, dass wenn Sie ein 3x3-Raster mit Ausnahme der Mitte und einer Ecke füllen, dies auch als Loch zählt ( 101010in Ihrer Beispielnotation)?
Ton Hospel
@Ton Hospel Ja, genau - dies ist das einzige Heptastrip-Stück mit einem Loch.
Galen Ivanov
1
Vielleicht eine gute Frage für math.SE
Jonah

Antworten:

12

Python 3 , 480 433 406 364 309 299 295 Bytes

Sah nach einem guten Zeitpunkt aus, um meine PPCG-Karriere zu beginnen (oder nicht?).

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)

Probieren Sie es online!

Bearbeitungen:

  • Inline Dund X, und an einigen Golfplätzen ein wenig optimiert.
  • Wendet mehr Tricks an, hauptsächlich satzbezogene.
  • Geändert in Programmform und geändert, um komplexe Zahlen anstelle von willkürlichen Zahlen zu verwenden m. (Komplexe Zahlen sind wirklich ein starkes, aber oft ignoriertes Golf-Feature; angepasst von der Lösung von xnor für eine andere Herausforderung )
  • Die LFRZeichenfolgendarstellung wurde in -1,0,1Tupel geändert und die Ausführungszeit für die verrückte Reduzierung der Bytezahl (!) Geopfert. Jetzt ist die Lösung theoretisch korrekt, es treten jedoch Zeitüberschreitungen auf, bevor das Ergebnis für 15 ausgegeben wird.
  • Dank Jonathan Frech habe ich die Schleife eingeklinkt, dann habe ich die viel bessere Alternative zum Rechnen gefunden r. ENDLICH UNTER 300 BYTES !!!
  • Erstaunlicherweise 1jkann man sich an nichts anderes halten, ohne den Parser (-2B) zu verwirren, und nothat eine wahnsinnig niedrige Priorität (-2B).

Veraltete Version (480 Bytes):

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

Probieren Sie es online!

Ungolfed Lösung mit Kommentaren:

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

Probieren Sie es online!

m = 999wird gewählt, weil es exponentiell lange dauert, alles zu zählen, und es bereits ~ 8s dauert, um zu berechnen n = 1..15. Vielleicht ist es in Ordnung, 1 Byte zu speichern, indem Sie stattdessen 99 verwenden. Wir brauchen das nicht mehr und jetzt ist es dank der integrierten komplexen Zahl garantiert für jede beliebige Eingabegröße richtig.

Bubbler
quelle
5
Willkommen bei PPCG! Auf jeden Fall eine beeindruckende Art und Weise, Ihre PPCG-Karriere zu beginnen. :)
Martin Ender
3
Willkommen bei PPCG und vielen Dank für diese Lösung! Ich hatte bereits aufgegeben, eine Lösung zu erwarten :)
Galen Ivanov
3
Sah nach einem guten Zeitpunkt aus, um meine PPCG-Karriere zu beginnen (oder nicht?) . Nun, dies ist eine überraschend kurze Lösung, die die meisten von uns nicht einmal für möglich halten würden. Selbst die ungolfed Version sieht überraschend einfach aus. :)
Erik der Outgolfer
1
@Erik Diese Zeile war ein halber Scherz :) Aber ja, die Lösung ist sogar überraschend für mich - ich hätte nie erwartet, dass ich ~ 36% Reduktion von der ursprünglichen Einreichung herausnehme.
Bubbler
1
Mögliche 303 Bytes .
Jonathan Frech
4

APL (Dyalog Unicode) , 70 bis 65 Byte

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

Probieren Sie es online!

Vollständige Programmversion des folgenden Codes, danke an Adám.


APL (Dyalog Unicode) , 70 Byte

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

Probieren Sie es online!

Wie es funktioniert

Der obige Code entspricht der folgenden Definition:

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

Dies funktioniert wie die Python-Lösung, jedoch in einer anderen Reihenfolge. Es genlöscht LFR-Längenstreifen n-2, canonicalizes jeden Streifen, nimmt spezielle Streifen, tests jeden Streifen, wenn er sich selbst berührt (1, wenn er sich nicht berührt, 0, wenn er sich nicht berührt), und summiert +/das Boolesche Ergebnis.

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)
Bubbler
quelle
-5
Adám