Nonogram adalah gim puzzle Jepang yang tujuannya adalah untuk menggambar hitam putih sesuai daftar wilayah yang berdekatan, seperti:
Tentukan besaran nonografis dari baris atau kolom menjadi jumlah wilayah hitam yang berdekatan di baris atau kolom itu. Misalnya, baris atas memiliki besaran nonografis 1, karena ada satu wilayah 2 kotak di baris itu. Baris ke-8 memiliki besaran nonografis 3 karena memiliki 2, 2, 1.
Baris atau kolom kosong memiliki besaran nonografis 0.
Tugas Anda adalah menulis sebuah program yang mengambil kisi solusi untuk nonogram, dan menghasilkan kisi solusi dengan sesedikit mungkin kuadrat yang diisi di mana setiap baris dan kolom memiliki magnutide nonografis yang sama dengan kisi solusi yang diberikan.
Misalnya, kisi nonogram dengan semua kuadrat yang diisi memiliki besaran nonografis 1 pada setiap baris atau kolom:
Besaran nonografis yang sama dapat dicapai hanya dengan memiliki garis diagonal melalui grid, mengurangi jumlah kotak yang diisi secara dramatis:
Program Anda akan menerima input yang terdiri dari 50.000 baris dari file ini ( file teks tar.gz 1,32 MB; 2,15 MB membuka ritsleting), masing-masing mewakili kisi solusi nonogram 16 × 16 tunggal dengan kuadrat yang diisi secara acak (80% hitam), dan mengeluarkan 50.000 baris lagi, masing-masing berisi kisi solusi yang dioptimalkan untuk kisi input yang sesuai.
Setiap kisi direpresentasikan sebagai string base64 dengan 43 karakter (kotak pengkodean dari kiri ke kanan, lalu atas ke bawah), dan program Anda harus mengembalikan hasilnya dalam format yang sama. Misalnya, kotak pertama dalam file adalah E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=
, dan dirender sebagai:
Kotak dimulai dengan E
peta mana 000100
, jadi enam sel pertama di baris atas semuanya putih kecuali yang keempat. Karakter selanjutnya adalah /
yang dipetakan 111111
, jadi 6 sel berikutnya semuanya hitam - dan seterusnya.
Program Anda harus benar-benar mengembalikan kisi solusi dengan besaran nonografis yang benar untuk masing-masing 50.000 kasus uji. Diperbolehkan mengembalikan kisi yang sama dengan input jika tidak ada yang lebih baik ditemukan.
Program untuk mengembalikan kuadrat terisi total paling sedikit (dalam bahasa apa pun) adalah pemenangnya, dengan kode yang lebih pendek menjadi tiebreak.
Papan skor saat ini:
- 3.637.260 - Sleafar, Jawa
- 7.270.894 - flawr, Matlab
- 10.239.288 - Joe Z., Bash
sumber
Jawaban:
Python 2 & PuLP - 2.644.688 kotak (diminimalkan secara optimal); 10.753.553 kotak (dimaksimalkan secara optimal)
Minimal bermain golf hingga 1152 byte
(NB: garis yang indentasi dimulai dengan tab, bukan spasi.)
Contoh keluaran: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing
Ternyata masalah seperti mudah dikonversi ke Integer Linear Programme, dan saya membutuhkan masalah dasar untuk belajar bagaimana menggunakan PuLP — antarmuka python untuk berbagai pemecah LP — untuk proyek saya sendiri. Ternyata PuLP juga sangat mudah digunakan, dan pembangun LP yang tidak diserang bekerja dengan sempurna saat pertama kali saya mencobanya.
Dua hal yang baik tentang menggunakan pemecah IP cabang-dan-terikat untuk melakukan kerja keras memecahkan ini untuk saya (selain tidak harus menerapkan pemecah cabang dan terikat) adalah bahwa
Cara menggunakan program ini
Pertama, Anda harus menginstal PuLP.
pip install pulp
harus melakukan trik jika Anda telah menginstal pip.Kemudian, Anda harus meletakkan yang berikut ini dalam file bernama "c": https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing
Kemudian, jalankan program ini di Python 2 build apa pun dari direktori yang sama. Dalam waktu kurang dari sehari, Anda akan memiliki file bernama "s" yang berisi 50.000 grid nonogram yang diselesaikan (dalam format yang dapat dibaca), masing-masing dengan jumlah total kotak yang diisi tercantum di bawahnya.
Jika Anda ingin memaksimalkan jumlah kuadrat yang diisi, ganti
LpMinimize
baris on ke 8LpMaximize
sebagai gantinya. Anda akan mendapatkan hasil sangat banyak seperti ini: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharingMasukkan format
Program ini menggunakan format input yang dimodifikasi, karena Joe Z. mengatakan bahwa kami akan diizinkan untuk menyandikan ulang format input jika kami suka dalam komentar di OP. Klik tautan di atas untuk melihat seperti apa bentuknya. Ini terdiri dari 10.000 baris, masing-masing berisi 16 angka. Garis bernomor genap adalah magnitudo untuk baris dari instance yang diberikan, sedangkan garis bernomor ganjil adalah magnitudo untuk kolom dengan instance yang sama dengan garis di atasnya. File ini dihasilkan oleh program berikut:
(Program pengodean ulang ini juga memberi saya kesempatan ekstra untuk menguji kelas BitQueue kustom yang saya buat untuk proyek yang sama yang disebutkan di atas. Ini hanyalah antrian yang datanya dapat didorong sebagai urutan bit ATAU byte, dan dari mana data dapat muncul sedikit atau byte pada suatu waktu. Dalam hal ini, itu bekerja dengan sempurna.)
Saya mengkodekan ulang input untuk alasan spesifik bahwa untuk membangun ILP, informasi tambahan tentang grid yang digunakan untuk menghasilkan besaran sama sekali tidak berguna. Magnitudo adalah satu-satunya kendala, dan magnitudo itulah yang saya butuhkan untuk mengakses.
Pembangun ILP tak berkerumun
Ini adalah program yang benar-benar menghasilkan "contoh keluaran" yang ditautkan di atas. Oleh karena itu string ekstra panjang di ujung setiap kotak, yang saya terpotong saat bermain golf. (Versi golf harus menghasilkan output yang identik, minus kata-kata
"Filled squares for "
)Bagaimana itu bekerja
Saya menggunakan kisi 18x18, dengan bagian tengah 16x16 menjadi solusi puzzle yang sebenarnya.
cells
apakah kotak ini. Baris pertama menciptakan 324 variabel biner: "cell_0_0", "cell_0_1", dan seterusnya. Saya juga membuat kisi-kisi "ruang" antara dan di sekitar sel di bagian solusi kisi.rowseps
menunjuk ke 289 variabel yang melambangkan ruang yang memisahkan sel secara horizontal, sementaracolseps
juga menunjuk ke variabel yang menandai ruang yang memisahkan sel secara vertikal. Berikut diagram unicode:The
0
s dan□
s adalah nilai-nilai biner dilacak olehcell
variabel, yang|
s adalah nilai-nilai biner dilacak olehrowsep
variabel, dan-
s adalah nilai-nilai biner dilacak olehcolsep
variabel.Ini adalah fungsi tujuan. Hanya jumlah dari semua
cell
variabel. Karena ini adalah variabel biner, ini persis jumlah kuadrat yang diisi dalam solusi.Ini hanya mengatur sel di sekitar tepi luar grid ke nol (itulah sebabnya saya mewakili mereka sebagai nol di atas). Ini adalah cara paling bijaksana untuk melacak berapa banyak "blok" sel yang terisi, karena memastikan bahwa setiap perubahan dari tidak terisi menjadi terisi (bergerak melintasi kolom atau baris) dicocokkan dengan perubahan yang sesuai dari terisi ke tidak terisi (dan sebaliknya ), bahkan jika sel pertama atau terakhir di baris terisi. Ini adalah alasan utama untuk menggunakan kisi 18x18 di tempat pertama. Ini bukan satu-satunya cara untuk menghitung blok, tetapi saya pikir ini adalah yang paling sederhana.
Ini adalah daging asli dari logika ILP. Pada dasarnya ia mensyaratkan bahwa setiap sel (selain yang ada di baris dan kolom pertama) menjadi karakter logis dari sel dan pemisah langsung ke kiri di barisnya dan langsung di atasnya dalam kolomnya. Saya mendapat kendala yang mensimulasikan xor dalam program integer {0,1} dari jawaban yang luar biasa ini: /cs//a/12118/44289
Untuk menjelaskan lebih banyak: batasan xor ini membuatnya sehingga separator bisa 1 jika dan hanya jika mereka terletak di antara sel-sel yang 0 dan 1 (menandai perubahan dari tidak terisi menjadi terisi atau sebaliknya). Dengan demikian, akan ada tepat dua kali lebih banyak pemisah bernilai 1 dalam satu baris atau kolom daripada jumlah blok di baris atau kolom itu. Dengan kata lain, jumlah pemisah pada baris atau kolom tertentu persis dua kali lipat besarnya baris / kolom itu. Karenanya kendala berikut:
Dan itu sudah cukup. Sisanya hanya meminta solver default untuk menyelesaikan ILP, lalu memformat solusi yang dihasilkan saat ia menulisnya ke file.
sumber
Jawa,
6.093.0924.332.6563.637 kotak (diminimalkan),10.567.55010.567.69110.568.746 kotak (dimaksimalkan)Kedua varian program berulang kali melakukan operasi pada kisi sumber, tanpa mengubah besarnya.
Minimizer
menyusut()
Jika kotak hitam memiliki 2 tetangga putih dan 2 tetangga hitam di sudut 90 °, itu bisa diganti dengan kotak putih.
moveLine ()
Dalam konfigurasi seperti di atas, garis hitam dapat dipindahkan ke kanan. Hal ini dilakukan berulang kali untuk semua 4 arah searah jarum jam dan berlawanan arah jarum jam, untuk membuka kemungkinan menyusut baru.
Maximizer
Batalkan komentar pada baris
main()
dan komentari baris di atas untuk versi ini.tumbuh()
Jika kotak putih memiliki 2 tetangga putih dan 2 tetangga hitam di sudut 90 °, itu bisa diganti dengan kotak hitam.
moveLine ()
Sama seperti di Minimizer.
Sumber
sumber
Bash - 10.239.288 kotak
Sebagai solusi referensi tempat terakhir:
Karena program Anda diizinkan untuk mengembalikan kotak yang sama jika tidak dapat menemukan solusi yang lebih baik, mencetak seluruh file kata demi kata juga valid.
Ada total 10.239.288 kotak hitam di file tes, yang cukup dekat dengan 10.240.000 yang Anda harapkan dari 80% kotak yang diisi dari 50.000 kotak dengan masing-masing 256 kotak. Seperti biasa dengan pertanyaan baterai tes saya, saya telah memilih jumlah test case dengan harapan bahwa skor optimal akan berada di sekitar kisaran 2-juta, meskipun saya menduga skor akan lebih dekat ke 4 atau 5 juta kali ini di sekitar .
Jika ada yang bisa membuat solusi yang memaksimalkan kotak hitam daripada meminimalkannya dan mengelola untuk mendapatkan lebih dari 10.240.000, saya mungkin mempertimbangkan untuk memberikannya hadiah.
sumber
Matlab, 7.270.894 kotak (~ 71% dari aslinya)
Ide adalah pencarian serakah berulang yang sederhana: Untuk setiap kotak hitam, coba jika Anda dapat mengaturnya menjadi putih tanpa mengubah besaran nonografis. Ulangi ini dua kali. (Anda dapat mencapai hasil yang jauh lebih baik dengan lebih banyak pengulangan, tetapi tidak gratis: Ini menghasilkan runtime yang lebih lama. Sekarang ini sekitar 80 menit. Saya akan melakukan itu, jika kita tidak perlu menghitung semua 50k testcases ...)
Berikut kodenya (masing-masing fungsi dalam file terpisah, seperti biasa.)
sumber