Saya baru di dunia pemecah SAT dan perlu beberapa panduan tentang masalah berikut.
Mengingat bahwa:
❶ Saya memiliki pilihan 14 sel yang berdekatan dalam kisi 4 * 4
❷ Saya memiliki 5 poliomino (A, B, C, D, E) dengan ukuran 4, 2, 5, 2 dan 1
Poly poliomino ini gratis , artinya bentuknya tidak tetap dan dapat membentuk pola yang berbeda
Bagaimana saya bisa menghitung semua kombinasi yang mungkin dari 5 poliomino bebas ini di dalam area yang dipilih (sel abu-abu) dengan SAT-solver?
Meminjam baik dari jawaban berwawasan @ spinkus dan dokumentasi OR-tools saya bisa membuat kode contoh berikut (berjalan dalam Jupyter Notebook):
from ortools.sat.python import cp_model
import numpy as np
import more_itertools as mit
import matplotlib.pyplot as plt
%matplotlib inline
W, H = 4, 4 #Dimensions of grid
sizes = (4, 2, 5, 2, 1) #Size of each polyomino
labels = np.arange(len(sizes)) #Label of each polyomino
colors = ('#FA5454', '#21D3B6', '#3384FA', '#FFD256', '#62ECFA')
cdict = dict(zip(labels, colors)) #Color dictionary for plotting
inactiveCells = (0, 1) #Indices of disabled cells (in 1D)
activeCells = set(np.arange(W*H)).difference(inactiveCells) #Cells where polyominoes can be fitted
ranges = [(next(g), list(g)[-1]) for g in mit.consecutive_groups(activeCells)] #All intervals in the stack of active cells
def main():
model = cp_model.CpModel()
#Create an Int var for each cell of each polyomino constrained to be within Width and Height of grid.
pminos = [[] for s in sizes]
for idx, s in enumerate(sizes):
for i in range(s):
pminos[idx].append([model.NewIntVar(0, W-1, 'p%i'%idx + 'c%i'%i + 'x'), model.NewIntVar(0, H-1, 'p%i'%idx + 'c%i'%i + 'y')])
#Define the shapes by constraining the cells relative to each other
## 1st polyomino -> tetromino ##
# #
# #
# # #
# ### #
# #
################################
p0 = pminos[0]
model.Add(p0[1][0] == p0[0][0] + 1) #'x' of 2nd cell == 'x' of 1st cell + 1
model.Add(p0[2][0] == p0[1][0] + 1) #'x' of 3rd cell == 'x' of 2nd cell + 1
model.Add(p0[3][0] == p0[0][0] + 1) #'x' of 4th cell == 'x' of 1st cell + 1
model.Add(p0[1][1] == p0[0][1]) #'y' of 2nd cell = 'y' of 1st cell
model.Add(p0[2][1] == p0[1][1]) #'y' of 3rd cell = 'y' of 2nd cell
model.Add(p0[3][1] == p0[1][1] - 1) #'y' of 3rd cell = 'y' of 2nd cell - 1
## 2nd polyomino -> domino ##
# #
# #
# # #
# # #
# #
#############################
p1 = pminos[1]
model.Add(p1[1][0] == p1[0][0])
model.Add(p1[1][1] == p1[0][1] + 1)
## 3rd polyomino -> pentomino ##
# #
# ## #
# ## #
# # #
# #
################################
p2 = pminos[2]
model.Add(p2[1][0] == p2[0][0] + 1)
model.Add(p2[2][0] == p2[0][0])
model.Add(p2[3][0] == p2[0][0] + 1)
model.Add(p2[4][0] == p2[0][0])
model.Add(p2[1][1] == p2[0][1])
model.Add(p2[2][1] == p2[0][1] + 1)
model.Add(p2[3][1] == p2[0][1] + 1)
model.Add(p2[4][1] == p2[0][1] + 2)
## 4th polyomino -> domino ##
# #
# #
# # #
# # #
# #
#############################
p3 = pminos[3]
model.Add(p3[1][0] == p3[0][0])
model.Add(p3[1][1] == p3[0][1] + 1)
## 5th polyomino -> monomino ##
# #
# #
# # #
# #
# #
###############################
#No constraints because 1 cell only
#No blocks can overlap:
block_addresses = []
n = 0
for p in pminos:
for c in p:
n += 1
block_address = model.NewIntVarFromDomain(cp_model.Domain.FromIntervals(ranges),'%i' % n)
model.Add(c[0] + c[1] * W == block_address)
block_addresses.append(block_address)
model.AddAllDifferent(block_addresses)
#Solve and print solutions as we find them
solver = cp_model.CpSolver()
solution_printer = SolutionPrinter(pminos)
status = solver.SearchForAllSolutions(model, solution_printer)
print('Status = %s' % solver.StatusName(status))
print('Number of solutions found: %i' % solution_printer.count)
class SolutionPrinter(cp_model.CpSolverSolutionCallback):
''' Print a solution. '''
def __init__(self, variables):
cp_model.CpSolverSolutionCallback.__init__(self)
self.variables = variables
self.count = 0
def on_solution_callback(self):
self.count += 1
plt.figure(figsize = (2, 2))
plt.grid(True)
plt.axis([0,W,H,0])
plt.yticks(np.arange(0, H, 1.0))
plt.xticks(np.arange(0, W, 1.0))
for i, p in enumerate(self.variables):
for c in p:
x = self.Value(c[0])
y = self.Value(c[1])
rect = plt.Rectangle((x, y), 1, 1, fc = cdict[i])
plt.gca().add_patch(rect)
for i in inactiveCells:
x = i%W
y = i//W
rect = plt.Rectangle((x, y), 1, 1, fc = 'None', hatch = '///')
plt.gca().add_patch(rect)
Masalahnya adalah bahwa saya memiliki 5 polyomino unik / tetap yang dikodekan dan saya tidak tahu bagaimana mendefinisikan kendala sehingga setiap pola yang mungkin untuk setiap polyomino diperhitungkan (asalkan dimungkinkan).
itertools
,numpy
,networkx
?minizinc
tag dengan jawaban terinci yang mencakup saran saya sebelumnya tentang penggunaanminizinc
.Jawaban:
EDIT: Saya melewatkan kata "gratis" dalam jawaban asli dan memberikan jawaban menggunakan OR-Tools untuk polyomino tetap. Menambahkan bagian untuk menjawab untuk menyertakan solusi untuk poliomino gratis - yang ternyata AFAICT cukup sulit untuk diungkapkan secara tepat dalam pemrograman kendala dengan OR-Tools.
POLYOMINO TETAP DENGAN ATAU ALAT:
Ya, Anda bisa melakukannya dengan pemrograman kendala di OR-Tools. OR-Tools tidak tahu apa-apa tentang geometri kisi 2D sehingga Anda harus menyandikan geometri dari setiap bentuk yang Anda miliki dalam hal batasan posisi. Yaitu bentuk adalah kumpulan blok / sel yang harus memiliki hubungan tertentu satu sama lain, harus dalam batas-batas grid dan tidak boleh tumpang tindih. Setelah Anda memiliki model kendala, Anda cukup bertanya Solver CP-SAT untuk menyelesaikannya, dalam kasus Anda, untuk semua solusi yang mungkin.
Ini adalah bukti konsep yang sangat sederhana dengan dua bentuk persegi panjang pada kisi 4x4 (Anda mungkin juga ingin menambahkan semacam kode juru bahasa untuk beralih dari deskripsi bentuk ke sekumpulan variabel OR-Tools dan kendala dalam masalah skala yang lebih besar karena memasukkan kendala dengan tangan agak membosankan).
Memberi:
POLYOMINO GRATIS:
Jika kita menganggap kisi-kisi sel sebagai grafik, masalahnya dapat ditafsirkan kembali sebagai menemukan k-partisi dari sel-sel kisi di mana setiap partisi memiliki ukuran tertentu dan di samping itu setiap partisi adalah komponen yang terhubung . Yaitu AFAICT tidak ada perbedaan antara komponen yang terhubung dan polyomino dan sisa jawaban ini membuat asumsi itu.
Menemukan semua kemungkinan "k-partisi dari sel-sel grid di mana setiap partisi memiliki ukuran tertentu" cukup sepele untuk diekspresikan dalam pemrograman kendala OR-Tools. Tetapi bagian keterhubungannya adalah AFAICT yang sulit (saya sudah mencoba dan gagal cukup lama ...). Saya pikir pemrograman kendala OR-Tools bukan pendekatan yang tepat. Saya perhatikan referensi OR-Tools C ++ untuk pustaka optimasi jaringan memiliki beberapa hal pada komponen yang terhubung yang mungkin patut dilihat, tapi saya tidak terbiasa dengannya. Di sisi lain, solusi pencarian rekursif naif dengan Python cukup bisa dilakukan.
Inilah solusi naif "dengan tangan". Ini cukup lambat tetapi dapat ditoleransi untuk kasing 4x4 Anda. Alamat digunakan untuk mengidentifikasi setiap sel dalam kisi. (Juga perhatikan jenis halaman wiki menyinggung sesuatu seperti algoritma ini sebagai solusi naif dan sepertinya itu menyarankan beberapa yang lebih efisien untuk masalah polyomino serupa).
Memberi:
sumber
Salah satu cara yang relatif mudah untuk membatasi wilayah yang terhubung sederhana di OR-Tools adalah membatasi perbatasannya menjadi sirkuit . Jika semua polyominos Anda berukuran kurang dari 8, kami tidak perlu khawatir tentang yang tidak terhubung secara mudah.
Kode ini menemukan semua 3884 solusi:
sumber
Untuk setiap polyonomino, dan setiap sel kiri atas yang mungkin, Anda memiliki variabel boolean yang menunjukkan apakah sel ini adalah bagian kiri atas dari persegi panjang terlampir.
Untuk setiap sel dan setiap polyomino, Anda memiliki variabel boolean yang menunjukkan apakah sel ini ditempati oleh polyomino ini.
Sekarang, untuk setiap sel dan setiap polyomino, Anda memiliki serangkaian implikasi: sel kiri atas dipilih menyiratkan setiap sel sebenarnya ditempati oleh polyomino ini.
Kemudian kendala: untuk setiap sel, paling banyak satu polyomino menempatinya untuk setiap polyomino, ada persis satu sel yang merupakan bagian kiri atas.
ini adalah masalah boolean murni.
sumber