不是在 python 中解数独,而是在 python 包中解数独。
根据您的需求解决 python 包的版本是 NP 完全的,在最坏的情况下,它的运行速度会呈指数级缓慢。数独也是NP完全的,这意味着我们可以通过python封装来解决数独。
数独网格中的每个单元格都是一个包数sudoku_{x}_{y}
(0索引),版本(1-9)是字段中的值,因此您可以编写一个pyproject.toml,安装的包为解决方案。
[ project ]
name = " sudoku "
version = " 1.0.0 "
dependencies = [
" sudoku_3_1 == 2 " ,
" sudoku_5_7 == 6 " ,
" sudoku_0_7 == 5 "
...
]
您可以将数独写入 csv:
5,3,_,_,7,_,_,_,_
6,_,_,1,9,5,_,_,_
_,9,8,_,_,_,_,6,_
8,_,_,_,6,_,_,_,3
4,_,_,8,_,3,_,_,1
7,_,_,_,2,_,_,_,6
_,6,_,_,_,_,2,8,_
_,_,_,4,1,9,_,_,5
_,_,_,_,8,_,_,7,9
将其转换为需求:
python csv_to_requirements.py sudoku.csv requirements.in
sudoku_0_0 == 5
sudoku_1_0 == 3
[...]
sudoku_7_8 == 7
sudoku_8_8 == 9
使用您最喜欢的包管理器解决它,例如:
uv pip compile --find-links packages/ --no-annotate --no-header requirements.in > requirements.txt
或(慢)
pip-compile --find-links packages/ --no-annotate --no-header requirements.in -o requirements.txt
sudoku-0-0==5
sudoku-0-1==6
sudoku-0-2==1
sudoku-0-3==8
sudoku-0-4==4
[...]
渲染解决方案:
python render_solution.py requirements.txt
5,3,4,6,7,8,9,1,2
6,7,2,1,9,5,3,4,8
1,9,8,3,4,2,5,6,7
8,5,9,7,6,1,4,2,3
4,2,6,8,5,3,7,9,1
7,1,3,9,2,4,8,5,6
9,6,1,5,3,7,2,8,4
2,8,7,4,1,9,6,3,5
3,4,5,2,8,6,1,7,9
或者作为单行:
$ python csv_to_requirements.py royle.csv - | uv pip compile --find-links packages/ --no-annotate --no-header - | python render_solution.py -
Resolved 81 packages in 126ms
5,3,4,6,7,8,9,1,2
6,7,2,1,9,5,3,4,8
1,9,8,3,4,2,5,6,7
8,5,9,7,6,1,4,2,3
4,2,6,8,5,3,7,9,1
7,1,3,9,2,4,8,5,6
9,6,1,5,3,7,2,8,4
2,8,7,4,1,9,6,3,5
3,4,5,2,8,6,1,7,9
$ hyperfine --warmup 5 "uv pip compile --find-links packages/ --no-index --no-annotate --no-header requirements.in"
Benchmark 1: uv pip compile --find-links packages/ --no-index --no-annotate --no-header requirements.in
Time (mean ± σ): 29.7 ms ± 1.6 ms [User: 29.9 ms, System: 21.0 ms]
Range (min … max): 27.5 ms … 35.0 ms 97 runs
我尝试过的数独之间的间隔时间没有太大差异。
虽然依赖解析是 NP 完全的,并且它们的支持算法通常是某种形式的 SAT 求解器,但实际上问题空间要小得多:包通常依赖于另一个包的单个范围,并且该范围通常单调增加。你几乎永远不会遇到指数情况,Python 中的大多数解析甚至不需要回溯就可以完成。相反,性能瓶颈在于获取和解析元数据,以及专门构建源代码发行版的 python。