我正在创建一个发明新数独谜题的程序。我最初计划这样做的方法是发明一个新的谜题,然后删除随机数。但是,我使用(见下文)创建新拼图的算法最多可能需要5分钟才能完成。有没有人有更快的解决方案?
for (int x = 0; x < boardWidth; x++) //boardWidth is the number of fillable squares wide and high the board is. (9 for a standard Sudoku board)
{
for (int y = 0; y < boardWidth; y++)
{
int errorCount = 0;
do
{
boardVals[y][x] = (byte)(rand.nextInt(boardWidth) + 1);
errorCount++;
if (errorCount > Math.pow(boardWidth, 4)) //If the square has been tried to be filled up to boardWidth^4 times (6,561 for a standard Sudoku board), it clears the board and starts again.
{
resetBoard();
x = 0; y = 0; break;
}
}while (!boardIsOK()); //boardIsOK() is a method that checks to see if the board is solvable, ignoring unfilled squares.
}
}
private boolean boardIsOK()
{
for (int i=0; i < boardWidth; i++)
{
if (!setIsOK(getRow(i)))
{
return false;
}
if (!setIsOK(getCol(i)))
{
return false;
}
}
for (int x=0; x < boardSegs; x++)
{
for (int y=0; y < boardSegs; y++)
{
if (!areaIsOK(getSquare(x,y)))
{
return false;
}
}
}
return true;
}
private byte[] getRow(int index)
{
return boardVals[index];
}
private byte[] getCol(int index)
{
byte[] b = new byte[boardWidth];
for (int i=0; i < boardWidth; i++)
b[i] = boardVals[i][index];
return b;
}
private byte[][] getSquare(int xIndex, int yIndex)
{
byte w = (byte)(boardWidth / boardSegs), b[][] = new byte[w][w];
for (int x=0; x < b.length; x++)
{
for (int y=0; y < b[x].length; y++)
{
b[y][x] = boardVals[y + (yIndex * w)][x + (xIndex * w)];
}
}
return b;
}
private boolean setIsOK(byte[] set)
{
for (int i=0; i < set.length - 1; i++)
{
for (int j=i + 1; j < set.length; j++)
{
if (set[i] == set[j] && set[i] != NULL_VAL && set[j] != NULL_VAL)
{
return false;
}
}
}
return true;
}
private boolean areaIsOK(byte[][] area)
{
int size = 0;
for (int i=0; i < area.length; i++)
{
size += area[i].length;
}
byte[] b = new byte[size];
for (int x=0, i=0; x < area.length; x++)
{
for (int y=0; y < area[x].length; y++, i++)
{
b[i] = area[x][y];
}
}
return setIsOK(b);
}
resetBoard()只需用NULL_VAL填充电路板。
答案 0 :(得分:5)
这里有几种可能的优化方法。首先,您应该为每个单元格添加一些簿记,为81个单元格中的每个单元格提供一组“仍然可能的数字”。当你填充下一个单元格时,不要采用任意随机数,而是从该集合中取一个随机数。
当你有6,561次尝试失败时不要停止。当81套中的一套变空时停止。在这种情况下,您不应该抛弃电路板并重新开始,而是向后退一步并尝试上一个电池的另一个值。尝试从中做出完整的backtracking算法。
答案 1 :(得分:2)
我建议看看D. Knuth的Dancing Links(gzipped postscript)论文。使用他的方法,你可以拥有一个非常快速的数独求解器,然后你可以解决你的板子来检查它是否正常。
作为一个想法,对于Project Euler problem 96,我的Java实现提供了解决方案(即解决了50个sudokus):
real 0m0.357s
user 0m0.350s
sys 0m0.010s
(Ubuntu Linux 2.6.32-26-server x86_64 GNU / Linux,在“Intel(R)Atom(TM)CPU 330 @ 1.60GHz”上运行)
答案 2 :(得分:0)
我认为删除随机数不是一个好主意。
在此处发布其他方法:boadIsOK()
和resetBoard()
并阅读一些文章如何创建拼图(1)。