Saat saya menulis teka-teki silang Jepang dalam 4 jam

Aku malas melihat-lihat daftar kursus yang diadakan oleh anak sekolah yang baru-baru ini disusun oleh rekan-rekan dari Sirius ... Jadi, apa ini? "Cari benda kombinatorial menggunakan SAT-solver"? "Kami membuat pemecah sudoku, teka-teki silang Jepang, dan lainnya?"

Pikiran muncul bahwa masalah NP lengkap dapat direduksi satu sama lain, dan khususnya, direduksi untuk mencari kelayakan formula Boolean . Salah satu penulis Habr menyatakan ide ini di sini , dan terus terang, bagi saya itu seperti sihir.

Tentu saja, sebagai orang yang telah menyelesaikan kursus dalam matematika diskrit dan kompleksitas algoritma, saya secara teoritis tahu bahwa tugas dapat direduksi satu sama lain. Tetapi biasanya sulit untuk melakukan ini, dan saya lebih baik mengambil kata-kata saya untuk para profesor dan orang pintar lainnya.

Tapi kemudian ditawarkan kepada ... SCHOOLCHILDREN! Di dalam, penusuk mulai menggerakkan kreativitas ... dan berkata: "Yah, mungkin tidak sulit untuk mengencangkannya, begitu para siswa ditawari. Tidak bisakah saya mencari tahu ?? Lihat, mereka berjanji bahwa mereka akan menggunakan pustaka Python, tetapi saya umumnya tahu python ... ”

Dan waktunya sekitar jam 9 malam, yang agak menumpulkan pandangan kritis saya pada kerumitan masalah ... (sebenarnya, kronik selanjutnya dari pemrograman 4 jam)

21:02 Tahap pertama pencarian - Saya mencoba menginstal perpustakaan dengan Python. Dari pengalaman dengan jaringan saraf, saya sudah tahu bahwa, pada kenyataannya, kode kadang-kadang membutuhkan waktu lebih sedikit daripada mengkonfigurasi paket.

Menggunakan pycosat install pip klasik tidak diinstal, diperlukan kesalahan dalam semangat Microsoft Visual C ++ 14.0 diperlukan .

Kami memanjat untuk mencari sumber masalahnya, kami sampai di Stackoverflow , di mana kami menemukan tautan kecil ke Alat Bangun Visual C ++ 2015 yang diperlukan , termasuk kompiler C ++.

Unduh, coba pasang ... wow, ini membutuhkan 4 GB! Saya sebenarnya hanya membutuhkan kompiler, tetapi oh well.

Sementara hal ini sedang diunduh dan diinstal, kami mengikuti tautan dalam program yang diposting dari Sirius ... Ya, tidak banyak - contoh proyek Python, dan daftar tugas yang mungkin untuk kursus. Tidak ada presentasi, tidak ada video untuk dengan cepat memahami cara bekerja dengan perpustakaan.

Oke, mari kita mulai dengan menguraikan contoh. Kita pergi ke queens_pycosat.py ... Wow, sepanjang jalan ini adalah solusi untuk masalah akrab menempatkan ratu di papan catur (tugasnya adalah mengatur 8 ratu sehingga mereka tidak saling mengalahkan).

21: 10-21: 30 Kami memahami dan mencoba memulai.

Logika umum mulai ditelusuri:
SAT- () (1, 2, 3),(-1, 2 ..), (True/False).

, , .

( , ); , , 8x8 = 64 , .

(1 2 3… 8) β€” 1 . β€” [1,2,3...8]
β€” (9… 16) ..

β€” 1 !
[ 1 2] ( β€” ). [-1,-2],[-1,-3] .

, , . , .

( ) β€” (1 2… 8) ( 1 2) (...). , β€” .

Ya, saya tidak begitu pintar sekarang, setelah saya menemukannya ...

Secara umum, kami menyalin kode pengajaran ke diri kami sendiri. Untuk memastikan bahwa kami memahami sesuatu, matikan cek untuk diagonal - maka tugas berubah menjadi tugas tentang benteng.

