a "dancing links" sudoku algorithm implemented in "pure" sql

Поиск
Список
Период
Сортировка
От big stone
Тема a "dancing links" sudoku algorithm implemented in "pure" sql
Дата
Msg-id CAJ-6aJQPxXEx8FtLJKsNgKSvBJRn0h3HYdw9xPSVU837gSN+Bg@mail.gmail.com
обсуждение исходный текст
Список pgsql-general
-- Weekend practice : 2012-09-30
--
-- having recently discovered the Common Table Expressions, I wanted to see if the SQL powerfull (threaded) motors
-- could run sophisticated algorithms (not "brute force") more quickly than classic langages.
--
-- result : "could" = yes, "more quickly" = no, but I'm  a postgresql beginner level and it is a vintage hardware
--
-- Does anyone has ideas to make the "dancing link" algorithm performant than the "brute" force ?
--
-- Do you have more favorable results for "dancing links" on your bigger hardware, compared to "brute" force  ?
--
-- ======================================================================
-- implementing the dancing link algorithm in pure sql to solve a sudoku
-- unfortunately : "dancing links" analyze 2067 possibilities only, but in 55 seconds
-- brute force is quicker : 18885 possibilities in 8 seconds
-- in python on same machine (centrino first generation, year 2003) , it's 3 seconds
-- ======================================================================

CREATE OR REPLACE FUNCTION whites(f text)
-- gives the table with the empty columns numbers
RETURNS SETOF integer
LANGUAGE SQL
AS $$
select gs FROM generate_series(1,length($1))  gs  where substr( $1, gs,  1 )=' ' ;
$$;

CREATE OR REPLACE function possibles(s text, ind integer)
-- gives the table of possible pieces to put in given "ind" position of the current "s" sudoku problem

RETURNS SETOF text
LANGUAGE SQL
AS $$

select z from   (SELECT gs::text AS z FROM generate_series(1,9) gs) z
  WHERE   NOT EXISTS ( SELECT NULL
                   FROM generate_series(1,9) lp
                   WHERE  z = substr( $1, ( ($2 - 1 ) / 9 ) * 9 + lp, 1 )
                   OR     z = substr( $1, mod( $2 - 1, 9 ) - 8 + lp * 9, 1 )
                   OR     z = substr( $1, mod( ( ( $2 - 1 ) / 3 ), 3 ) * 3
                                      + ( ( $2 - 1 ) / 27 ) * 27 + lp
                                      + ( ( lp - 1 ) / 3 ) * 6
                                   , 1 )
                 )
$$;


 CREATE OR REPLACE function recommands2(s text , holes integer)
-- gives the least possible positions to evaluate from initial "s" sudoku
-- if the initial "s" sudoku is discovered non-solvable, there is no output
RETURNS SETOF text
LANGUAGE SQL
AS $$

with

-- search the smallest group of solutions to try from initial "s" sudoku 
moves(posit, pion, nb, nblg , nbco, nbsq, line, colo, square) as(
select  dd, pp, least(score, nblg , nbco, nbsq) as nb, nblg ,
nbco, nbsq, line , colo, square from (
  select dd, pp , sum(100000  + dd ) over (partition by dd)  as score,
                  sum(110000+100*line+dd) over (partition by line, dd) as nblg ,
                  sum(120000+100*colo+dd) over (partition by colo, dd) as nbco,
                  sum(130000+100*square+dd) over (partition by square, dd) as nbsq ,
     line, colo, square from (select  d as dd , possibles($1, d)  as pp, (d-1)/9 as line,
      mod(d-1 , 9) as colo ,
      mod( ( ( d - 1 ) / 3 ), 3 )  * 3 +  ( ( d - 1 ) / 27 ) as square from (
      select  whites($1)    as d ) as ee
   ) tyty) dsgf) ,

-- we must be able to play all empty position of the initial "s" sudoku given
  unfounds(nb_nosol, nbmin) as (
    select $2 - count(*) , min(nb) from (
    select distinct  posit , nb from moves) as meme ) ,

-- we must be able to play as much different pieces in any given column, line, sub-square
-- as there are empty holes in these sub-parts of the of the initial "s" sudoku given
-- (as you can't play "more", a global check is sufficient, I suppose)
  unfound_lines(issues) as (
    select 3*$2 - sum(1)  as issues from  (select distinct  pion   r, line t from moves
                union all  select distinct  pion   r, colo  t from moves
                union all  select distinct  pion   r, square  t from moves ) dfdf ) 
-- gives minimal group of solutions to try, is current "s" sudoku is still a possible solution
select substr( $1, 1, a.posit - 1 ) || a.pion || substr( $1, a.posit + 1 ) from
 moves as a, unfounds, unfound_lines where a.nb=nbmin and nb_nosol=0 and issues=0
$$;     

 
     

WITH RECURSIVE
--'53  7    6  195    98    6 8   6   34  8 3  17   2   6 6    28    419  5    8  79' 2sec
--
-- 184 sec with dancing link vs 9 without
--'1    7 9  3  2   8  96  5    53  9   1  8   26    4   3      1  4      7  7   3  '
--'111111111222222222333333333444444444555555555666666666777777777888888888999999999'
sudoku(initial) as (select  (
'1    7 9  3  2   8  96  5    53  9   1  8   26    4   3      1  4      7  7   3  '::text)),

x( s, ind ) AS
( SELECT initial, length(initial) -length(replace(initial, ' ', '') )
  FROM   sudoku 
  UNION ALL
  (with
  reco(s, ind, posi) as (select x.s , x.ind, recommands(x.s) from x where ind>0)
  SELECT substr( s, 1, posi - 1 ) || possibles(s, posi) || substr( s, posi + 1 )
       , ind-1
  FROM   reco
  
  )
),




q( s, ind ) AS
( SELECT initial, length(initial) -length(replace(initial, ' ', '') )
  FROM   sudoku 
  UNION ALL
   SELECT recommands2(q.s, q.ind) , q.ind-1   FROM   q where ind>0
  
  )
 
SELECT * FROM q WHERE ind >= 0  order by ind -- 0= sudoku, >=0 to show all possibilities evaluated

-- "brute force" algorithm is  : http://archives.postgresql.org/pgsql-general/2009-11/msg00150.php

В списке pgsql-general по дате отправления:

Предыдущее
От: Szymon Guz
Дата:
Сообщение: Re: DROP CASCADE
Следующее
От: Alexander Farber
Дата:
Сообщение: Re: Would my postgresql 8.4.12 profit from doubling RAM?