我正在开展一个我解决数独难题的项目N ^ 2 * N ^ 2 其中N小于等于20。
我已经编写了一个单线程数独求解器,它可以正常使用具有N值为5的谜题,但是如果我增加N的值,如N = 10或20,则代码变得无法响应。我也尝试使用线程池(java.concurrent)并分配N^2
个线程来执行并行。但它没有用,任何人都可以给我任何可以提高性能的解决方案。
这是我的单线程方法:
public class SUDOKU {
public static int[][] grid;
public boolean solveSUDOKU() {
int row;
int col;
int[] blankCell = findBlankLocation();
row = blankCell[0];
col = blankCell[1];
if (row == -1) {
// means will have filled the grid, return;
return true;
}
// we need to fill grid[row][col] cell
for (int i = 1; i <= grid.length; i++) {
// check if number i is safe for grid[row][col] cell
if (isSafe(row, col, i)) {
// means its safe to fill the number
grid[row][col] = i;
// fill the rest of the grid
if (solveSUDOKU()) {
return true;
}
// if we are here that means current selection of number didnt
// work, revert back the changes
grid[row][col] = 0;
}
}
return false; // This will cause the backtracking
}
public boolean isSafe(int row, int col, int n) {
// we need to check row contains number n OR
// Column contains number n OR
// Block in which cell appears contains number n
// If Any of the above statement is true, return false
if (!UsedInRow(row, n)
&& !UsedInColumn(col, n)
&& !UsedInBox((int) (row - row % Math.sqrt(grid.length)),
(int) (col - col % Math.sqrt(grid.length)), n)) {
return true;
}
return false;
}
// check if n not in particular row
public boolean UsedInRow(int row, int n) {
for (int i = 0; i < grid.length; i++) {
if (grid[row][i] == n) {
return true;
}
}
return false;
}
// check if n not in particular column
public boolean UsedInColumn(int col, int n) {
for (int i = 0; i < grid.length; i++) {
if (grid[i][col] == n) {
return true;
}
}
return false;
}
// check if n not in particular box
public boolean UsedInBox(int boxStartRow, int boxStartCol, int n) {
for (int i = 0; i < Math.sqrt(grid.length); i++) {
for (int j = 0; j < Math.sqrt(grid.length); j++) {
if (grid[i + boxStartRow][j + boxStartCol] == n) {
return true;
}
}
}
return false;
}
public int[] findBlankLocation() {
int[] cell = new int[2]; // cell[0]-row cell[1] -column
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid.length; j++) {
if (grid[i][j] == 0) {
cell[0] = i;
cell[1] = j;
return cell;
}
}
}
cell[0] = -1;
cell[1] = -1;
return cell; // means grid is full
}
public void print() {
for (int row = 0; row < grid.length; row++) {
if (row % Math.sqrt(grid.length) == 0) {
System.out.println(); // for more readability
}
for (int col = 0; col < grid.length; col++) {
if (col % Math.sqrt(grid.length) == 0) {
System.out.print(" "); // for more readability
}
System.out.print(grid[row][col] + " ");
}
System.out.println();
}
}
public static void main(String[] args) {
grid = new int[][]{
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
SUDOKU s = new SUDOKU();
if (s.solveSUDOKU()) {
s.print();
} else {
System.out.println("NO SOLUTION");
}
}
}
答案 0 :(得分:2)
事情并非那么简单。你不会在一个问题上抛出“平行”和“更多线程”而你会神奇地提高性能。
在您的情况下,您有一个巨大的搜索树来覆盖。含义:您的方法可能被称为数百万次次。
首先要做的事情是:了解您的代码正在做什么。
含义:你可以从简单的print语句开始;或者通过添加“调用计数器”。或者,如果需要了解现有的性能分析工具,可以帮助您了解程序花费大部分时间的哪些部分。然后你开始研究优化方法。
第一个明显的候选人:你正在计算
Math.sqrt(grid.length)
喜欢在3或5个不同的地方。这是一个相当昂贵的解决方案。 sqrt()可以轻松避免(通过计算一次值并将其放入所有其他代码将使用的常量)。它也可能值得研究模数计算(尽管可能更难摆脱)。
除此之外:为了并行化您的代码,您必须了解该代码如何处理其数据。你看,你不能只用n个线程来处理相同的单个数字数组。因为突然间你不得不担心一致性;你不能让一个线程覆盖内容,而另一个线程正在读取它。
所以,基本答案是:
然后;当你真正理解你的代码在做什么时;而你仍然不快乐;然后你可能会考虑在你的问题上投入“更多线程”。但即便如此:请记住线程不是免费的。在它们之间创建和切换存在巨大的开销。对于纯粹的CPU密集型操作而言,拥有更多线程将无济于事。
当您有大量IO操作(以及等待从IO读取数据的线程)时,线程有助于提高吞吐量。