Hello

The SQLite version is 3.8.4 by now and this is stil about the sudoku solving. I studied Norvigs algorithm (Python) http://norvig.com/sudoku.html . It is possible to achieve this method in SQL.

A very important difference between Norvigs programme and the SQLite example is however that Norvig only reporst the first solution (assuming the sudoku has just one solution). Changing my SQLIte code by adding LIMIT 1 immediately makes it much more competitive. To achieve Norvigs algorithm I need to choose the cell with least remaining choices. The Python program maintains a list of remaining choices for each cell. In my SQL, using bitmaps, the number of choices is expressed as:

                        (w10&peers0 or w11&peers1) +
                        (w20&peers0 or w21&peers1) +
                        (w30&peers0 or w31&peers1) +
                        (w40&peers0 or w41&peers1) +
                        (w50&peers0 or w51&peers1) +
                        (w60&peers0 or w61&peers1) +
                        (w70&peers0 or w71&peers1) +
                        (w80&peers0 or w81&peers1) +
                        (w90&peers0 or w91&peers1)

The complete programme is below. In my tests it needs 0.2 seconds for the SQLite example from http://www.sqlite.org/lang_with.html#sudoku

A novelty (comapred to Norvigs algorithm) is the ordering in which candidate digist are generated. I pick the digit that occurs most as first. That is intuitivelythe the way that I solve sudoku's by hand. This extra ordering (by zfixed desc) makes the time for the SQLIte example sudoku worse. But ..

the ;hard1' example from Norvigs code is solved in a fraction of a second. (by the way this example is ambigu).

