Chà, bạn có thể làm mọi thứ dễ dàng hơn một chút bằng cách sửa cú pháp:
def r(a):
i = a.find('0')
~i or exit(a)
[m in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3)or a[j]for j in range(81)] or r(a[:i]+m+a[i+1:])for m in'%d'%5**18]
from sys import *
r(argv[1])
Dọn dẹp một chút:
from sys import exit, argv
def r(a):
i = a.find('0')
if i == -1:
exit(a)
for m in '%d' % 5**18:
m in[(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3) or a[j] for j in range(81)] or r(a[:i]+m+a[i+1:])
r(argv[1])
Được rồi, vì vậy tập lệnh này mong đợi một đối số dòng lệnh và gọi hàm r trên đó. Nếu không có số 0 nào trong chuỗi đó, r sẽ thoát và in ra đối số của nó.
(Nếu một loại đối tượng khác được truyền, None tương đương với việc chuyển số 0 và bất kỳ đối tượng nào khác được in ra sys.stderr và dẫn đến mã thoát là 1. Cụ thể, sys.exit ("một số thông báo lỗi") là một cách nhanh chóng để thoát khỏi chương trình khi xảy ra lỗi. Xem
http://www.python.org/doc/2.5.2/lib/module-sys.html )
Tôi đoán điều này có nghĩa là số không tương ứng với không gian mở và một câu đố không có số không sẽ được giải. Sau đó, có biểu thức đệ quy khó chịu đó.
Vòng lặp là thú vị: for m in'%d'%5**18
Tại sao 5 ** 18? Nó chỉ ra rằng '%d'%5**18
đánh giá đến '3814697265625'
. Đây là một chuỗi có mỗi chữ số 1-9 ít nhất một lần, vì vậy có thể nó đang cố gắng đặt từng chữ số đó. Trên thực tế, có vẻ như đây là những gì r(a[:i]+m+a[i+1:])
đang làm: gọi đệ quy r, với ô trống đầu tiên được điền bởi một chữ số từ chuỗi đó. Nhưng điều này chỉ xảy ra nếu biểu thức trước đó là sai. Hãy xem xét điều đó:
m in [(i-j)%9*(i/9^j/9)*(i/27^j/27|i%9/3^j%9/3) or a[j] for j in range(81)]
Vì vậy, việc sắp xếp chỉ được thực hiện nếu m không có trong danh sách quái vật đó. Mỗi phần tử là một số (nếu biểu thức đầu tiên khác không) hoặc một ký tự (nếu biểu thức đầu tiên là 0). m được loại trừ là thay thế có thể xảy ra nếu nó xuất hiện dưới dạng ký tự, điều này chỉ có thể xảy ra nếu biểu thức đầu tiên bằng 0. Khi nào thì biểu thức bằng 0?
Nó có ba phần được nhân lên:
(i-j)%9
là 0 nếu i và j là bội số của 9 cách nhau, tức là cùng một cột.
(i/9^j/9)
là 0 nếu i / 9 == j / 9, tức là cùng một hàng.
(i/27^j/27|i%9/3^j%9/3)
là 0 nếu cả hai đều bằng 0:
i/27^j^27
bằng 0 nếu i / 27 == j / 27, tức là cùng một khối gồm ba hàng
i%9/3^j%9/3
bằng 0 nếu i% 9/3 == j% 9/3, tức là cùng một khối gồm ba cột
Nếu bất kỳ phần nào trong ba phần này bằng không, toàn bộ biểu thức bằng không. Nói cách khác, nếu tôi và j chia sẻ một hàng, cột hoặc khối 3x3, thì giá trị của j không thể được sử dụng làm ứng cử viên cho ô trống tại i. Aha!
from sys import exit, argv
def r(a):
i = a.find('0')
if i == -1:
exit(a)
for m in '3814697265625':
okay = True
for j in range(81):
if (i-j)%9 == 0 or (i/9 == j/9) or (i/27 == j/27 and i%9/3 == j%9/3):
if a[j] == m:
okay = False
break
if okay:
r(a[:i]+m+a[i+1:])
r(argv[1])
Lưu ý rằng nếu không có vị trí nào hoạt động hiệu quả, r sẽ quay lại và sao lưu đến điểm có thể chọn thứ gì đó khác, vì vậy, đó là thuật toán đầu tiên về chiều sâu cơ bản.
Không sử dụng bất kỳ heuristics nào, nó không đặc biệt hiệu quả. Tôi lấy câu đố này từ Wikipedia ( http://en.wikipedia.org/wiki/Sudoku ):
$ time python sudoku.py 530070000600195000098000060800060003400803001700020006060000280000419005000080079
534678912672195348198342567859761423426853791713924856961537284287419635345286179
real 0m47.881s
user 0m47.223s
sys 0m0.137s
Phụ lục: Tôi sẽ viết lại nó như thế nào với tư cách là một lập trình viên bảo trì (phiên bản này có tốc độ tăng gấp 93 lần :)
import sys
def same_row(i,j): return (i/9 == j/9)
def same_col(i,j): return (i-j) % 9 == 0
def same_block(i,j): return (i/27 == j/27 and i%9/3 == j%9/3)
def r(a):
i = a.find('0')
if i == -1:
sys.exit(a)
excluded_numbers = set()
for j in range(81):
if same_row(i,j) or same_col(i,j) or same_block(i,j):
excluded_numbers.add(a[j])
for m in '123456789':
if m not in excluded_numbers:
r(a[:i]+m+a[i+1:])
if __name__ == '__main__':
if len(sys.argv) == 2 and len(sys.argv[1]) == 81:
r(sys.argv[1])
else:
print 'Usage: python sudoku.py puzzle'
print ' where puzzle is an 81 character string representing the puzzle read left-to-right, top-to-bottom, and 0 is a blank'