Вопрос конкурса по программированию: Подсчет Поломино - PullRequest
9 голосов
/ 10 января 2011

Пожалуйста, посмотрите мой собственный ответ, я думаю, что сделал это!


Привет

Примером вопроса для соревнования по программированию было написание программы, которая выяснит, сколько полиоминиумов возможно при данном количестве камней.

Таким образом, для двух камней (n = 2) есть только один polyominos:

XX

Вы можете подумать, что это второе решение:

X
X

Но это не так. Полиномы не являются уникальными, если вы можете повернуть их.

Итак, для 4 камней (n = 4) существует 7 решений:

X
X   XX   X    X     X   X
X   X    XX   X    XX   XX   XX
X   X    X    XX   X     X   XX

Приложение должно быть в состоянии найти решение для 1 <= n <=10

PS: использование списка полиамино в Википедии не допускается;)

РЕДАКТИРОВАТЬ: Конечно, вопрос: как это сделать в Java, C / C ++, C #


Я начал этот проект на Java. Но потом мне пришлось признать, что я не знал, как строить многозначности, используя эффективный алгоритм.

Это то, что у меня было до сих пор:

import java.util.ArrayList;
import java.util.List;


public class Main
{

    private int countPolyminos(int n)
    {
        hashes.clear();
        count = 0;
        boolean[][] matrix = new boolean[n][n];
        createPolyominos(matrix, n);
        return count;
    }

    private List<Integer> hashes = new ArrayList<Integer>();
    private int count;

    private void createPolyominos(boolean[][] matrix, int n)
    {
        if (n == 0)
        {
            boolean[][] cropped = cropMatrix(matrix); 
            int hash = hashMatrixOrientationIndependent(matrix);
            if (!hashes.contains(hash))
            {
                count++;
                hashes.add(hash);
            }
            return;
        }
    // Here is the real trouble!!
    // Then here something like; createPolyominos(matrix, n-1);
    // But, we need to keep in mind that the polyominos can have ramifications
    }

    public boolean[][] copy(boolean[][] matrix)
    {
        boolean[][] b = new boolean[matrix.length][matrix[0].length];
        for (int i = 0; i < matrix.length; ++i)
        {
            System.arraycopy(matrix[i], 0, b, 0, matrix[i].length);
        }
        return b;
    }

    public boolean[][] cropMatrix(boolean[][] matrix)
    {
        int l = 0, t = 0, r = 0, b = 0;
        // Left
        left: for (int x = 0; x < matrix.length; ++x)
        {
            for (int y = 0; y < matrix[x].length; ++y)
            {
                if (matrix[x][y])
                {
                    break left;
                }
            }
            l++;
        }
        // Right
        right: for (int x = matrix.length - 1; x >= 0; --x)
        {
            for (int y = 0; y < matrix[x].length; ++y)
            {
                if (matrix[x][y])
                {
                    break right;
                }
            }
            r++;
        }
        // Top
        top: for (int y = 0; y < matrix[0].length; ++y)
        {
            for (int x = 0; x < matrix.length; ++x)
            {
                if (matrix[x][y])
                {
                    break top;
                }
            }
            t++;
        }
        // Bottom
        bottom: for (int y = matrix[0].length; y >= 0; --y)
        {
            for (int x = 0; x < matrix.length; ++x)
            {
                if (matrix[x][y])
                {
                    break bottom;
                }
            }
            b++;
        }

        // Perform the real crop
        boolean[][] cropped = new boolean[matrix.length - l - r][matrix[0].length - t - b];
        for (int x = l; x < matrix.length - r; ++x)
        {
            System.arraycopy(matrix[x - l], t, cropped, 0, matrix[x].length - t - b);
        }
        return cropped;
    }

    public int hashMatrix(boolean[][] matrix)
    {
        int hash = 0;
        for (int x = 0; x < matrix.length; ++x)
        {
            for (int y = 0; y < matrix[x].length; ++y)
            {
                hash += matrix[x][y] ? (((x + 7) << 4) * ((y + 3) << 6) * 31) : ((((x+5) << 9) * (((y + x) + 18) << 7) * 53));
            }
        }
        return hash;
    }

