more information on the same topic
I used very soon an integrated version derived of the zhouyundong_2012 brute force.
Recently, working on the generation of puzzles having a double symmetry (double diagonal, pi_stick, Rotational 90), I faced unexpected problems showing some weaknesses in that code.
From some private exchanges, I think that this is something mladen Dobrichev already noticed.
The main weakness came when a puzzle showing a contradiction with the basic rules was submitted to the brute force. This is something that we have no chance to see in benchmarks based on valid puzzles.
The second weakness I faced is a kind of "worst case" for that algorithm heavily row oriented.
this is a puzzle not valid.
...12........35......96....3.74.2.9....7.3....1.8.63.7....41......57........89...
... 12. ...
... .35 ...
... 96. ...
3.7 4.2 .9.
... 7.3 ...
.1. 8.6 3.7
... .41 ...
... 57. ...
... .89 ...
One can see immediately that no digit can be assigned to r8c6.
It took 145 milliseconds to my code to tell that.
The main use of the brute force is to check the validity of a given puzzle.
Benchmarks have all been done on valid puzzles, a tiny corner of that problem.
I reworked on the zhouyundong_2012 process trying to find a way to fix the weaknesses with a minimum penalty.
I also revisited some special cases to try to define a kind of "best process" for that situation.
I'll comment on that in several posts.
The code I am using can be downloaded from the file
Zhou_usethe main change is in the InitSudoku() function.My assumption for the "basic" code is the following:
The empty cell (no given) can have any value except '1' to '9'. In the canonical form, the empty cell is '.'
The puzzle can be false against the basic rule (same digit twice in one unit)
the init function includes 3 steps
. create a canonical form of the puzzle
. check for no given against the basic rule
. solve single in cells at the start.
The third point has been added for 2 reasons
. it's nearly for free at that point
. this is something the brute force would not detect.
The second change I introduced is more for calls skipping that new InitSudoku().zhouyundong_2012 is in average so efficient that any change should be applied when the process appears to enter the "worst case".
Any other point in that code is the high efficiency of the Init() process.
The best trade off I found is valid in the main call looking for {is it a valid sudoku}?
The risk to enter the worst case can be seen in the "guess" function.
The first step, in the guess function is to use a digit bivalue.
If, after "n" guess (n=4 seems to be a good limit), you find no bi-value then, you are in risk to be in the worst case.
Then, the best option in that process seems to switch to the diagonal symmetry, restarting in the very efficient Init() function.
1) you are protected against the worst case,
2) the penalty in the standard process is close to nil.
The next post will summarize the results I got using that strategy.