我在计划中写了一个数独求解器。我将板单元表示为3×3载体的3×3载体,其中每个单元中具有候选数字列表。因此,例如空白板并更新其中一个单元格
(define blank-board-cell (for/list ([x 9]) (add1 x)))
(define blank-row (make-vector 9 blank-board-cell))
(define blank-board (make-vector 9 blank-row))
(define (board-ref b row col)
(vector-ref (vector-ref b row) col))
(define (board-update b target-row target-col new-cell)
(for/vector ([row (vector-length b)])
(for/vector ([col (vector-length b)])
(cond [(and (= row target-row)
(= col target-col))
new-cell]
[else (board-ref b row col)]))))
我想实现裸单和隐藏的单一策略来解决这个问题。 裸单:找到空单元格,其值可以通过查看其行,列和3x3块的内容来推断。如果已经为这些相邻单元分配了8个数字,那么空单元必须包含最后剩余的数字,并且必须从同一行,列和3×3块中的单元中移除该数字。
例如,在Java /命令式样式中,这看起来像
boolean nakedSingles()
{
for (int row = 0; row < 9; row++)
{
for (int col = 0; col < 9; col++)
{
HashSet<Integer> cellCandidates = board[row][col].candidates;
if (cellCandidates.size()==1)
{
board[row][col].setValue(cellCandidates.iterator().next());
//remove candidate from neighboring cells
return true;
}
}
}
return false;
}
计划“伪代码”的“翻译”我将前往
(define (naked-single b)
(for*/vector ([row (in-range (vector-length b))]
[col (in-range (vector-length b))])
(if (= 1 (length (board-ref b row col)))
; set candidate and remove it from cells in row/col
; and return #t
#f)))
这看起来是否合理/正确?
隐藏单一:通过查看行,列和3x3块,很明显只有一个候选可能,尽管单元本身可能有几个候选。我们将该候选项分配给单元格,并将其从同一行,列和3x3块中的单元格中删除。
例如,在Java /命令式样式中,这看起来像
boolean hiddenSingles()
{
int [] unitCandidates = new int[10];
// For each row, column or boxID
for (int unitSelect = 0; unitSelect < 3; unitSelect++)
{
for (int i = 0; i < 9; i++)
{
if (unitSelect == 0)
{
unit = getRow(i);
}
else if (unitSelect == 1)
{
unit = getCol(i);
}
else if (unitSelect == 2)
{
unit = getBox(i + 1);
}
for (int n = 1; n <= 9; n++)
{
unitCandidates[n] = 0;
}
for (Integer[] elem:unit)
{
int row = elem[0];
int col = elem[1];
if (board[row][col].getValue() == 0)
{
for (int cand:board[row][col].candidates)
{
unitCandidates[cand] += 1;
}
}
}
int foundDigit = 0;
for (int n = 1; n <= 9; n++)
{
// Check for hidden single
if (unitCandidates[n] == 1)
{
// Found hidden single
foundDigit = n;
break;
}
}
// If a hidden single was found, check what cell
// contained that hidden single and set cellvalue
if (foundDigit != 0)
{
for (Integer[] elem:unit)
{
int row = elem[0];
int col = elem[1];
if (board[row][col].getValue() == 0)
{
if (board[row]col].candidates.contains((Object)
foundDigit))
{
board[row][col].setValue(foundDigit);
removeDigitfrom(row,col);
return true;
}
}
}
}
}
}
return false;
}
这个转化为方案稍微复杂一点,不确定更优雅的方式是什么? (我可以用嵌套for循环来强制它。)
答案 0 :(得分:2)
您可以通过一点点冗余来简化和加快您的方法。
电路板单元应仅包含两种类型的值 - 数字或特殊值,表示仍需要确定该值。通过这种方式,您可以快速找到尚未确定的所有单元格。
同时,保留一组可能的值
在创建空白板时,所有可能的值(1到9)都已初始化。
创建一个设置单元格值的过程(在最初从外部格式读取电路板时,或者在找到要设置的值时使用,并确保
迭代棋盘(我称之为#34;传递1&#34;),对于尚未确定的每个单元格,计算行,列和单元格的集合交集。如果只剩下一个值,那么请使用之前描述的过程。如果没有剩余价值,董事会无法解决。 &#34;裸单&#34;之间没有区别。和&#34;隐藏单身&#34;。
迭代直到你通过并没有找到任何东西。
保持要确定的单元格数,并在将单元格设置为某个值时递减。通过这种方式,您将了解电路板何时解决。
许多数独谜题都可以通过这种方式解决,但对于某些人来说,你需要一个&#34;传递2&#34;你递归地尝试一个单元格的所有值,看看是否有助于你找到其他单元格。每当你&#34;尝试&#34;一个值,回到传递1,这将更快。请注意,您需要在第2阶段复制您的电路板,其中复制件与原件没有任何结构。