    public int hashMatrixOrientationIndependent(boolean[][] matrix)
    {
        int hash = 0;
        hash += hashMatrix(matrix);
        for (int i = 0; i < 3; ++i)
        {
            matrix = rotateMatrixLeft(matrix);
            hash += hashMatrix(matrix);
        }
        return hash;
    }

    public boolean[][] rotateMatrixRight(boolean[][] matrix)
    {
        /* W and H are already swapped */
        int w = matrix.length;
        int h = matrix[0].length;
        boolean[][] ret = new boolean[h][w];
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                ret[i][j] = matrix[w - j - 1][i];
            }
        }
        return ret;
    }

    public boolean[][] rotateMatrixLeft(boolean[][] matrix)
    {
        /* W and H are already swapped */
        int w = matrix.length;
        int h = matrix[0].length;
        boolean[][] ret = new boolean[h][w];
        for (int i = 0; i < h; ++i)
        {
            for (int j = 0; j < w; ++j)
            {
                ret[i][j] = matrix[j][h - i - 1];
            }
        }
        return ret;
    }

}

Ответы [ 7 ]

6 голосов
/ 10 января 2011

Всего 4 461 полиномино размера 10, поэтому мы можем просто перечислить их все.

Начните с одного камня. Чтобы увеличить его на один камень, попробуйте добавить новый камень во все пустые ячейки, которые соседствуют с существующим камнем. Делайте это рекурсивно, пока не достигнете желаемого размера.

Чтобы избежать дубликатов, сохраните хеш-таблицу всех полиномов каждого размера, которые мы уже перечислили. Когда мы собираем новое полиномино, мы проверяем, что его еще нет в хеш-таблице. Нам также нужно проверить его 3 поворота (и, возможно, его зеркальное отображение). В то время как повторная проверка в конечном размере является единственной строго необходимой проверкой, проверка на каждом шаге сокращает рекурсивные ветви, которые приводят к новому полиномину.

Вот некоторый псевдокод:

polynomino = array of n hashtables
function find_polynominoes(n, base):
  if base.size == n:
    return
  for stone in base:
    for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
      new_stone.x = stone.x + dx
      new_stone.y = stone.y + dy
      if new_stone not in base:
        new_polynomino = base + new_stone
        is_new = true
        for rotation in [0, 90, 180, 270]:
          if new_polynomino.rotate(rotation) in polynomino[new_polynomino.size]:
            is_new = false
            break
        if is_new:
          polynomino[new_polynomino.size].add(new_polynomino)
3 голосов
/ 10 января 2011

Самое наивное решение - начать с одного X, и для каждой итерации построить список уникальных возможных следующих состояний. Из этого списка создайте список уникальных состояний, добавив еще один X. Продолжайте до нужной итерации.

Однако я не уверен, что это работает в разумное время для N = 10. Может, в зависимости от ваших требований.

2 голосов
/ 30 ноября 2015

Только что решил это и в Java. Так как все здесь, похоже, имеют проблемы с производительностью. Я даю тебе и мою.

Представление платы:

2 массива целых чисел. 1 для строк и 1 для столбцов.

  • Вращение: column[i]=row[size-(i+1)], row[i] = reverse(column[i]), где инверсия - биты, обращенные в соответствии с размером (для размера = 4 и первые 2 бита принимаются: rev(1100) = 0011)
  • Смещающий блок: row[i-1] = row[i], col[i]<<=1
  • Проверьте, установлен ли бит: (row[r] & (1<<c)) > 0
  • Уникальность платы: Плата уникальна, если строка массива уникальна.
  • Хэш платы: Хеш-код строки массива
  • ..

Так что это делает все операции быстрыми. Многие из них были бы O(size²) в представлении 2D-массива вместо O(size).

