Ну, ви можете полегшити ситуацію, виправивши синтаксис:
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])
Прибирання трохи:
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])
Гаразд, отже, цей сценарій очікує аргументу командного рядка і викликає на ньому функцію r. Якщо в цьому рядку немає нулів, r виходить і виводить свій аргумент.
(Якщо передано інший тип об'єкта, None еквівалентно передачі нуля, а будь-який інший об'єкт друкується в sys.stderr і призводить до коду виходу 1. Зокрема, sys.exit ("деяке повідомлення про помилку") є швидкий спосіб виходу з програми при виникненні помилки. Див.
http://www.python.org/doc/2.5.2/lib/module-sys.html )
Я думаю, це означає, що нулі відповідають відкритим просторам, і головоломка без нулів вирішена. Тоді є той неприємний рекурсивний вираз.
Цикл цікавий: for m in'%d'%5**18
Чому 5 ** 18? Виявляється, '%d'%5**18оцінює до '3814697265625'. Це рядок, в якому кожна цифра 1-9 є принаймні один раз, тому, можливо, він намагається розмістити кожну з них. Насправді, схоже, це те, що r(a[:i]+m+a[i+1:])робиться: рекурсивно викликає r, причому перша порожня частина заповнюється цифрою з цього рядка. Але це відбувається лише в тому випадку, якщо попередній вираз хибний. Давайте подивимось на це:
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)]
Тож розміщення здійснюється лише в тому випадку, якщо m немає у цьому списку монстрів. Кожен елемент є або числом (якщо перший вираз ненульовий), або символом (якщо перший вираз дорівнює нулю). m виключається як можлива заміна, якщо вона виглядає символом, що може статися лише за умови, що перший вираз дорівнює нулю. Коли вираз дорівнює нулю?
Він складається з трьох частин, які множаться:
(i-j)%9 що дорівнює нулю, якщо i та j кратні 9, тобто однаковий стовпець.
(i/9^j/9) що дорівнює нулю, якщо i / 9 == j / 9, тобто той самий рядок.
(i/27^j/27|i%9/3^j%9/3) що дорівнює нулю, якщо обидва вони дорівнюють нулю:
i/27^j^27 що дорівнює нулю, якщо i / 27 == j / 27, тобто той самий блок із трьох рядків
i%9/3^j%9/3 що дорівнює нулю, якщо i% 9/3 == j% 9/3, тобто той самий блок із трьох стовпців
Якщо будь-яка з цих трьох частин дорівнює нулю, весь вираз дорівнює нулю. Іншими словами, якщо i та j мають спільний рядок, стовпець або блок 3x3, тоді значення j не може бути використано як кандидат для порожнього місця на i. Ага!
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])
Зауважте, що якщо жодне з розташувань не спрацює, r повернеться і повернеться назад до точки, де можна вибрати щось інше, тому це базовий алгоритм спочатку глибини.
Не використовуючи жодної евристики, це не особливо ефективно. Я взяв цю головоломку з Вікіпедії ( http://en.wikipedia.org/wiki/Sudoku ):
$ time python sudoku.py 530070000600195000098000060800060003400803001700020006060000280000419005000080079
534678912672195348198342567859761423426853791713924856961537284287419635345286179
real 0m47.881s
user 0m47.223s
sys 0m0.137s
Додаток: Як би я переписав його як програміста з обслуговування (ця версія має швидкість 93x :)
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'