Ada banyak cara untuk mendekati masalah ini. Format data raster menunjukkan pendekatan berbasis raster; dalam mengkaji pendekatan-pendekatan tersebut, rumusan masalah sebagai program linear bilangan bulat biner tampak menjanjikan, karena sangat sesuai dengan semangat banyak analisis pemilihan lokasi GIS dan dapat dengan mudah disesuaikan dengannya.
Dalam formulasi ini, kami menyebutkan semua posisi dan orientasi yang mungkin dari poligon isian, yang saya sebut sebagai "ubin". Terkait dengan setiap ubin adalah ukuran "kebaikannya." Tujuannya adalah untuk menemukan koleksi ubin yang tidak tumpang tindih yang total kebaikannya sebesar mungkin. Di sini, kita dapat mengambil kebaikan setiap ubin menjadi area yang dicakupnya. (Dalam lingkungan keputusan yang lebih kaya data dan canggih, kami dapat menghitung kebaikan sebagai kombinasi sifat sel yang termasuk dalam setiap ubin, properti yang mungkin terkait dengan visibilitas, kedekatan dengan hal-hal lain, dan sebagainya.)
Kendala pada masalah ini hanya karena tidak ada dua ubin dalam solusi yang tumpang tindih.
Hal ini dapat dibingkai sedikit lebih abstrak, dengan cara kondusif untuk perhitungan efisien, dengan enumerasi sel-sel dalam poligon yang akan diisi ( "daerah") 1, 2, ..., M . Setiap penempatan ubin dapat dikodekan dengan vektor indikator nol dan yang, membiarkan yang sesuai dengan sel-sel yang dicakup oleh ubin dan nol di tempat lain. Dalam pengkodean ini, semua informasi yang diperlukan tentang koleksi ubin dapat ditemukan dengan menjumlahkan vektor indikator mereka (komponen dengan komponen, seperti biasa): jumlahnya akan nol di mana setidaknya satu ubin menutupi sel dan jumlahnya akan lebih besar dari satu tempat dua atau lebih ubin tumpang tindih. (Jumlahnya secara efektif menghitung jumlah yang tumpang tindih.)
Satu abstraksi yang lebih sedikit: himpunan kemungkinan penempatan ubin sendiri bisa disebutkan, mengatakan 1, 2, ..., N . Pemilihan setiap set penempatan ubin itu sendiri sesuai dengan vektor indikator di mana yang menentukan ubin yang akan ditempatkan.
Berikut ilustrasi kecil untuk memperbaiki ide . Itu disertai dengan kode Mathematica yang digunakan untuk melakukan perhitungan, sehingga kesulitan pemrograman (atau ketiadaan) dapat menjadi jelas.
Pertama, kami menggambarkan daerah yang akan diberi ubin:
region = {{0, 0, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}};
Jika kita beri nomor selnya dari kiri ke kanan, mulai dari atas, vektor indikator untuk wilayah ini memiliki 16 entri:
Flatten[region]
{0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
Mari kita gunakan ubin berikut, bersama dengan semua rotasi dengan kelipatan 90 derajat:
tileSet = {{{1, 1}, {1, 0}}};
Kode untuk menghasilkan rotasi (dan refleksi):
apply[s_List, alpha] := Reverse /@ s;
apply[s_List, beta] := Transpose[s];
apply[s_List, g_List] := Fold[apply, s, g];
group = FoldList[Append, {}, Riffle[ConstantArray[alpha, 4], beta]];
tiles = Union[Flatten[Outer[apply[#1, #2] &, tileSet, group, 1], 1]];
(Komputasi yang agak buram ini dijelaskan dalam balasan di /math//a/159159 , yang menunjukkannya hanya menghasilkan semua kemungkinan rotasi dan pantulan ubin dan kemudian menghapus hasil duplikat apa pun.)
Misalkan kita menempatkan ubin seperti yang ditunjukkan di sini:
Sel 3, 6, dan 7 tercakup dalam penempatan ini. Itu ditunjuk oleh vektor indikator
{0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Jika kita menggeser ubin ini satu kolom ke kanan, vektor indikator itu akan menjadi
{0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Kombinasi mencoba menempatkan ubin di kedua posisi ini secara bersamaan ditentukan oleh jumlah indikator ini,
{0, 0, 1, 1, 0, 1, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
Angka 2 di posisi ketujuh menunjukkan tumpang tindih ini dalam satu sel (baris kedua ke bawah, kolom ketiga dari kiri). Karena kami tidak ingin tumpang tindih, kami akan mensyaratkan bahwa jumlah vektor dalam setiap solusi yang valid harus tidak memiliki entri melebihi 1.
Ternyata untuk masalah ini, 29 kombinasi orientasi dan posisi dimungkinkan untuk ubin. (Ini ditemukan dengan sedikit pengkodean yang melibatkan pencarian lengkap.) Kami dapat menggambarkan semua 29 kemungkinan dengan menggambar indikator mereka sebagai vektor kolom . (Menggunakan kolom alih-alih baris adalah konvensional.) Berikut adalah gambar array yang dihasilkan, yang akan memiliki 16 baris (satu untuk setiap sel yang mungkin dalam persegi panjang) dan 29 kolom:
makeAllTiles[tile_, {n_Integer, m_Integer}] :=
With[{ m0 = Length[tile], n0 = Length[First[tile]]},
Flatten[
Table[ArrayPad[tile, {{i, m - m0 - i}, {j, n - n0 - j}}], {i, 0, m - m0}, {j, 0, n - n0}], 1]];
allTiles = Flatten[ParallelMap[makeAllTiles[#, ImageDimensions[regionImage]] & , tiles], 1];
allTiles = Parallelize[
Select[allTiles, (regionVector . Flatten[#]) >= (Plus @@ (Flatten[#])) &]];
options = Transpose[Flatten /@ allTiles];
(Dua vektor indikator sebelumnya muncul sebagai dua kolom pertama di sebelah kiri.) Pembaca yang bermata tajam mungkin telah memperhatikan beberapa peluang untuk pemrosesan paralel: perhitungan ini dapat memakan waktu beberapa detik.
Semua hal di atas dapat disajikan kembali secara ringkas menggunakan notasi matriks:
F adalah array opsi ini, dengan M rows dan N kolom.
X adalah indikator dari serangkaian penempatan genteng, panjang N .
b adalah vektor- N yang.
R adalah indikator untuk wilayah tersebut; ini adalah M- vektor.
Total "kebaikan" yang terkait dengan solusi X yang mungkin , sama dengan RFX , karena FX adalah indikator sel yang dicakup oleh X dan produk dengan R menjumlahkan nilai-nilai ini. (Kita dapat mempertimbangkan R jika kita menginginkan solusi untuk mendukung atau menghindari area tertentu di wilayah ini.) Ini harus dimaksimalkan. Karena kita bisa menuliskannya sebagai ( RF ). X , ini adalah fungsi linier X : ini penting. (Dalam kode di bawah ini, variabel c
berisi RF .)
Kendalanya adalah itu
Semua elemen X harus non-negatif;
Semua elemen X harus kurang dari 1 (yang merupakan entri yang sesuai dalam b );
Semua elemen X harus integral.
Batasan (1) dan (2) menjadikan ini program linier , sedangkan persyaratan ketiga mengubahnya menjadi program linier bilangan bulat .
Ada banyak paket untuk menyelesaikan program linier integer yang diekspresikan dengan tepat dalam formulir ini. Mereka mampu menangani nilai-nilai M dan N menjadi puluhan atau bahkan ratusan ribu. Itu mungkin cukup baik untuk beberapa aplikasi di dunia nyata.
Sebagai ilustrasi pertama kami, saya menghitung solusi untuk contoh sebelumnya menggunakan perintah Mathematica 8 LinearProgramming
. (Ini akan meminimalkan fungsi objektif linier. Minimalisasi dengan mudah diubah menjadi maksimalisasi dengan meniadakan fungsi objektif.) Ini mengembalikan solusi (sebagai daftar ubin dan posisi mereka) dalam 0,011 detik:
b = ConstantArray[-1, Length[options]];
c = -Flatten[region].options;
lu = ConstantArray[{0, 1}, Length[First[options]]];
x = LinearProgramming[c, -options, b, lu, Integers, Tolerance -> 0.05];
If[! ListQ[x] || Max[options.x] > 1, x = {}];
solution = allTiles[[Select[x Range[Length[x]], # > 0 &]]];
Sel abu-abu sama sekali tidak berada di wilayah tersebut; sel-sel putih tidak tercakup oleh solusi ini.
Anda dapat mengerjakan (dengan tangan) banyak tilings lain yang sama baiknya dengan yang satu ini - tetapi Anda tidak dapat menemukan yang lebih baik. Itulah batasan potensial dari pendekatan ini: itu memberi Anda satu solusi terbaik, bahkan ketika ada lebih dari satu. (Ada beberapa solusi: jika kita menyusun ulang kolom X , masalahnya tetap tidak berubah, tetapi hasilnya perangkat lunak sering memilih solusi yang berbeda. Namun, perilaku ini tidak dapat diprediksi.)
Sebagai ilustrasi kedua , agar lebih realistis, mari kita pertimbangkan wilayah dalam pertanyaan. Dengan mengimpor gambar dan meng-resampling, saya mewakilinya dengan grid 69 kali 81:
Wilayah ini terdiri dari 2156 sel dari kisi ini.
Untuk membuat hal-hal menarik, dan untuk menggambarkan generalisasi dari pengaturan pemrograman linier, mari kita coba untuk mencakup sebanyak mungkin wilayah ini dengan dua jenis persegi panjang:
Satu adalah 17 dengan 9 (153 sel) dan yang lainnya adalah 15 oleh 11 (165 sel). Kita mungkin lebih suka menggunakan yang kedua, karena lebih besar, tetapi yang pertama lebih kurus dan bisa muat di tempat yang lebih sempit. Ayo lihat!
Program sekarang melibatkan N = 5589 kemungkinan penempatan ubin. Itu cukup besar! Setelah 6,3 detik perhitungan, Mathematica datang dengan solusi sepuluh ubin ini:
Karena beberapa slack ( .eg, kita dapat menggeser ubin kiri bawah hingga empat kolom ke kiri), jelas ada beberapa solusi lain yang sedikit berbeda dari yang ini.