Алгоритм:

  • Начните с блока размером 1
  • Для каждого размера начинайте с блоков, на 1 камень меньше.
  • Если есть возможность добавить камень. Проверьте, был ли он уже добавлен в набор.
  • Если он еще не добавлен. Добавьте это к решению этого размера.
    • добавить блок в набор и все его повороты. (3 поворота, всего 4)
    • Важно, после каждого поворота сдвигайте блок как можно выше влево / вверх.
  • + Особые случаи: сделать ту же логику для следующих 2 случаев
    • сдвиньте блок один вправо и добавьте камень в первый столбец
    • сдвиньте блок один на дно и добавьте камень в первый ряд

Производительность:

  • N=5, время: 3 мс
  • N=10, время: 58мс
  • N=11, время: 166мс
  • N=12, время: 538 мс
  • N=13, время: 2893мс
  • N=14, время: 17266мс
  • N=15, NA (вне пространства кучи)

Код: https://github.com/Samjayyy/logicpuzzles/tree/master/polyominos

1 голос
/ 11 февраля 2013

Вот мое решение в Java для той же проблемы.Я могу подтвердить номера Мартина (см. Ниже).Я также добавил, что для приблизительных вычислений требуется трудное время (Macbook Retina Core i7 в середине 2012 года).Я полагаю, что существенное улучшение производительности может быть достигнуто с помощью распараллеливания.

numberOfStones -> numberOfPolyominos
        1  -> 1
        2  -> 1
        3  -> 2
        4  -> 7
        5  -> 18
        6  -> 60
        7  -> 196
        8  -> 704      (3 seconds)
        9  -> 2500     (46 seconds)
        10 -> 9189     (~14 minutes)

.

    /*
 * This class is a solution to the Tetris unique shapes problem. 
 * That is, the game of Tetris has 7 unique shapes. These 7 shapes
 * are all the possible unique combinations of any 4 adjoining blocks
 * (i.e. ignoring rotations).
 * 
 * How many unique shapes are possible with, say, 7 or n blocks? 
 * 
 * The solution uses recursive back-tracking to construct all the possible
 * shapes. It uses a HashMap to store unique shapes and to ignore rotations.
 * It also uses a temporary HashMap so that the program does not needlessly
 * waste time checking the same path multiple times.
 * 
 * Even so, this is an exponential run-time solution, with n=10 taking a few
 * minutes to complete.
 */
