1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ポリオミノを列挙する

Last updated at Posted at 2025-04-25

image.png

image.png

image.png

image.png

image.png

image.png

image.png

はじめに

ポリオミノを列挙するプログラムです。
小学校の宿題で、「何種類あるかな?」「全部、描けるかな?」というのが出たので、実装したくなった次第です。

環境

Apple M1 pro
Processing 4.2

ポリオミノの種類

正方形の数 日本語 English 種類 計算時間
1 モノミノ monomino 1 <1s
2 ドミノ domino 1 <1s
3 トロミノ tromino 2 <1s
4 テトロミノ tetromino 5 <1s
5 ペントミノ pentomino 12 <1s
6 ヘキソミノ hexomino 35 <1s
7 ヘプトミノ heptomino 108 1.4s
8 オクトミノ octomino 369 13.4s
9 ノノミノ nonomino 1285 259s
10 デコミノ decomino 4655 -

※このサイトのhextominoというのは、造語です。-ominoというのは四角を意味します。あとは、音の語感です。

※立方体の展開図は11種類です。

プログラム

polyomino.pde
import java.util.*;

ArrayList<HashSet<PVector>> pentominoes = new ArrayList<HashSet<PVector>>();
HashSet<String> seenShapes = new HashSet<String>();

void setup() {
  size(1100, 800);
  findPentominoes();
  println("見つかったペントミノの数:" + pentominoes.size());
  noLoop();
}

void draw() {
  background(255);
  drawPentominoes();
}

// --- 探索ロジック(前回と同じ) ---
void findPentominoes() {
  HashSet<PVector> start = new HashSet<PVector>();
  start.add(new PVector(0, 0));
  grow(start);
}

void grow(HashSet<PVector> shape) {
  if (shape.size() == 5) {
    if (!isDuplicate(shape)) {
      pentominoes.add(shape);
      seenShapes.add(shapeKey(normalize(shape)));
    }
    return;
  }

  for (PVector block : shape) {
    int[][] dirs = {{1,0},{-1,0},{0,1},{0,-1}};
    for (int[] dir : dirs) {
      PVector next = new PVector(block.x + dir[0], block.y + dir[1]);
      if (!containsPoint(shape, next)) {
        HashSet<PVector> newShape = new HashSet<PVector>(shape);
        newShape.add(next);
        grow(newShape);
      }
    }
  }
}

boolean containsPoint(HashSet<PVector> shape, PVector point) {
  for (PVector p : shape) {
    if (p.x == point.x && p.y == point.y) return true;
  }
  return false;
}

boolean isDuplicate(HashSet<PVector> candidate) {
  for (int r = 0; r < 4; r++) {
    for (boolean f : new boolean[]{false, true}) {
      HashSet<PVector> transformed = transform(candidate, r, f);
      String key = shapeKey(normalize(transformed));
      if (seenShapes.contains(key)) return true;
    }
  }
  return false;
}

HashSet<PVector> transform(HashSet<PVector> shape, int rotation, boolean flip) {
  HashSet<PVector> result = new HashSet<PVector>();
  for (PVector p : shape) {
    PVector q = p.copy();
    if (flip) q.x *= -1;
    for (int i = 0; i < rotation; i++) {
      q = new PVector(-q.y, q.x);
    }
    result.add(q);
  }
  return result;
}

HashSet<PVector> normalize(HashSet<PVector> shape) {
  float minX = Float.MAX_VALUE, minY = Float.MAX_VALUE;
  for (PVector p : shape) {
    if (p.x < minX) minX = p.x;
    if (p.y < minY) minY = p.y;
  }
  HashSet<PVector> result = new HashSet<PVector>();
  for (PVector p : shape) {
    result.add(new PVector(p.x - minX, p.y - minY));
  }
  return result;
}

String shapeKey(HashSet<PVector> shape) {
  ArrayList<String> coords = new ArrayList<String>();
  for (PVector p : shape) {
    coords.add((int)p.x + "," + (int)p.y);
  }
  Collections.sort(coords);
  return String.join(";", coords);
}

// --- ペントミノ描画 ---
void drawPentominoes() {
  int cols = 4;
  int cellSize = 40;
  int padding = 20;
  int i = 0;

  for (HashSet<PVector> shape : pentominoes) {
    int row = i / cols;
    int col = i % cols;
    float offsetX = col * (cellSize * 6 + padding) + 50;
    float offsetY = row * (cellSize * 6 + padding) + 50;

    drawShape(shape, offsetX, offsetY, cellSize);
    i++;
  }
}

// 各ペントミノの形を描く
void drawShape(HashSet<PVector> shape, float offsetX, float offsetY, int s) {
  HashSet<PVector> norm = normalize(shape);
  fill(100, 150, 255);
  stroke(0);
  strokeWeight(2);
  for (PVector p : norm) {
    float x = offsetX + p.x * s;
    float y = offsetY + p.y * s;
    rect(x, y, s, s);
  }
}

・正方形からなる全ての連結形状を再帰で生成
・回転・反転させて重複を除外

形状の表現

・各ペントミノは、(x, y) 座標の5つの相対位置のセット
 (例:[(0,0), (1,0), (2,0), (2,1), (3,1)])で表現
・正規化(左上を原点)して比較

全体の保存形式

ArrayList<HashSet<PVector>>:各ペントミノは、5個のPVectorを持つHashSetとして格納
・HashSetであれば順序の違いは無視されるため、形状の一致判定に便利

重複除去

・回転・反転した形状をすべて生成して、既存のリストに同じものがないか確認。
・回転4種 × 反転2種 → 8通りをすべて比較
・「正規化」+「全変換(8通り)」
・HashSet を直接使うのではなく、各図形を正規化・ソートして → List → String に変換。その文字列を HashSet に登録して、重複除外。

参考リンク

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?