用 Java 创建数独
我正在创建一个发明新数独谜题的程序。我最初计划这样做的方法是发明一个新的谜题,然后删除随机数。然而,我用来创建新谜题的算法(见下文)最多可能需要 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 填充棋盘。
I'm creating a program that invents a new Sudoku puzzle. The way I initially planned on doing this is by inventing a new puzzle and then removing random numbers. However, the algorithm I use (seen below) to create a new puzzle can take up to 5 minutes to make one. Does anyone have any faster solutions?
Creation Algorithm
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.
}
}
Methods:
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() simply fills the board with NULL_VAL.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(3)
我建议您看一下 D. Knuth 的 舞蹈链接 (压缩后记)论文。通过他的方法,您可以拥有一个非常快速的数独求解器,然后您可以求解您的棋盘以检查它是否正常。
作为一个想法,对于 Project Euler 问题 96,我的 Java 实现给出了解决方案(即解决 50 个数独):(
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”上运行)
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
这里有几种可能的优化方法。首先,您应该为每个单元格添加一些簿记,为 81 个单元格中的每个单元格提供一组“仍然可能的数字”。当您填充下一个单元格时,不要采用任意随机数,而是从该组中采用随机数。
当您尝试了 6,561 次失败时,请不要停止。当 81 组中的一组变空时停止。在这种情况下,您不应该扔掉电路板并重新开始,而应该向后退一步,为前一个单元格尝试另一个值。尝试从中创建一个完整的回溯算法。
There are several optimization approaches possible here. First, you should add some bookkeeping to each cell, having a set of the "still possible numbers" for each of the 81 cells. Instead of taking an arbitrary random number, take a random number from this set when you fill the next cell.
And don't stop when you had 6,561 unsuccessful tries. Stop when one of the 81 sets goes empty. When this is the case, you should not throw the board away and start over again, but go one step backwards and try another value for the previous cell. Try to make a complete backtracking algo from that.