Kami mulai, kami menerima: Berhasil! 21:35 Setelah berkenalan dengan dokumentasi perpustakaan, kami menemukan bahwa ada tantangan untuk menemukan satu solusi atau semua solusi. Katakan padaku, teman baikku, ada berapa pengaturan rook di papan 8x8? Apakah Anda akan menemukan segalanya? Mesin itu berpikir sebentar, dan saya mulai mencari-cari di tabel faktorial. Semenit kemudian, solver itu menjawab - "40320 solusi ditemukan." Periksa - benar-benar 8 !, Semuanya benar.

| | | | | | | |x|
| | | | | | |x| |
| | | | | |x| | |
| | | | |x| | | |
| | | |x| | | | |
| | |x| | | | | |
| |x| | | | | | |
|x| | | | | | | |











Saya menghapus kondisi bahwa benteng tidak saling mengenai secara vertikal ... tapi berapa harganya sekarang? Harus 8 ^ 8 = 16777216. Kemudian saya sadar bahwa dia akan membutuhkan waktu lama untuk menghitung - kami mengganggu prosesnya.

Juga menemukan cara cepat untuk memperoleh 4 solusi tanpa menghitung semuanya - dari semua yang ada di perpustakaan yang sama. Ini dilakukan - kami menemukan solusi, setelah itu kami menambahkannya ke pengecualian rumus Boolean ...

Bagaimana menyimpulkan beberapa solusi dalam python
for cnter in range(4):
  sol = pycosat.solve(clauses)
  if isinstance(sol, list):
    print(sol)
    clauses.append([-x for x in sol]) #   -      
  else: #    - 
    return
#     ,    ,   .


Secara umum, kami mendapatkan beberapa solusi untuk "masalah benteng": 21:45 I: Secara umum, semuanya jelas, jika ada, sekarang saya dapat mengencangkannya. Saya akan membaca buku dan spa ... MULAI KREATIF: Ya, Anda mengerti bagaimana semuanya berjalan. Dan mari kita menulis teka-teki silang kecil sederhana teka-teki silang Jepang! Ambil case paling sederhana - pada setiap baris dan kolom tidak akan ada lebih dari satu digit (dan, karenanya, satu blok). Anda cukup mencari melalui opsi yang memungkinkan untuk baris / kolom, cepat di sini, dan kemudian memuatnya ke dalam solver. Ayo lakukan, ya? Saya: ( tanpa perasaan penyergapan ) Baiklah, ayolah ... sepertinya kebenarannya tidak lama ... Untuk kesederhanaan, kami hanya mengambil NXN kotak-kotak silang - pada saat yang sama, Anda dapat mengambil kode output siap dan berpikir di mana horizontal dan vertikal tidak diperlukan ...

| | | | | | | |x|
| | | | | | |x| |
| | | | | |x| | |
| | | | |x| | | |
| | | |x| | | | |
| |x| | | | | | |
| | |x| | | | | |
|x| | | | | | | |











