数独优化求解C库tdoku-lib的使用

tdoku-lib是基于优化求解器tdoku改造的动态库和静态库,它的存储库地址 https://github.com/hackerzhuli/tdoku-lib

1.拉取源代码

复制代码
root@DESKTOP-59T6U68:/mnt/c/d# git clone https://github.com/hackerzhuli/tdoku-lib.git

Cloning into 'tdoku-lib'...
remote: Enumerating objects: 1230, done.
remote: Counting objects: 100% (339/339), done.
remote: Compressing objects: 100% (77/77), done.
remote: Total 1230 (delta 288), reused 284 (delta 255), pack-reused 891 (from 1)
Receiving objects: 100% (1230/1230), 72.96 MiB | 3.20 MiB/s, done.
Resolving deltas: 100% (885/885), done.
Updating files: 100% (27/27), done.

2.编译

复制代码
root@DESKTOP-59T6U68:/mnt/c/d# cd tdoku-lib
root@DESKTOP-59T6U68:/mnt/c/d/tdoku-lib# docker start gcc
gcc
root@DESKTOP-59T6U68:/mnt/c/d/tdoku-lib# docker exec -it gcc bash
root@6ae32a5ffcde:/# cd /par/tdoku-lib/
root@6ae32a5ffcde:/par/tdoku-lib# ./BUILD.sh
-- The C compiler identification is GNU 14.2.0
-- The CXX compiler identification is GNU 14.2.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/local/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /par/tdoku-lib/build
[  6%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_simd.cc.o
[ 12%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_basic.cc.o
[ 18%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_scc.cc.o
/par/tdoku-lib/src/solver_dpll_triad_scc.cc:20:11: error: 'uint16_t' does not name a type
   20 | constexpr uint16_t kNumLiterals = kNumBoxes * kNumPosClausesPerBox * kNumValues * 2;
      |           ^~~~~~~~
/par/tdoku-lib/src/solver_dpll_triad_scc.cc:11:1: note: 'uint16_t' is defined in header '<cstdint>'; this is probably fixable by adding '#include <cstdint>'
   10 | #include <vector>
  +++ |+#include <cstdint>
   11 |

...
/par/tdoku-lib/src/solver_dpll_triad_scc.cc:526:38: error: 'Literal' was not declared in this scope
  526 |                 if (result_.asserted[Literal(box, elm, val)]) {
      |                                      ^~~~~~~
/par/tdoku-lib/src/solver_dpll_triad_scc.cc: At global scope:
/par/tdoku-lib/src/solver_dpll_triad_scc.cc:540:65: error: 'uint32_t' has not been declared
  540 | size_t TdokuSolverDpllTriadScc(const char *input, size_t limit, uint32_t configuration,
      |                                                                 ^~~~~~~~
make[2]: *** [CMakeFiles/tdoku_object.dir/build.make:104: CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_scc.cc.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:93: CMakeFiles/tdoku_object.dir/all] Error 2
make: *** [Makefile:91: all] Error 2

编译出错。

按照编译的提示,在solver_dpll_triad_scc.cc的开头加上#include <cstdint>,重新编译通过

复制代码
root@6ae32a5ffcde:/par/tdoku-lib# ./BUILD.sh
...
[  6%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_simd.cc.o
[ 12%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_basic.cc.o
[ 18%] Building CXX object CMakeFiles/tdoku_object.dir/src/solver_dpll_triad_scc.cc.o
[ 25%] Building CXX object CMakeFiles/tdoku_object.dir/src/util.cc.o
[ 31%] Building CXX object CMakeFiles/tdoku_object.dir/src/generate.cc.o
[ 37%] Building CXX object CMakeFiles/tdoku_object.dir/src/solve.cc.o
make[2]: warning:  Clock skew detected.  Your build may be incomplete.
[ 37%] Built target tdoku_object
[ 43%] Linking CXX static library libtdoku_static.a
[ 43%] Built target tdoku_static
[ 50%] Linking CXX shared library libtdoku_shared.so
[ 50%] Built target tdoku_shared
[ 56%] Building CXX object CMakeFiles/run_tests.dir/test/run_tests.cc.o
[ 62%] Building CXX object CMakeFiles/run_tests.dir/src/util.cc.o
[ 68%] Building CXX object CMakeFiles/run_tests.dir/src/solver_dpll_triad_simd.cc.o
[ 75%] Linking CXX executable run_tests
[ 75%] Built target run_tests
[ 81%] Building CXX object CMakeFiles/grid_lib.dir/src/grid_lib.cc.o
[ 87%] Linking CXX static library libgrid_lib.a
[ 87%] Built target grid_lib
[ 93%] Building CXX object CMakeFiles/grid_tools.dir/src/grid_tools.cc.o
[100%] Linking CXX executable grid_tools
[100%] Built target grid_tools
make[1]: warning:  Clock skew detected.  Your build may be incomplete.
make: warning:  Clock skew detected.  Your build may be incomplete.
root@6ae32a5ffcde:/par/tdoku-lib#

3.执行

执行编译出来的run_tests文件,显示PASS: tdoku

复制代码
root@6ae32a5ffcde:/par/tdoku-lib/build# ./run_tests
Error opening test/test_puzzles
root@6ae32a5ffcde:/par/tdoku-lib/build# cd ..
root@6ae32a5ffcde:/par/tdoku-lib# build/run_tests
PASS: tdoku

再编译示例文件,

c 复制代码
#include "../include/tdoku.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, const char **argv) {
    size_t limit = argc > 1 ? atoll(argv[1]) : 10000;

    char *puzzle = NULL;
    char solution[81];
    size_t size, guesses;

    while (getline(&puzzle, &size, stdin) != -1) {
        if (strlen(puzzle) < 81 || puzzle[0] == '#') continue;
        solution[0] = '\0';
        size_t count = SolveSudoku(puzzle, limit, 0, solution, &guesses);
        if (limit > 1 && count == 1) {
            SolveSudoku(puzzle, 1, 0, solution, &guesses);
        }
        printf("%.81s:%ld:%.81s\n", puzzle, count, solution);
    }
}

slove求解文件中的数独题目,空格用"."表示。结果输出到控制台,格式是题目:解序号:解

复制代码
root@6ae32a5ffcde:/par/tdoku-lib# gcc example/solve.c build/libtdoku_static.a -O3 -o solve -lstdc++ -lm
root@6ae32a5ffcde:/par/tdoku-lib# unzip data.zip
root@6ae32a5ffcde:/par/tdoku-lib# ./solve < data/puzzles0_kaggle
..2..76416712.....5..3..28.46......9.35.18.6...7.93..4.9.5.61...5..7...3..48...2.:1:382957641671284935549361287468725319935418762127693854293546178856172493714839526
...6.2..9.3..1.6..1.238.5.4.15...96....94.....27..38.58.42..3.7.....8.....6.7125.:1:748652139539714682162389574415827963683945721927163845854296317271538496396471258
^C

示例还有python程序

python 复制代码
import sys
from ctypes import *

class Tdoku:
    def __init__(self):
        self.__tdoku = CDLL("build/libtdoku_shared.so")

        self.__solve = self.__tdoku.TdokuSolverDpllTriadSimd
        self.__solve.restype = c_ulonglong

        self.__constrain = self.__tdoku.TdokuConstrain
        self.__constrain.restype = c_bool

        self.__minimize = self.__tdoku.TdokuMinimize
        self.__minimize.restype = c_bool

    def Solve(self, puzzle):
        if type(puzzle) is str:
            puzzle = str.encode(puzzle)
        limit = c_ulonglong(1)
        config = c_ulong(0)
        solution = create_string_buffer(81)
        guesses = c_ulonglong(0)
        count = self.__solve(c_char_p(puzzle), limit, config, solution, pointer(guesses))
        if count:
            return count, solution.value.decode(), guesses.value
        else:
            return 0, "", guesses

    def Count(self, puzzle, limit=2):
        if type(puzzle) is str:
            puzzle = str.encode(puzzle)
        limit = c_ulonglong(limit)
        config = c_ulong(0)
        solution = create_string_buffer(81)
        guesses = c_ulonglong(0)
        count = self.__solve(c_char_p(puzzle), limit, config, solution, pointer(guesses))
        return count

    def Constrain(self, partial_puzzle):
        if type(partial_puzzle) is str:
            buffer = create_string_buffer(str.encode(partial_puzzle))
        else:
            buffer = create_string_buffer(partial_puzzle)
        pencilmark = c_bool(False)
        self.__constrain(pencilmark, buffer)
        return buffer.value

    def Minimize(self, non_minimal_puzzle):
        if type(non_minimal_puzzle) is str:
            buffer = create_string_buffer(str.encode(non_minimal_puzzle))
        else:
            buffer = create_string_buffer(non_minimal_puzzle)
        pencilmark = c_bool(False)
        monotonic = c_bool(False)
        self.__minimize(pencilmark, monotonic, buffer)
        return buffer.value


if __name__ == '__main__':
    tdoku = Tdoku()

    if len(sys.argv) > 1:
        filename = sys.argv[1]
    else:
        filename = 'data/puzzles2_17_clue'

    with open(filename, 'r') as f:
        for puzzle in f.readlines():
            if len(puzzle) >= 81 and not puzzle.startswith('#'):
                count, solution, guesses = tdoku.Solve(puzzle)
                print("%.81s:%lu:%.81s:%lu" % (puzzle, count, solution, guesses))

这次还多出来一列猜测数

复制代码
root@6ae32a5ffcde:/par/tdoku-lib# python3 example/solve.py data/puzzles0_kaggle|more
.5..83.17...1..4..3.4..56.8....3...9.9.8245....6....7...9....5...729..861.36.72.4:1:652483917978162435314975628825736149791824563436519872269348751547291386183657294:0
2.6.3......1.65.7..471.8.5.5......29..8.194.6...42...1....428..6.93....5.7.....13:1:256734198891265374347198652514683729728519436963427581135942867689371245472856913:0

Traceback (most recent call last):
  File "/par/tdoku-lib/example/solve.py", line 73, in <module>
    print("%.81s:%lu:%.81s:%lu" % (puzzle, count, solution, guesses))
BrokenPipeError: [Errno 32] Broken pipe
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
BrokenPipeError: [Errno 32] Broken pip

用49150道17个未知数的题目测试性能,并与dlx程序比较

复制代码
root@6ae32a5ffcde:/par/tdoku-lib# time ./solve < data/puzzles2_17_clue >17result.txt

real    0m0.622s
user    0m0.229s
sys     0m0.017s

root@6ae32a5ffcde:/par/1230# time ./dlxline <sudoku17.txt >sudoku17outdlx.txt

real    0m1.568s
user    0m1.122s
sys     0m0.073s

tdoku用时只有dlx的40%。采用不同的文件是因为两个程序要求的空格符不同,一个是.,另一个是0。

相关推荐
有一个好名字2 小时前
力扣-奇偶链表
算法·leetcode·链表
wxm6312 小时前
力扣算法题(C++):1、2
java·算法·leetcode
带土12 小时前
8. C++ explicit 关键字
开发语言·c++
im_AMBER2 小时前
Leetcode 103 反转链表 II
数据结构·c++·笔记·学习·算法·leetcode
iYun在学C2 小时前
驱动程序(创建设备节点实验)
linux·c语言·嵌入式硬件
人道领域2 小时前
【零基础学java】(TCP协议)
java·开发语言·tcp/ip
小二·2 小时前
Python Web 开发进阶实战:微前端架构初探 —— 基于 Webpack Module Federation 的 Vue 微应用体系
前端·python·架构
rgeshfgreh2 小时前
回溯算法精解:排列、子集与组合
python·算法·深度优先