package com.glugabytes.gbjutils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class TetrisBlocks {

    private HashMap uShapes;
    private HashMap tempShapes;

    /* Get a map of unique shapes for n squares. The keys are string-representations
     * of each shape, and values are corresponding boolean[][] arrays.
     * @param squares - number of blocks to use for shapes, e.g. n=4 has 7 unique shapes
     */
    public Map getUniqueShapes(int squares) {
        uShapes = new HashMap();
        tempShapes = new HashMap();

        boolean[][] data = new boolean[squares*2+1][squares*2+1];
        data[squares][squares] = true;
        make(squares, data, 1);             //start the process with a single square in the center of a boolean[][] matrix

        return uShapes;
    }

    /* Recursivelly keep adding blocks to the data array until number of blocks(squares) = required size (e.g. n=4)
     * Make sure to eliminate rotations. Also make sure not to enter infinite backtracking loops, and also not 
     * needlessly recompute the same path multiple times.
     */
    private void make(int squares, boolean[][] data, int size) {
        if(size == squares) {   //used the required number of squares
            //get a trimmed version of the array
            boolean[][] trimmed = trimArray(data);
            if(!isRotation(trimmed)) {  //if a unique piece, add it to unique map
                uShapes.put(arrayToString(trimmed), trimmed);
            }

        } else {
            //go through the grid 1 element at a time and add a block next to an existing block
            //do this for all possible combinations
            for(int iX = 0; iX < data.length; iX++) {
                for(int iY = 0; iY < data.length; iY++) {
                    if(data[iX][iY] == true) {      //only add a block next to an existing block
                        if(data[iX+1][iY] != true) {    //if no existing block to the right, add one and recuse
                            data[iX+1][iY] = true;
                            if(!isTempRotation(data)) {         //only recurse if we haven't already been on this path before
                                make(squares, data, size+1);
                                tempShapes.put(arrayToString(data), data);  //store this path so we don't repeat it later
                            }
                            data[iX+1][iY] = false; 
                        }

                        if(data[iX-1][iY] != true) {    //repeat by adding a block on the left
                            data[iX-1][iY] = true;      
                            if(!isTempRotation(data)) {
                                make(squares, data, size+1);
                                tempShapes.put(arrayToString(data), data);
                            }
                            data[iX-1][iY] = false; 
                        }

                        if(data[iX][iY+1] != true) {   //repeat by adding a block down
                            data[iX][iY+1] = true;      
                            if(!isTempRotation(data)) {
                                make(squares, data, size+1);
                                tempShapes.put(arrayToString(data), data);
                            }
                            data[iX][iY+1] = false; 
                        }

                        if(data[iX][iY-1] != true) {   //repeat by adding a block up
                            data[iX][iY-1] = true;      
                            if(!isTempRotation(data)) {
                                make(squares, data, size+1);
                                tempShapes.put(arrayToString(data), data);
                            }
                            data[iX][iY-1] = false; 
                        }
                    }
                }
            }
        }
    }

    /**
     * This function basically removes all rows and columns that have no 'true' flags,
     * leaving only the portion of the array that contains useful data.
     * 
     * @param data
     * @return 
     */
    private boolean[][] trimArray(boolean[][] data) {
        int maxX = 0;
        int maxY = 0;
        int firstX = data.length;
        int firstY = data.length;

        for(int iX = 0; iX < data.length; iX++) {
            for (int iY = 0; iY < data.length; iY++) {
                if(data[iX][iY]) {
                    if(iY < firstY) firstY = iY;
                    if(iY > maxY) maxY = iY;
                }
            }
        }

        for(int iY = 0; iY < data.length; iY++) {
            for (int iX = 0; iX < data.length; iX++) {
                if(data[iX][iY]) {
                    if(iX < firstX) firstX = iX;
                    if(iX > maxX) maxX = iX;
                }
            }
        }



        boolean[][] trimmed = new boolean[maxX-firstX+1][maxY-firstY+1];
            for(int iX = firstX; iX <= maxX; iX++) {
                for(int iY = firstY; iY <= maxY; iY++) {
                    trimmed[iX-firstX][iY-firstY] = data[iX][iY];
                }
            }

        return trimmed;
    }

    /**
     * Return a string representation of the 2D array.
     * 
     * @param data
     * @return 
     */
    private String arrayToString(boolean[][] data) {
        StringBuilder sb = new StringBuilder();
        for(int iX = 0; iX < data.length; iX++) {
            for(int iY = 0; iY < data[0].length; iY++) {
                sb.append(data[iX][iY] ? '#' : ' ');
            }
            sb.append('\n');
        }

        return sb.toString();
    }

    /**
     * Rotate an array clockwise by 90 degrees.
     * @param data
     * @return 
     */
    public boolean[][] rotate90(boolean[][] data) {
        boolean[][] rotated = new boolean[data[0].length][data.length];

        for(int iX = 0; iX < data.length; iX++) {
            for(int iY = 0; iY < data[0].length; iY++) {
                rotated[iY][iX] = data[data.length - iX - 1][iY];
            }
        }

        return rotated;
    }

    /**
     * Checks to see if two 2d boolean arrays are the same
     * @param a
     * @param b
     * @return 
     */
    public boolean equal(boolean[][] a, boolean[][] b) {
        if(a.length != b.length || a[0].length != b[0].length) {
            return false;
        } else {
            for(int iX = 0; iX < a.length; iX++) {
                for(int iY = 0; iY < a[0].length; iY++) {
                    if(a[iX][iY] != b[iX][iY]) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public boolean isRotation(boolean[][] data) {
        //check to see if it's a rotation of a shape that we already have
        data = rotate90(data);                      //+90*
        String str = arrayToString(data);
        if(!uShapes.containsKey(str)) {
            data = rotate90(data);                  //180*
            str = arrayToString(data);
            if(!uShapes.containsKey(str)) {
                data = rotate90(data);              //270*
                str = arrayToString(data);
                if(!uShapes.containsKey(str)) {
                    return false;
                }
            }
        }
        return true;
    }

    public boolean isTempRotation(boolean[][] data) {
        //check to see if it's a rotation of a shape that we already have
        data = rotate90(data);                      //+90*
        String str = arrayToString(data);
        if(!tempShapes.containsKey(str)) {
            data = rotate90(data);                  //180*
            str = arrayToString(data);
            if(!tempShapes.containsKey(str)) {
                data = rotate90(data);              //270*
                str = arrayToString(data);
                if(!tempShapes.containsKey(str)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        TetrisBlocks tetris = new TetrisBlocks();
        long start = System.currentTimeMillis();
        Map shapes = tetris.getUniqueShapes(8);
        long end = System.currentTimeMillis();

        Iterator it = shapes.keySet().iterator();
        while(it.hasNext()) {
            String shape = (String)it.next();
            System.out.println(shape);
        }

        System.out.println("Unique Shapes: " + shapes.size());
        System.out.println("Time: " + (end-start));
    }
}
1 голос
/ 11 января 2011

Я думаю, что сделал это!

РЕДАКТИРОВАТЬ: Я использую алгоритм SHA-256 для их хеширования, теперь он работает правильно.

Вот результаты:

numberOfStones -> numberOfPolyominos
            1  -> 1
            2  -> 1
            3  -> 2
            4  -> 7
            5  -> 18
            6  -> 60
            7  -> 196
            8  -> 704
            9  -> 2500
            10 -> terminated

Вот код (Java):

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/* VPW Template */

public class Main
{

    /**
     * @param args
     */
    public static void main(String[] args) throws IOException
    {
        new Main().start();
    }

public void start() throws IOException
{

    /* Read the stuff */
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String[] input = new String[Integer.parseInt(br.readLine())];
    for (int i = 0; i < input.length; ++i)
    {
        input[i] = br.readLine();
    }
    /* Process each line */
    for (int i = 0; i < input.length; ++i)
    {
        processLine(input[i]);
    }
}

public void processLine(String line)
{
    int n = Integer.parseInt(line);
    System.out.println(countPolyminos(n));
}

private int countPolyminos(int n)
{
    hashes.clear();
    count = 0;
    boolean[][] matrix = new boolean[n][n];
    matrix[n / 2][n / 2] = true;
    createPolyominos(matrix, n - 1);
    return count;
}

private List<BigInteger> hashes = new ArrayList<BigInteger>();
private int count;

private void createPolyominos(boolean[][] matrix, int n)
{
    if (n == 0)
    {
        boolean[][] cropped = cropMatrix(matrix);
        BigInteger hash = hashMatrixOrientationIndependent(cropped);
        if (!hashes.contains(hash))
        {
            // System.out.println(count + " Found!");
            // printMatrix(cropped);
            // System.out.println();
            count++;
            hashes.add(hash);
        }
        return;
    }
    for (int x = 0; x < matrix.length; ++x)
    {
        for (int y = 0; y < matrix[x].length; ++y)
        {
            if (matrix[x][y])
            {
                if (x > 0 && !matrix[x - 1][y])
                {
                    boolean[][] clone = copy(matrix);
                    clone[x - 1][y] = true;
                    createPolyominos(clone, n - 1);
                }
                if (x < matrix.length - 1 && !matrix[x + 1][y])
                {
                    boolean[][] clone = copy(matrix);
                    clone[x + 1][y] = true;
                    createPolyominos(clone, n - 1);
                }
                if (y > 0 && !matrix[x][y - 1])
                {
                    boolean[][] clone = copy(matrix);
                    clone[x][y - 1] = true;
                    createPolyominos(clone, n - 1);
                }
                if (y < matrix[x].length - 1 && !matrix[x][y + 1])
                {
                    boolean[][] clone = copy(matrix);
                    clone[x][y + 1] = true;
                    createPolyominos(clone, n - 1);
                }
            }
        }
    }
}

public boolean[][] copy(boolean[][] matrix)
{
    boolean[][] b = new boolean[matrix.length][matrix[0].length];
    for (int i = 0; i < matrix.length; ++i)
    {
        System.arraycopy(matrix[i], 0, b[i], 0, matrix[i].length);
    }
    return b;
}

public void printMatrix(boolean[][] matrix)
{
    for (int y = 0; y < matrix.length; ++y)
    {
        for (int x = 0; x < matrix[y].length; ++x)
        {
            System.out.print((matrix[y][x] ? 'X' : ' '));
        }
        System.out.println();
    }
}

public boolean[][] cropMatrix(boolean[][] matrix)
{
    int l = 0, t = 0, r = 0, b = 0;
    // Left
    left: for (int x = 0; x < matrix.length; ++x)
    {
        for (int y = 0; y < matrix[x].length; ++y)
        {
            if (matrix[x][y])
            {
                break left;
            }
        }
        l++;
    }
    // Right
    right: for (int x = matrix.length - 1; x >= 0; --x)
    {
        for (int y = 0; y < matrix[x].length; ++y)
        {
            if (matrix[x][y])
            {
                break right;
            }
        }
        r++;
    }
    // Top
    top: for (int y = 0; y < matrix[0].length; ++y)
    {
        for (int x = 0; x < matrix.length; ++x)
        {
            if (matrix[x][y])
            {
                break top;
            }
        }
        t++;
    }
    // Bottom
    bottom: for (int y = matrix[0].length - 1; y >= 0; --y)
    {
        for (int x = 0; x < matrix.length; ++x)
        {
            if (matrix[x][y])
            {
                break bottom;
            }
        }
        b++;
    }

    // Perform the real crop
    boolean[][] cropped = new boolean[matrix.length - l - r][matrix[0].length - t - b];
    for (int x = l; x < matrix.length - r; ++x)
    {
        System.arraycopy(matrix[x], t, cropped[x - l], 0, matrix[x].length - t - b);
    }
    return cropped;
}

public BigInteger hashMatrix(boolean[][] matrix)
{
    try
    {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update((byte) matrix.length);
        md.update((byte) matrix[0].length);
        for (int x = 0; x < matrix.length; ++x)
        {
            for (int y = 0; y < matrix[x].length; ++y)
            {
                if (matrix[x][y])
                {
                    md.update((byte) x);
                } else
                {
                    md.update((byte) y);
                }
            }
        }
        return new BigInteger(1, md.digest());
    } catch (NoSuchAlgorithmException e)
    {
        System.exit(1);
        return null;
    }
}

public BigInteger hashMatrixOrientationIndependent(boolean[][] matrix)
{
    BigInteger hash = hashMatrix(matrix);
    for (int i = 0; i < 3; ++i)
    {
        matrix = rotateMatrixLeft(matrix);
        hash = hash.add(hashMatrix(matrix));
    }
    return hash;
}

public boolean[][] rotateMatrixRight(boolean[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;
    boolean[][] ret = new boolean[h][w];
    for (int i = 0; i < h; ++i)
    {
        for (int j = 0; j < w; ++j)
        {
            ret[i][j] = matrix[w - j - 1][i];
        }
    }
    return ret;
}

public boolean[][] rotateMatrixLeft(boolean[][] matrix)
{
    /* W and H are already swapped */
    int w = matrix.length;
    int h = matrix[0].length;
    boolean[][] ret = new boolean[h][w];
    for (int i = 0; i < h; ++i)
    {
        for (int j = 0; j < w; ++j)
        {
            ret[i][j] = matrix[j][h - i - 1];
        }
    }
    return ret;
}
0 голосов
/ 23 сентября 2018

Вот мое полное решение Python, вдохновленное ответом @ marcog.На моем ноутбуке выводится число полиоминиумов размером 2,10 за 2 секунды.

Алгоритм прост:

  • Размер 1: начать с одного квадрата
  • Размер n + 1: возьмите все кусочки размером n и попробуйте добавить один квадрат во все возможные смежные позиции.Таким образом, вы все возможные новые штуки.Пропускать дубликаты.

Основное ускорение происходит за счет хэширования фрагментов, чтобы быстро проверить, видели ли мы уже фрагмент.

import itertools
from collections import defaultdict

n = 10
print("Number of Tetris pieces up to size", n)

# Times:
# n is number of blocks
# - Python O(exp(n)^2): 10 blocks 2.5m
# - Python O(exp(n)): 10 blocks 2.5s, 11 blocks 10.9s, 12 block 33s, 13 blocks 141s (800MB memory)
# - Other implementation:

smallest_piece = [(0, 0)]  # We represent a piece as a list of block positions
pieces_of_size = {
  1: [smallest_piece],
}

# Returns a list of all possible pieces made by adding one block to given piece
def possible_expansions(piece):
    # No flatMap in Python 2/3:
    # https://stackoverflow.com/questions/21418764/flatmap-or-bind-in-python-3
    positions = set(itertools.chain.from_iterable(
         [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)] for (x, y) in piece
    ))
    # Time complexity O(n^2) can be improved
    # For each valid position, append to piece
    expansions = []
    for p in positions:
        if not p in piece:
            expansions.append(piece + [p])
    return expansions

def rotate_90_cw(piece):
    return [(y, -x) for (x, y) in piece]

def canonical(piece):
    min_x = min(x for (x, y) in piece)
    min_y = min(y for (x, y) in piece)
    res = sorted((x - min_x, y - min_y) for (x, y) in piece)
    return res

def hash_piece(piece):
    return hash(tuple(piece))

def expand_pieces(pieces):
    expanded = []
    #[
    #  332322396: [[(1,0), (0,-1)], [...]],
    #  323200700000: [[(1,0), (0,-2)]]
    #]
    # Multimap because two different pieces can happen to have the same hash
    expanded_hashes = defaultdict(list)
    for piece in pieces:
        for e in possible_expansions(piece):
            exp = canonical(e)
            is_new = True
            if exp in expanded_hashes[hash_piece(exp)]:
                is_new = False
            for rotation in range(3):
                exp = canonical(rotate_90_cw(exp))
                if exp in expanded_hashes[hash_piece(exp)]:
                    is_new = False
            if is_new:
                expanded.append(exp)
                expanded_hashes[hash_piece(exp)].append(exp)
    return expanded


for i in range(2, n + 1):
    pieces_of_size[i] = expand_pieces(pieces_of_size[i - 1])
    print("Pieces with {} blocks: {}".format(i, len(pieces_of_size[i])))
0 голосов
/ 11 января 2011

Вот какой-то питон, который вычисляет ответ.Кажется, согласен с Википедией.Это не очень быстро, потому что он использует множество поисков по массиву вместо хеш-таблиц, но для его завершения требуется всего около минуты.

#!/usr/bin/python                                                                                                                                                 

# compute the canonical representation of polyomino p.                                                                                                            
# (minimum x and y coordinate is zero, sorted)                                                                                                                    
def canonical(p):
  mx = min(map(lambda v: v[0], p))
  my = min(map(lambda v: v[1], p))
  return sorted(map(lambda v: (v[0]-mx, v[1]-my), p))

# rotate p 90 degrees                                                                                                                                               
def rotate(p):
  return canonical(map(lambda v: (v[1], -v[0]), p))

# add one tile to p                                                                                                                                               
def expand(p):
  result = []
  for (x,y) in p:
    for (dx,dy) in ((-1,0),(1,0),(0,-1),(0,1)):
      if p.count((x+dx,y+dy)) == 0:
        result.append(canonical(p + [(x+dx,y+dy)]))
  return result

polyominos = [[(0,0)]]

for i in xrange(1,10):
  new_polyominos = []
  for p in polyominos:
    for q in expand(p):
      dup = 0
      for r in xrange(4):
        if new_polyominos.count(q) != 0:
          dup = 1
          break
        q = rotate(q)
      if not dup: new_polyominos.append(q)
  polyominos = new_polyominos
  print i+1, len(polyominos)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...