Don't say postgresql is slower : - I remember now I did then implement the "norvig postgresql", - and it manages a 20% win over your solution with today postgresql 9.3.2.3.
Anyway, it's indeed a "less inefficient" run for now between SQL motors. It remains to be seen if improving SQLite in sudoku solving will improve it for other existing (or not yet imagined) workloads. ** the "norvig" PostgresSQL code ** -- implementing several "logical" algorithms in SQL to solve a sudoku -- -- Norvig algorithm from Peter Norvig, http://norvig.com/sudoku.html -- -- Brut Force algorithm from Anton Scheffer, http://technology.amis.nl/2009/10/13/oracle-rdbms-11gr2-solving-a-sudoku-using-recursive-subquery-factoring/ -- translated per Marcin Mank, http://archives.postgresql.org/pgsql-general/2009-11/msg00150.php -- -- Norvig Algorithm = -- . from given Sudoku problem, generate an array with each cell of the sudoku being a string with all remaining possibilites -- . define as "neighbors" of a cell, all cells which must not have the same piece as this cells, (there are 20 neighbors to each cell) -- . then analyse the cell (the string) have the less remaining solutions, but more than 1, -- .. for a given sudoku position, "play" only on the first cell you found with the less possibilities, and try each of them, -- ... replace in this cell the string by the possible piece you want to try, -- ... remove the piece you played from the neighbors cells own possibilities (as it's no more a possibility for them) -- ... when removing a possibility to a neighbor, if it leave him only on possibility, "play" it immediately, -- ... if you remove all the solutions from a neighbor, than you're not on the sudoku solution path, stop this track. -- -- Brut force Algorithm = -- . try all possible piece in all empty position of the sudoku problem, from top left to bottom right of the sudoku board cells, -- . stop a track each time you can't fill the next position -- -- Speed result : -- . brute force loose on the easy and complex sudo, wins on the medium -- . "Brut" and "Norvig" algorithms seem to stay mono-thread, giving-up the competitive advantage of SQL -- ====================================================================== -- Array usage in SQL ( http://www.postgresql.org/docs/9.2/static/functions-array.html) -- array length (on a dimension) :array_length(anyarray, dimension_integer) -- array to rowset : select unnest(ARRAY[1,2]) -- rowset to array : ARRAY(SELECT * FROM test) -- array to string : select array_to_string(ARRAY[1, 2, 3, NULL, 5], ',', '*') -- string to array : select string_to_array('xx~^~yy~^~zz', '~^~', 'yy') CREATE OR REPLACE function format_string_sudoku(board text []) -- displays a sudoku "board" in 9x9 form with delimiters (so 13 line records) RETURNS setof text LANGUAGE SQL AS $$ with recursive formatted_string(ss , ind ) as (select '' , 81 FROM generate_series(1,1) lp union all select (case when length($1[ind])=1 then $1[ind] else '_' end) || (case when ind % 81=0 then '|<CRLF>=============' when ind % 27=0 then '|<CRLF>|-----------|<CRLF>|' when ind % 9=0 then '|<CRLF>|' when ind % 3=0 then '|' else '' end) || ss , ind-1 from formatted_string where ind>0 ) select unnest(string_to_array( '=============<CRLF>|' || ss, '<CRLF>')) from formatted_string where ind=0 $$; CREATE OR REPLACE function neighbors(cell integer) -- gives back the rowset of neighbors of the given "cell" of any sudoku board RETURNS SETOF integer LANGUAGE SQL AS $$ with list_of(neighbors) as (select unnest(ARRAY[ mod( $1 - 1, 9 ) - 8 + lp * 9 , ( ($1 - 1 ) / 9 ) * 9 + lp , mod( ( ( $1 - 1 ) / 3 ), 3 ) * 3 + ( ( $1 - 1 ) / 27 ) * 27 + lp + ( ( lp - 1 ) / 3 ) * 6]) FROM generate_series(1,9) lp ) select distinct * from list_of where neighbors<>$1 $$; CREATE OR REPLACE function truly_possibles(board text, cell integer) -- gives the table of possible pieces to put in given "cell" of the current sudoku "board" -- including the one already in place, if it exists RETURNS SETOF text LANGUAGE SQL AS $$ select z from (SELECT gs::text AS z FROM generate_series(1,9) gs) z WHERE substr( $1, $2 ,1)=' ' and NOT EXISTS ( SELECT NULL FROM neighbors($2) lp WHERE z = substr( $1, lp , 1 ) ) union select substr( $1, $2 ,1 ) z where substr( $1, $2 ,1 )<>' ' $$; create or replace function create_sudoku_board(initial text) returns setof text[] -- create a sudoku board from the given "s" (string form) sudoku problem language sql as $$ with recursive -- handle several string presentations of the sudoku board problem to solve cleaned_starter(starter) as (select regexp_replace(regexp_replace(initial,'\n|\=|\-|\|', '' ,'g' ),'\.',' ','g' )), -- recursively generate the board from the bottom right cell to the top left of the sudoku board board_generator( t , board , next_cell ) AS ( SELECT starter, array[array_to_string(array(select * from truly_possibles(starter , length(starter) )),'')] , length(starter)-1 from cleaned_starter UNION ALL SELECT t , array[array_to_string(array(select * from truly_possibles(t , next_cell)),'')] ||board, next_cell-1 FROM board_generator where next_cell>0 ) select board from board_generator where next_cell=0 $$; create or replace function next_move(board text []) returns integer -- select a position of the given board with the less solutions (but more than 1) language sql as $$ with recursive -- scan recursively all cells to find the best_cell with the least solutions (over 1) -- don't continue the scanning if you find an ideal "2 solutions" cell before the end board_scanning( best_cell , best_count , this_cell ) AS ( SELECT 0, 99 , array_length(board,1) UNION ALL SELECT (case when this_length between 2 and best_count-1 then this_cell else best_cell end), (case when this_length between 2 and best_count-1 then length(board[this_cell]) else best_count end), (case when this_length=2 then 0 else this_cell-1 end) from (select length(board[this_cell]) this_length , board_scanning.* FROM board_scanning where this_cell>0 ) temp ) select best_cell from board_scanning where this_cell=0 $$; create or replace function play_move(board text [] , best_cell integer, piece text) returns setof text[] -- on the current sudoku "board", play in "best_cell" the given "piece" language sql as $$ -- put the piece in the given best_cell -- then remove that piece from all neighbors possibilities -- if it was the last freedom of a given neighbor, then recursively play the "no_freedom" piece on that neighbor with recursive board_updating( s , neighbor_index , neighborhood ) AS ( -- put the piece in the given best_cell, and prepare to scan the 20 neighbors SELECT board[1:(best_cell-1)] ||array[piece] ||board[(best_cell+1):999], 20 as neighbor_index , array(select * from neighbors(best_cell) as erer) UNION ALL SELECT (case when neighbor_new_freedom=1 and neighbor_old_freedom=2 then -- if it was the last freedom of a given neighbor, then recursively play the "no_freedom" piece on that neighbor play_move(s_after, neighborhood[neighbor_index], s_after[neighborhood[neighbor_index]]) else -- if it not the last freedom of a given neighbor, just proceed on next neighbor s_after end), neighbor_index-1 , neighborhood from ( -- remove the played piece from all neighbors possibilities select s[1:(neighborhood[neighbor_index]-1)] ||replace(s[neighborhood[neighbor_index]], $3, '') ||s[(neighborhood[neighbor_index]+1):999] as s_after, length(replace(s[neighborhood[neighbor_index]], $3, '')) as neighbor_new_freedom, length(s[neighborhood[neighbor_index]]) as neighbor_old_freedom, neighborhood, neighbor_index FROM board_updating where neighbor_index>0 ) as q2 where neighbor_new_freedom > 0 ) -- play induced moves select s from board_updating where neighbor_index=0 -- and s not empty ? $$; --------------------------------- create or replace function solve_a_sudoku_with_brutforce(initial text) returns setof text -- our problem to solve language sql as $$ with recursive -- handle several string presentations of the sudoku board problem to solve cleaned_starter(starter) as (select regexp_replace(regexp_replace(initial,'\n|\=|\-|\|', '' ,'g' ),'\.',' ','g' )), x( s, ind ) as ( select starter, position( ' ' in starter ) from cleaned_starter union all select substr( s, 1, ind - 1 ) || z || substr( s, ind + 1 ) , position(' ' in repeat('x',ind) || substr( s, ind + 1 ) ) from x , (select gs::text as z from generate_series(1,9) gs)z where ind > 0 and not exists ( select null from generate_series(1,9) lp where z.z = substr( s, ( (ind - 1 ) / 9 ) * 9 + lp, 1 ) or z.z = substr( s, mod( ind - 1, 9 ) - 8 + lp * 9, 1 ) or z.z = substr( s, mod( ( ( ind - 1 ) / 3 ), 3 ) * 3 + ( ( ind - 1 ) / 27 ) * 27 + lp + ( ( lp - 1 ) / 3 ) * 6 , 1 ) ) ) select format_string_sudoku(create_sudoku_board(initial)) union all select format_string_sudoku(create_sudoku_board(s)) from x where ind = 0 $$; ------------------------------------------- --------------------------------- create or replace function solve_a_sudoku_with_brutforce(initial text) returns setof text -- our problem to solve language sql as $$ with recursive -- handle several string presentations of the sudoku board problem to solve cleaned_starter(starter) as (select regexp_replace(regexp_replace(initial,'\n|\=|\-|\|', '' ,'g' ),'\.',' ','g' )), neighb(ind_n, lp20) as (select lp, neighbors(lp) from generate_series(1, 81) lp ), x( s, ind ) as ( select starter, position( ' ' in starter ) from cleaned_starter union all select substr( s, 1, ind - 1 ) || z || substr( s, ind + 1 ) , position(' ' in repeat('x',ind) || substr( s, ind + 1 ) ) from (select gs::text as z from generate_series(1,9) gs)z , (select s , ind from x where ind > 0 ) tt where z not in ( select substr( s, lp20 , 1 ) from neighb where ind_n=ind ) ) select format_string_sudoku(create_sudoku_board(initial)) union all select format_string_sudoku(create_sudoku_board(s)) from x where ind = 0 $$; create or replace function solve_a_sudoku_with_norvig(initial text) returns setof text -- our problem to solve language sql as $$ with recursive q (s, next_move , ind) as ( select tt , next_move(tt) as posit ,0 from (select create_sudoku_board(initial) as tt ) sds union select tt , next_move(tt) as posit , ind+1 from (select play_move(s, next_move, unnest(string_to_array(s[next_move] ,Null)))as tt , ind from q where next_move>0) as ertrt ) select format_string_sudoku(create_sudoku_board(initial)) union all select format_string_sudoku(s) from q where next_move = 0 $$; ------------- create or replace function solve_a_sudoku(initial text, algorithm text) returns setof text language sql as $$ select (case when lower(algorithm)='norvig' then solve_a_sudoku_with_norvig(initial) else solve_a_sudoku_with_brutforce(initial) end) $$; ------------- create or replace function sudoku_problem(initial integer) returns text -- all our problems language sql as $$ select portfolio[initial] from (select array[ ' 53..7.... 6..195... .98....6. 8...6...3 4..8.3..1 7...2...6 .6....28. ...419..5 ....8..79' --easy --"534678912672195348198342567859761423426853791713924856961537284287419635345286179" ,'1....7.9..3..2...8..96..5....53..9...1..8...26....4...3......1..4......7..7...3..' -- medium --"162857493534129678789643521475312986913586742628794135356478219241935867897261354" ,'8..........36......7..9.2...5...7.......457.....1...3...1....68..85...1..9....4..' --hardest (35s) --"812753649943682175675491283154237896369845721287169534521974368438526917796318452" ,'1.......2.9.4...5...6...7...5.9.3.......7.......85..4.7.....6...3...9.8...2.....1' --hard (28s) "easter monster" ] as portfolio) as our_portfolio $$; -- let's play sudoku -- problem ( 1 or 2 or 3) , algorithm choosen ('Norvig' or 'Brutforce') -- beware , response time of up to 5 minutes on problem 3 with 'Brutforce' algorithm --select lp, array(select neighbors(lp) from generate_series(1,1 ) --) from generate_series(1, 81) lp select solve_a_sudoku(sudoku_problem(3), 'Norvig') _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users