Also your (Bigstone's) eastermonster ('1 ....... 2.9.4 ...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1') is solved within a second.

Finally here is the code (but it is not suited for educational purpose)

Regards, Edzard

create temporary table i ( --
        i integer primary key, -- sudoku cell (1..81)
        name char, -- A1..H8
        word0, -- only bit i is set, i = 1..54
        word1, -- only bit i-54 is set, i = 55..81
        peers0, peers1) -- bitmap of neighbour cells
;
insert into i
select  i,
        char(65+y)||(x+1) as name,
        case when iword=0 then 1<<ibit else 0 end AS word0,
        case when iword=1 then 1<<ibit else 0 end AS word1,
        --peers0
            --horizontal
            CASE WHEN iword=0
            THEN (512-1)<<(y*9)
            ELSE 0
            END |
            --vertical
            ((1+512*(1+512*(1+512*(1+512*(1+512)))))<<x) |
            --box
            CASE WHEN iword=0
            THEN ((8-1)*(1+512*(1+512)))<<(y/3*3*9+x/3*3)
            ELSE 0
            END
            AS peers0,
        --peers1
            --horizontal
            CASE WHEN iword=1
            THEN (512-1)<<((y%6)*9)
            ELSE 0
            END |
            --vertical
            ((1 + 512 * (1 + 512)) << x) |
            --box
            CASE WHEN iword=1
            THEN ((8-1)*(1+512*(1+512)))<<((y%6)/3*3*9+x/3*3)
            ELSE 0
            END
            AS peers1
from    (
with xy AS (SELECT 0 AS xy UNION ALL SELECT xy+1 FROM xy WHERE xy < 80)
    select  xy+1 AS i,
            xy%9 AS x,
            xy/9 AS y,
            xy/54 AS iword,
            xy%54 AS ibit
    from    xy
        )
;

.timer on
with z AS (
    SELECT 1 AS z UNION ALL SELECT z+1 FROM z WHERE z < 9
        )
,
input as (
    select  input,
            sud,
            ifnull (sum (word0), 0) as w0,
            ifnull (sum (word1), 0) as w1,
            ifnull (sum (case when z=1 then word0 end), 0) as w10,
            ifnull (sum (case when z=1 then word1 end), 0) as w11,
            ifnull (sum (case when z=2 then word0 end), 0) as w20,
            ifnull (sum (case when z=2 then word1 end), 0) as w21,
            ifnull (sum (case when z=3 then word0 end), 0) as w30,
            ifnull (sum (case when z=3 then word1 end), 0) as w31,
            ifnull (sum (case when z=4 then word0 end), 0) as w40,
            ifnull (sum (case when z=4 then word1 end), 0) as w41,
            ifnull (sum (case when z=5 then word0 end), 0) as w50,
            ifnull (sum (case when z=5 then word1 end), 0) as w51,
            ifnull (sum (case when z=6 then word0 end), 0) as w60,
            ifnull (sum (case when z=6 then word1 end), 0) as w61,
            ifnull (sum (case when z=7 then word0 end), 0) as w70,
            ifnull (sum (case when z=7 then word1 end), 0) as w71,
            ifnull (sum (case when z=8 then word0 end), 0) as w80,
            ifnull (sum (case when z=8 then word1 end), 0) as w81,
            ifnull (sum (case when z=9 then word0 end), 0) as w90,
            ifnull (sum (case when z=9 then word1 end), 0) as w91,
            count (*) as fixed,
            ifnull (sum (z=1), 0) as f1,
            ifnull (sum (z=2), 0) as f2,
            ifnull (sum (z=3), 0) as f3,
            ifnull (sum (z=4), 0) as f4,
            ifnull (sum (z=5), 0) as f5,
            ifnull (sum (z=6), 0) as f6,
            ifnull (sum (z=7), 0) as f7,
            ifnull (sum (z=8), 0) as f8,
            ifnull (sum (z=9), 0) as f9
    from    (
        select 'easy1' as input,
'53 .. 7 .... 6 ..195....98....6.8...6...34..8.3..17...2...6.6....28....419..5....8..79'
                    as sud
        union all
        select 'sqlite1',
'1 .... 7.9 .. 3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..'
        union all
        select 'hard1',
'..... 6 .... 59.....82....8....45........3........6..3.54...325..6..................'
        union all
        select 'hola1',
'4.2 .... 3.1 ..6.5.299.....1......42....8.9.1.5....85......3.....861.5.4..2.4....5.7'
        union all
        select 'hardest1',
'8 .......... 36......7..9.2...5...7.......457.....1...3...1....68..85...1..9....4..'
        union all
        select 'eastermonster1',
'1 ....... 2.9.4 ...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1'
            )
    join    i
    join    z on z = cast (substr (sud, i.i, 1) as int)
    where   input='sqlite1'
        )
,
sudoku as (
    select  '' as text,
            fixed,
            f1,f2,f3,f4,f5,f6,f7,f8,f9,
            0 as zfixed,
            0 as i0,
            w0, w1,
            w10, w11,
            w20, w21,
            w30, w31,
            w40, w41,
            w50, w51,
            w60, w61,
            w70, w71,
            w80, w81,
            w90, w91
    from    input
    union all
    select  --text
                (fixed+1)||','||
                name||','||
                (
                        (w10&peers0 or w11&peers1) +
                        (w20&peers0 or w21&peers1) +
                        (w30&peers0 or w31&peers1) +
                        (w40&peers0 or w41&peers1) +
                        (w50&peers0 or w51&peers1) +
                        (w60&peers0 or w61&peers1) +
                        (w70&peers0 or w71&peers1) +
                        (w80&peers0 or w81&peers1) +
                        (w90&peers0 or w91&peers1)
                        ) ||','||
                z||','||
                '',
            fixed+1,
            f1+(z=1),
            f2+(z=2),
            f3+(z=3),
            f4+(z=4),
            f5+(z=5),
            f6+(z=6),
            f7+(z=7),
            f8+(z=8),
            f9+(z=9),
            --zfixed
                case z
                when 1 then f1
                when 2 then f2
                when 3 then f3
                when 4 then f4
                when 5 then f5
                when 6 then f6
                when 7 then f7
                when 8 then f8
                when 9 then f9
                end,
            i.i,
            w0+word0,
            w1+word1,
            case when z=1 then  w10+word0 else w10 end,
            case when z=1 then  w11+word1 else w11 end,
            case when z=2 then  w20+word0 else w20 end,
            case when z=2 then  w21+word1 else w21 end,
            case when z=3 then  w30+word0 else w30 end,
            case when z=3 then  w31+word1 else w31 end,
            case when z=4 then  w40+word0 else w40 end,
            case when z=4 then  w41+word1 else w41 end,
            case when z=5 then  w50+word0 else w50 end,
            case when z=5 then  w51+word1 else w51 end,
            case when z=6 then  w60+word0 else w60 end,
            case when z=6 then  w61+word1 else w61 end,
            case when z=7 then  w70+word0 else w70 end,
            case when z=7 then  w71+word1 else w71 end,
            case when z=8 then  w80+word0 else w80 end,
            case when z=8 then  w81+word1 else w81 end,
            case when z=9 then  w90+word0 else w90 end,
            case when z=9 then  w91+word1 else w91 end
    from    sudoku
    join    i
    on      i = ifnull (
                        (
                    select  i
                    from    i
                    where   i > i0
                    and     not (word0&w0 or word1&w1)
                    and     (
                                (w10&peers0 or w11&peers1) +
                                (w20&peers0 or w21&peers1) +
                                (w30&peers0 or w31&peers1) +
                                (w40&peers0 or w41&peers1) +
                                (w50&peers0 or w51&peers1) +
                                (w60&peers0 or w61&peers1) +
                                (w70&peers0 or w71&peers1) +
                                (w80&peers0 or w81&peers1) +
                                (w90&peers0 or w91&peers1)
                             ) = 8
                    limit 1
                        )
                ,

                (
            select  i
            from    (
                select  i,
                        max (
                            (w10&peers0 or w11&peers1) +
                            (w20&peers0 or w21&peers1) +
                            (w30&peers0 or w31&peers1) +
                            (w40&peers0 or w41&peers1) +
                            (w50&peers0 or w51&peers1) +
                            (w60&peers0 or w61&peers1) +
                            (w70&peers0 or w71&peers1) +
                            (w80&peers0 or w81&peers1) +
                            (w90&peers0 or w91&peers1)
                            ) as maxfixed
                from    i
                where   not (word0&w0 or word1&w1)
                    )
            where     maxfixed is not null -- break sqlite optimizer
                ))
    join    z
    on
                case z
                when 1 then not (w10&peers0 or w11&peers1)
                when 2 then not (w20&peers0 or w21&peers1)
                when 3 then not (w30&peers0 or w31&peers1)
                when 4 then not (w40&peers0 or w41&peers1)
                when 5 then not (w50&peers0 or w51&peers1)
                when 6 then not (w60&peers0 or w61&peers1)
                when 7 then not (w70&peers0 or w71&peers1)
                when 8 then not (w80&peers0 or w81&peers1)
                when 9 then not (w90&peers0 or w91&peers1)
                end
    order by fixed desc, --depth first
            zfixed desc --most used digit first
        )
,
output as (
    select  *
    from    (
        select  1 as i,
                w10, w11,
                w20, w21,
                w30, w31,
                w40, w41,
                w50, w51,
                w60, w61,
                w70, w71,
                w80, w81,
                w90, w91,
                '' as sud
        from    sudoku
        where   fixed = 81 limit 1
            )
    union all
    select  nullif (output.i + 1, 82),
            w10, w11,
            w20, w21,
            w30, w31,
            w40, w41,
            w50, w51,
            w60, w61,
            w70, w71,
            w80, w81,
            w90, w91,
            sud || replace (cast (
                case 1
                when w10&word0 OR w11&word1 then 1
                when w20&word0 OR w21&word1 then 2
                when w30&word0 OR w31&word1 then 3
                when w40&word0 OR w41&word1 then 4
                when w50&word0 OR w51&word1 then 5
                when w60&word0 OR w61&word1 then 6
                when w70&word0 OR w71&word1 then 7
                when w80&word0 OR w81&word1 then 8
                when w90&word0 OR w91&word1 then 9
                else 0
                end
                as char), '0', '.')
    from    output
    join    i on i.i = output.i
        )
--select  * from input
--select text from sudoku-- where fixed = 81 limit 1
select substr (sud, z*9-8, 3) as s1, substr (sud, z*9-5, 3) as s2, substr (sud, z*9-2, 3) as s3 from ( select 1 as io, sud from input union all select 2 as io, sud from output where i is null) sud join z order by io, z
;

_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to