22:10 Tidak berfungsi: ((
Kami mencoba teka-teki silang 3x3, dalam format 1-0-1 (sejauh ini hanya blok yang horisontal, kami tidak memperhitungkan vertikal). Ia mengeluarkan semua sel kosong, infeksi, bahkan tidak memperhitungkan blok yang diisi ...

Oke, secara rinci Kami mempelajari format transfer data ke solver, dan kemudian sesuatu, dengan kata lain, tak terduga terungkap ...

, Β«-Β» ?

/ /, β€” . , .

[1] 3 , :
[X__],[_X_],[__X]
, ,
[1,-2,-3],[-1,2,-3],[-1,-2,3]

, …
:
[1 -2 -3] [-1, 2...] [...]

, . , . β€” , !

Penyergapan. Sebenarnya, ada dua opsi - baik untuk datang dengan kondisi untuk teka-teki silang di KNF, atau untuk mencari tahu bagaimana mengkonversi DNF ke KNF (well, seseorang harus memikirkannya, terutama karena keduanya adalah formula Boolean!)

22:50 Saya menyelesaikan camilan. Saya tidak memikirkan bagaimana membuat kondisi untuk KNF (kemudian saya menemukan bahwa mereka dideskripsikan dalam artikel , tetapi itu adalah waktu yang lama untuk memprogram mereka, dan, terus terang, tidak sportif).

Jadi kami mencari konverter DNF ke CNF. Deskripsi formal transformasi dalam gaya akademik tersebar di Internet ... mengapa saya membutuhkannya? Saya pasti tidak akan tetap berpegang pada mereka sekarang, saya akan memecahkan masalah. Jadi, kita melihat satu-satunya pustaka Python (!) Dengan nama dan fungsi yang sesuai. Sekarang kita akan terhubung ...

23:10Ia bekerja lucu - ia membuat variabel tambahan, dan melaluinya ia membawa DNF ke CNF. Ternyata, jujur ​​membuat KNF cukup sulit ...

Jadi, itu juga tidak berhasil! Apa masalahnya?
Kami mengirimkan input uji DNF: lstall = [[1,2,3]]. Yaitu, kami berharap bahwa hanya [1,2,3] (sesuai dengan baris penuh [XXX]) yang akan menjadi solusi yang valid.

Perpustakaan memberi KNF: cnf = [[-1, -2, -3, 4], [1 , -4], [2, -4], [3, -4]]
Tidak jelas, tetapi sangat menarik. Baiklah, katakanlah saya percaya Anda ...

Kami meminta pycosat untuk menemukan semua solusi untuk cnf:
[1, 2, 3, 4]
[1, 2, -3, -4]
[1, -2, -3, -4]
[1 , -2, 3, -4]
[-1, -2, -3, -4]
[-1, -2, 3, -4]
[-1, 2, -3, -4]
[-1, 2, 3, -4]

Alih-alih yang diharapkan, ada delapan! Apa yang harus dilakukan?

, , 4 True. ?

: cnf2 = [[-1, -2, -3, 4], [1, -4], [2, -4], [3, -4], [4]]
pycosat cnf2:
1: [1, 2, 3, 4]

, , ! , , .
β€” !

23:10 Dia memutuskan! Berikut ini adalah teka-teki silang 1-3-1 secara vertikal dan horizontal: Ini adalah solusi ganda dari teka-teki silang 1-0-1 secara vertikal dan horizontal: Saya: Jadi, semuanya bekerja untuk saya, saya sudah selesai. Wow, ini sudah jam 11 malam. Spa ... MULAI KREATIF: Apakah Anda mengerti bahwa Anda memiliki sedikit waktu untuk menyelesaikan teka-teki silang Jepang? Hanya perlu menulis satu prosedur rekursif yang akan memberikan semua opsi baris bukan untuk satu blok, tetapi untuk N ... Saya: Ya ... sepertinya mudah, ya ... 00:00 Pada tengah malam, kepala tupit sudah masak tidak begitu baik, tapi saya terus men-debug pengulangan. Ketentuan teka-teki silang Jepang - di antara blok harus ada setidaknya satu ruang, tetapi mungkin ada lebih dari satu.

| |x| |
|x|x|x|
| |x| |




1
| | |x|
| | | |
|x| | |
2
|x| | |
| | | |
| | |x|












Jadi dalam kondisi [1,2,1] untuk 6 sel, program harus menghasilkan satu-satunya opsi
[X_XX_X],
dan untuk 7 sel sudah ada 4 opsi:
[X_XX_X_],
[X_XX__X],
[X__XX_X],
[XXXX_X], [_X_XX_X],

Semua waktu dalam Saya keliru menghitung lokasi sel oleh satu, dan persetan dengan itu - lebih mudah untuk memulai dan memperbaikinya daripada berpikir ...

00:30 Apakah berhasil? Apa lagi yang perlu diperiksa? Jadi, di mana untuk mendapatkan deskripsi mesin teka-teki silang yang berorientasi mesin ... Di situs ini, tidak, pada ini - tidak ... Oke, persetan dengan itu, saya akan mengganggu pena. 00:50 Ini benar-benar berfungsi O_O Ini kepiting dengan jcrosswords.ru/crossword/65

3x3
rows = [[1,1],[0],[1,1]]
cols = [[1,1],[0],[1,1]]

:
|X| |X|
| | | |
|X| |X|

4x4
rows = [[1,1],[0],[0],[1,1]]
cols = [[1],[1],[1],[1]]

:
1
| |X| |X|
| | | | |
| | | | |
|X| |X| |
2
|X| |X| |
| | | | |
| | | | |
| |X| |X|







diputuskan oleh pemecah saya. Ukuran maksimum yang diuji adalah 10x10, sekarang saya sangat malas untuk menyela lebih banyak teka-teki silang ke dalam program ... - Saya: Wow, ini sudah jam satu pagi! Tunggu, hanya butuh 4 jam dari saat ketika saya tidak tahu apa-apa tentang SAT-solver? Jadi, bagaimana itu sama sekali? (Catatan singkat ditulis tentang apa yang terjadi ...)

|x|_|x|_|_|_|_|x|_|x|
|x|x|x|_|_|_|_|x|x|x|
|_|x|_|_|_|_|_|_|x|_|
|_|x|_|x|_|_|x|_|x|_|
|_|x|x|x|x|x|x|x|x|_|
|_|_|x|x|x|x|x|x|_|_|
|x|x|x|x|x|x|x|x|x|x|
|_|_|x|x|x|x|x|x|_|_|
|x|x|_|x|x|x|x|_|x|x|
|x|_|_|_|_|_|_|_|_|x|








Kata penutup

Pagi . Pada Habr, artikel tentang pemecah masalah pada Rust ditemukan . Mengunduh dari situs teka-teki silang tidak berfungsi, tetapi setelah pergi ke artikel Github yang ditentukan dalam artikel, kami menemukan di situs teka-teki silang Webpbn tautan tersembunyi untuk mengekspor teka-teki silang dalam format yang dapat dibaca mesin . Opsi "Format untuk versi 1 pemecah Python Kyle Keen" cocok untuk kita, sama seperti milik kita.

Saat Anda tidak perlu menyela teka-teki silang dengan pena, segalanya berjalan lebih cepat. Dengan cepat menjadi jelas bahwa maksimum yang dapat diselesaikan oleh pemecah adalah teka-teki silang di wilayah 30x30. Masalah utama adalah generasi varian dari masing-masing garis, dalam hal tumpukan kondisi ini, SANGAT banyak dan semuanya mulai melambat:

1. Pertama, opsi string membutuhkan waktu lama untuk dibuat. Menghasilkan hanya semua opsi untuk string [2,1,3,1,4] dengan panjang 60 sudah sekitar setengah menit ... (ada 2118760 di antaranya, jika ada yang tertarik)
2. Juga, konversi DNF-> CNF menciptakan terlalu banyak kondisi dan variabel tambahan ...

Tetapi jika ada beberapa blok, maka bahkan teka-teki silang yang besar dapat diselesaikan dalam waktu yang berarti.

Kucing - semuanya baik-baik saja
20x20
51511 clauses...
2851 last var number
--- 0.05186057090759277 seconds for clauses gen ---
1
| | |X|X| | | | | | | | | | | | | | | | |
| |X|X| | | | | | | | | | | | | | | | | |
| |X| | | | | | | | | | | | | | | | | | |
| |X| | | | | | | | | | | | | | | | | | |
| |X| | | | | |X|X|X| | | | | | | | | | |
|X|X| | | | |X|X|X|X|X| | | | | | | | | |
|X| | | | |X|X|X|X|X|X|X| | | |X| | | |X|
|X| | | | |X|X|X|X|X|X|X|X| | |X|X| |X|X|
|X| | | |X|X|X|X|X|X|X|X|X| | |X|X|X|X|X|
|X|X| | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| |X| |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| |X|X|X|X|X|X|X| |X|X|X|X|X|X|X|X|X|X|X|
| | |X|X|X|X|X| | | |X|X|X|X|X| |X|X|X| |
| | |X|X|X|X|X| | | |X|X|X|X| | | | | | |
| | | |X|X|X| | | | | |X|X|X| | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | |
| | |X|X| | | | | | | | |X| | | | | | | |
| | |X| | | | | | | | | |X| | | | | | | |
| | |X|X| | | | | | | | |X|X| | | | | | |
| | |X|X| | | | | | | | |X|X| | | | | | |
--- 0.02073383331298828 seconds for solve ---


Dalam hasrat untuk menguji lebih banyak teka-teki silang, ia dengan cepat membuat penyejajaran - setiap teka-teki silang direduksi menjadi kotak ketika kita mengisi garis yang tersisa dengan nol. Ya, itu tidak optimal, tetapi sekarang saya enggan untuk mengulang kode ...

Kuda - 30x38
22
13622799 clauses... -
350737 last var number
--- 14.18206787109375 seconds for clauses gen ---
1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X| | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | |
| | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X| |X|X| | |
| | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X|X|X|X| |
| | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X| |X|X|X|X|X|
| | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | | | |X| |
| | | | | | | | | | |X|X|X|X| | | | | | | | | |X|X|X|X|X|X|X|X| | | | | | | |
| | | | | | | | |X|X|X|X|X|X|X|X|X| | | |X|X|X|X|X|X|X|X|X|X| | | | | | | | |
| | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | |
| | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | | | |X|X|X| |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | | |X|X|X| | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | |X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | |
| |X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | |
|X|X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | |X|X| | | |
| |X|X| | | |X|X|X|X|X| | | | |X|X|X|X|X|X|X|X|X|X|X|X| | | | | | |X|X| | | |
| | |X| | | |X|X|X|X| |X| | | | | | |X|X|X|X|X|X| | | |X| | | | | |X|X| | | |
| | | | | |X|X|X|X| |X|X| | | | | | | | | | | | | |X|X|X| | | | | |X|X| | | |
| | | | |X|X|X|X| |X|X| | | | | | | | | | | | | | |X|X|X| | | | | |X|X| | | |
| | | | |X|X|X| | |X|X| | | | | | | | | | | | | | | |X|X| | | | | |X|X| | | |
| | | | |X|X| | | |X|X| | | | | | | | | | | | | | | |X|X| | | | |X|X|X| | | |
| | | |X|X|X| | | | |X|X| | | | | | | | | | | | | | |X|X| | | | |X|X| | | | |
| | | |X|X| | | | | |X|X| | | | | | | | | | | | | | |X|X| | | | |X| | | | | |
| | | |X|X| | | | | |X|X| | | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | |
| | | | |X|X|X| | | | | |X|X|X| | | | | | | | | | | | |X|X|X| | | | | | | | |

--- 17.535892963409424 seconds for solve ---



Dalam publikasi asli pada SAT-solver , kondisi dan variabel lebih jujur ​​dihasilkan - oleh karena itu, solusinya lebih cepat dan mungkin membutuhkan lebih sedikit memori. Ketika memutuskan kuda, dibutuhkan sekitar 2,7 GB RAM, dan membutuhkan beberapa hal rumit untuk semua 12 diinstal, dan kemudian mereka mulai dengan panik bertukar ke disk ...

Lebih dari kesimpulan:

1. Saya sadar mengapa contoh pycosat yang paling cepat di-google adalah solusi Sudoku. Aturan Sudoku jauh lebih mudah untuk digunakan pada solver, karena Anda tidak perlu menggambarkan hubungan kompleks antara blok dan sel, pada dasarnya hanya kondisi "setiap digit di setiap baris" dan "hanya satu digit di satu baris".
Tapi aku kurang mencintai sudoku, jadi aku tidak menyesali keputusan yang telah aku pilih.

2. Saya suka bagaimana Anda bisa mencampur DNF dan CNF. Jika Anda tidak ingin menulis kondisi yang tepat - jangan; cukup hasilkan semua opsi. Tentu saja ini tidak optimal, tetapi berhasil.

Tentu saja, saya tidak membuat rekor untuk kecepatan memecahkan teka-teki silang, tetapi saya memiliki waktu yang baik. Yang paling penting adalah saya bisa melakukannya! :) Apa yang saya harap juga!

All Articles