¿Existe un algoritmo para calcular todas las posibles poliominós libres para un número n?

Suposiciones

Esta respuesta genera todas las poliominoas dimensionales libres [math] 2 [/ math] que contienen [math] n \ in \ mathbb {N} [/ math] celdas. En otras palabras, no cuenta dos veces los poliominoes congruentes.

Responder:

Los primeros valores se calcularon utilizando el siguiente código en los siguientes tiempos:

Tenga en cuenta que mis resultados coinciden con los de A000105 – OEIS. A continuación se muestran las celdas calculadas reales para diferentes poliominoes hasta [math] n = 5 [/ math]:

[matemáticas] n = 1 [/ matemáticas]:

(1, 1)

[matemáticas] n = 2 [/ matemáticas]:

(1, 1), (2, 1)

[matemáticas] n = 3 [/ matemáticas]:

(1, 1), (1, 2), (2, 1)

(1, 1), (2, 1), (3, 1)

[matemáticas] n = 4 [/ matemáticas]:

(1, 1), (1, 2), (2, 1), (2, 2)

(1, 1), (2, 1), (3, 1), (4, 1)

(1, 2), (2, 1), (2, 2), (3, 1)

(1, 1), (1, 2), (1, 3), (2, 1)

(1, 1), (2, 1), (2, 2), (3, 1)

[matemáticas] n = 5 [/ matemáticas]:

(1, 2), (2, 2), (3, 1), (3, 2), (4, 1)

(1, 1), (2, 1), (3, 1), (4, 1), (5, 1)

(1, 1), (1, 2), (1, 3), (2, 1), (2, 3)

(1, 1), (2, 1), (2, 2), (2, 3), (3, 1)

(1, 1), (2, 1), (3, 1), (3, 2), (4, 1)

(1, 3), (2, 1), (2, 2), (2, 3), (3, 1)

(1, 1), (1, 2), (1, 3), (2, 1), (3, 1)

(1, 3), (2, 1), (2, 2), (2, 3), (3, 2)

(1, 2), (1, 3), (2, 1), (2, 2), (3, 1)

(1, 2), (2, 1), (2, 2), (3, 1), (3, 2)

(1, 1), (2, 1), (3, 1), (4, 1), (4, 2)

(1, 2), (2, 1), (2, 2), (2, 3), (3, 2)

Código:

El siguiente script de Python utiliza programación dinámica. Específicamente, dado que se han encontrado todos los polyominoes para las células [math] n-1 [/ math], intenta agregar una celda adyacente a cada una de las celdas del borde de esos polyominoes. También almacena en caché todos los reflejos y rotaciones de los poliominoes encontrados, asegurando así que no contamos doblemente los poliominoes congruentes. Tenga en cuenta que este enfoque también puede extenderse para hacer [math] k [/ math] -dimensional polyominoes, para cualquier [math] k \ in \ mathbb {N} [/ math].

#! / usr / bin / python

de sys import argv
tiempo de importación

def main (argv):
maxCells = int (argv [1])
si maxCells <2:
raise Exception (“need (maxCells = argv [1])> = 2”)
print “Num Cells \ tPolyominoes \ tTime (s)”
poliomino = set ()
polyomino.add ((1, 1))
distinctPolyominoToEdgeCells = dict ()
distinctPolyominoToEdgeCells [frozenset (polyomino)] = poliomino
para numCells en rango (2, maxCells + 1):
startTime = time.time ()
distinctPolyominoToEdgeCells = \
getPolyominoes (distinctPolyominoToEdgeCells)
sTime = str (time.time () – startTime)
# print str (distinctPolyominoToEdgeCells)
print str (numCells) + “\ t” \
+ str (len (distinctPolyominoToEdgeCells)) + “\ t” + sTime

# #
# Dado todos los poliominoes válidos con celdas {“numCells” – 1}, devuelve
# el conjunto de poliominoes con celdas “numCells”.
# No hay dos poliominoes devueltos que sean congruentes entre sí.
# #
def getPolyominoes (distinctPolyominoToEdgeCells):
toReturn = dict ()
adyacenteCells = [(1, 0), (-1, 0), (0, 1), (0, -1)]
cachedPolyominoes = set ()
para anteriorPolyomino, edgeCells \
en distinctPolyominoToEdgeCells.iteritems ():
para edgeCell en edgeCells:
para adyacentesCélulas adyacentes:
newCell = (edgeCell [0] + adyacenteCell [0],
edgeCell [1] + adyacenteCell [1])
si newCell en anteriorPolyomino:
Hacer continuación
newPolyomino, maxXVal, maxYVal, newEdgeCells = \
nuevoNormalizedPolyomino (anteriorPolyomino,
nuevoCell,
edgeCells)
si nuevoPolyomino en cachéPolyominoes:
Hacer continuación
frozenNewPolyomino = frozenset (newPolyomino)
toReturn [frozenNewPolyomino] = newEdgeCells
cachedPolyominoes.add (frozenNewPolyomino)
cachedPolyominoes.add (frozenset (reflectVertical (newPolyomino, maxYVal)))
cachedPolyominoes.add (frozenset (reflectHorizontal (newPolyomino, maxXVal)))
para rotación en rango (3):
newPolyomino, maxXVal, maxYVal = rotateClockwise (newPolyomino, maxXVal)
cachedPolyominoes.add (frozenset (newPolyomino))
cachedPolyominoes.add (frozenset (reflectVertical (newPolyomino, maxYVal)))
cachedPolyominoes.add (frozenset (reflectHorizontal (newPolyomino, maxXVal)))
volver a Regresar

# #
# Devuelve True si la celda dada es una celda de borde en el poliomino dado.
# #
def isEdgeCell (poliomino, celular):
adyacenteCells = [(1, 0), (-1, 0), (0, 1), (0, -1)]
para adyacentesCélulas adyacentes:
otherCell = (adyacenteCell [0] + celda [0],
adyacenteCell [1] + celda [1])
Si es otro, no en poliomino:
volver verdadero
falso retorno

# #
# Obtenga el conjunto de nuevas celdas de borde, dado el nuevo poliomino,
# las celdas de borde antiguas y la celda recién agregada.
# #
def getNewEdgeCells (newPolyomino,
nuevoCell,
edgeCells):
adyacenteCells = [(1, 0), (-1, 0), (0, 1), (0, -1)]
if isEdgeCell (newPolyomino, newCell):
edgeCells.add (newCell)
para adyacentesCélulas adyacentes:
adyacenteToNuevoCell = (adyacenteCell [0] + nuevoCell [0],
adyacenteCell [1] + nuevoCell [1])
si adyacente a NewCell no está en edgeCells:
Hacer continuación
si no esEdgeCell (newPolyomino, adyacente a NewCell):
edgeCells.remove (adyacente a NewCell)
borde de retorno

# #
# Devuelve un poliomino normalizado, que se forma al reflejar
# el poliomino normalizado dado verticalmente alrededor del eje horizontal.
# #
def reflectVertical (polyomino, maxYVal):
newPolyomino = set ()
para celda en poliomino:
newPolyomino.add ((celda [0], -cell [1] + maxYVal + 1))
volver nuevoPolyomino

# #
# Devuelve un poliomino normalizado, que se forma al reflejar
# el poliomino normalizado dado horizontalmente alrededor del eje vertical.
# #
def reflectHorizontal (polyomino, maxXVal):
newPolyomino = set ()
para celda en poliomino:
newPolyomino.add ((- celda [0] + maxXVal + 1, celda [1]))
volver nuevoPolyomino

# #
# Devuelve un poliomino normalizado, que se forma girando
# el poliomino normalizado dado en sentido horario 90 grados.
# #
def rotateClockwise (polyomino, maxXVal):
newPolyomino = set ()
newMaxXVal = 1
maxYVal = 1
para celda en poliomino:
newXVal = cell [1]
newYVal = -cell [0] + maxXVal + 1
newPolyomino.add ((newXVal, newYVal))
si newXVal> newMaxXVal:
newMaxXVal = newXVal
si newYVal> maxYVal:
maxYVal = newYVal
return newPolyomino, newMaxXVal, maxYVal

# #
# Cree un nuevo poliomino normalizado a partir del poliomino normalizado dado,
# añadiéndole la nueva celda dada. También actualice las nuevas celdas de borde.
# #
def newNormalizedPolyomino (polyomino, newCell, edgeCells):
xModify = 1 si newCell [0] == 0 más 0
yModify = 1 si newCell [1] == 0 más 0
newPolyomino = set ()
newEdgeCells = set ()
maxXVal = 1
maxYVal = 1
para celda en poliomino:
modifiedCell = (celda [0] + xModify, celda [1] + yModify)
newPolyomino.add (modifiedCell)
si modificadoCell [0]> maxXVal:
maxXVal = modifiedCell [0]
si modificadoCell [1]> maxYVal:
maxYVal = modifiedCell [1]
si la celda en edgeCells:
newEdgeCells.add (modifiedCell)
modifiedNewCell = (newCell [0] + xModify, newCell [1] + yModify)
newPolyomino.add (modifiedNewCell)
si modificadoNewCell [0]> maxXVal:
maxXVal = modifiedNewCell [0]
si modificadoNewCell [1]> maxYVal:
maxYVal = modifiedNewCell [1]
return newPolyomino, maxXVal, maxYVal, getNewEdgeCells (newPolyomino,
modifiedNewCell,
newEdgeCells)

if __name__ == ‘__main__’:
principal (argv)

A000105 – OEIS proporciona un algoritmo de Mathematica que no es particularmente fácil de entender.

La búsqueda de fuerza bruta de todos los n cuadrados posibles rellenados en una cuadrícula [matemática] 2 ^ {(n ^ 2)} [/ matemática] es muy ineficiente. Un algoritmo aún simple pero mucho más eficiente es observar que cualquier poliomino con cuadrados [matemáticos] n + 1 [/ matemáticos] puede construirse a partir de uno con cuadrados [matemáticos] n [/ matemáticos]. Por lo tanto, tome una lista de todos los N-ominos de orden y agregue un cuadrado en cada posición desocupada adyacente a un cuadrado existente. Luego, valide que esto genera un omino previamente desconocido (N + 1) comprobando sus rotaciones (y simetrías espejo, si lo desea).

Un algoritmo muy simple e ineficiente es considerar una cuadrícula n por n. Todos los n-poliminoes deben caber en dicha cuadrícula.

Simplemente complete n-cuadrados en esta cuadrícula y luego verifique que esto sea un poliomino legal. Esto significa que está conectado, por lo que cada cuadrado rellenado está conectado al menos a otro cuadrado rellenado y puede pasar de un cuadrado de cuadrícula a otro.

Hay muchas simplificaciones que puede hacer al algoritmo, pero debería ser suficiente para comenzar. Probablemente también desee eliminar los casos que son traslaciones o rotaciones de casos existentes.