Algorithm 如何生成具有独特解决方案的数独板
如何生成具有独特解决方案的数独板?我的想法是初始化一个随机板,然后删除一些数字。但我的问题是如何保持解决方案的唯一性?要给出一个通用的解决方案并不容易。你需要知道一些事情来生成一个特定的数独游戏。。。例如,您不能构建包含九个以上空9号组(行、3x3块或列)的数独。单解数独中的最小给定数字(即“线索”)被认为是17,但如果我没有错的话,这个数独的数字位置是非常具体的。一个数独的平均线索数大约是26,我不确定,但是如果你放弃一个完整的网格,直到有26个,然后以对称的方式保留这些,你可能拥有一个有效的数独。Algorithm 如何生成具有独特解决方案的数独板,algorithm,puzzle,sudoku,Algorithm,Puzzle,Sudoku,如何生成具有独特解决方案的数独板?我的想法是初始化一个随机板,然后删除一些数字。但我的问题是如何保持解决方案的唯一性?要给出一个通用的解决方案并不容易。你需要知道一些事情来生成一个特定的数独游戏。。。例如,您不能构建包含九个以上空9号组(行、3x3块或列)的数独。单解数独中的最小给定数字(即“线索”)被认为是17,但如果我没有错的话,这个数独的数字位置是非常具体的。一个数独的平均线索数大约是26,我不确定,但是如果你放弃一个完整的网格,直到有26个,然后以对称的方式保留这些,你可能拥有一个有效的
另一方面,您可以从已完成的网格中随机退出数字,并使用检查器或其他工具进行测试,直到得到“确定”为止。您可以作弊。从一个可以解决的现有数独板开始,然后摆弄它 您可以将三个3x3块的任意行与任何其他行交换。可以将三个3x3块的任何列与另一列交换。在每个块行或块列中,可以交换单行和单列。最后,您可以排列数字,以便在填充的位置中有不同的数字,只要排列在整个板上是一致的 所有这些更改都不会使可解决的电路板无法解决。简单:
我怀疑你能找到比这快得多的解决方案。我自己的数独程序就是这样做的:
这不仅为您提供了唯一的电路板,而且还提供了在不破坏解决方案唯一性的情况下无法删除更多数字的电路板 当然,这只是算法的后半部分。前半部分是先找到一个完整的有效板(随机填充!),其工作原理非常相似,但“方向相反”:
除非p=NP,否则没有多项式时间算法来生成只有一个解的一般数独问题 在他的硕士论文中,Takayuki Yato定义了(ASP),其中的目标是,给定一个问题和一些解决方案,找到一个不同的解决方案来解决这个问题,或者证明不存在任何问题。Yato随后定义了ASP完备性,即很难找到其他解决方案的问题,并证明数独是ASP完备性的。因为他还证明了ASP完备性意味着NP难,这意味着如果你允许任意大小的数独板,就没有多项式时间算法来检查你生成的谜题是否有唯一的解(除非P=NP)
很抱歉破坏了您对快速算法的希望 我还认为您必须明确检查唯一性。如果你只有不到17个givens,那么一个独特的解决方案是不太可能的:还没有找到,尽管还不清楚它是否可能存在。)
但您也可以使用SAT解算器,而不是编写自己的回溯算法。这样,您可以在某种程度上调节找到解决方案的难度:如果您限制SAT解算器使用的推理规则,您可以检查您是否可以轻松解决难题。只需谷歌搜索“SAT解数独” 这里有一种制作经典数独游戏的方法(只有一种解决方案的数独游戏;预填充的方块围绕中心方块对称) 1) 从一个完整的网格开始(使用组填充加循环移位以轻松获得) 2) 如果可以使用剩余线索推断出清除的正方形,则从两个对称正方形中删除数字 3) 重复(2)直到所有数字都被选中
使用此方法,您可以创建一个非常简单的数独游戏,无论是否编程。你也可以用这个方法来制作更难的数独游戏。您可能希望在YouTube上搜索“创建经典数独”以获得一个分步示例。解决方案分为两部分:
A.生成数字模式6000亿 B.生成掩蔽图案~7e23组合 A)对于数字模式,最快的方法是生成具有NO
int[][] board = new int[][] {
{1,2,3, 4,5,6, 7,8,9},
{4,5,6, 7,8,9, 1,2,3},
{7,8,9, 1,2,3, 4,5,6},
{2,3,1, 5,6,4, 8,9,7},
{5,6,4, 8,9,7, 2,3,1},
{8,9,7, 2,3,1, 5,6,4},
{3,1,2, 6,4,5, 9,7,8},
{6,4,5, 9,7,8, 3,1,2},
{9,7,8, 3,1,2, 6,4,5}
};
void shuffleNumbers() {
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(9);
swapNumbers(i, ranNum);
}
}
private void swapNumbers(int n1, int n2) {
for (int y = 0; y<9; y++) {
for (int x = 0; x<9; x++) {
if (board[x][y] == n1)
board[x][y] = 0;
if (board[x][y] == n2)
board[x][y] = n1;
}
}
for (int y = 0; y<9; y++) {
for (int x = 0; x<9; x++) {
if (board[x][y] == 0)
board[x][y] = n2;
}
}
}
void shuffleRows() {
int blockNumber;
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(3);
blockNumber = i / 3;
swapRows(i, blockNumber * 3 + ranNum);
}
}
void swapRows(int r1, int r2) {
int[] row = board[r1];
board[r1] = board[r2];
board[r2] = row;
}
void shuffleCols() {
int blockNumber;
for (int i = 0; i < 9; i++) {
int ranNum = random.nextInt(3);
blockNumber = i / 3;
swapCols(i, blockNumber * 3 + ranNum);
}
}
void swapCols(int c1, int c2) {
int colVal;
for (int i = 0; i < 9; i++){
colVal = board[i][c1];
board[i][c1] = board[i][c2];
board[i][c2] = colVal;
}
}
void shuffle3X3Rows() {
for (int i = 0; i < 3; i++) {
int ranNum = random.nextInt(3);
swap3X3Rows(i, ranNum);
}
}
void swap3X3Rows(int r1, int r2) {
for (int i = 0; i < 3; i++) {
swapRows(r1 * 3 + i, r2 * 3 + i);
}
}
void shuffle3X3Cols() {
for (int i = 0; i < 3; i++) {
int ranNum = random.nextInt(3);
swap3X3Cols(i, ranNum);
}
}
private void swap3X3Cols(int c1, int c2) {
for (int i = 0; i < 3; i++) {
swapCols(c1 * 3 + i, c2 * 3 + i);
}
}
func getNumberSudoku() -> [[Int]] {
// Original number
let originalNum = [1,2,3,4,5,6,7,8,9]
// Create line 1 to 9 and shuffle from original
let line1 = originalNum.shuffled()
let line2 = line1.shift(withDistance: 3)
let line3 = line2.shift(withDistance: 3)
let line4 = line3.shift(withDistance: 1)
let line5 = line4.shift(withDistance: 3)
let line6 = line5.shift(withDistance: 3)
let line7 = line6.shift(withDistance: 1)
let line8 = line7.shift(withDistance: 3)
let line9 = line8.shift(withDistance: 3)
// Final array
let renewRow = [line1,line2,line3,line4,line5,line6,line7,line8,line9]
// Pre-shuffle for column
let colSh1 = [0,1,2].shuffled()
let colSh2 = [3,4,5].shuffled()
let colSh3 = [6,7,8].shuffled()
let rowSh1 = [0,1,2].shuffled()
let rowSh2 = [3,4,5].shuffled()
let rowSh3 = [6,7,8].shuffled()
// Create the let and var
let colResult = colSh1 + colSh2 + colSh3
let rowResult = rowSh1 + rowSh2 + rowSh3
var preCol: [Int] = []
var finalCol: [[Int]] = []
var prerow: [Int] = []
var finalRow: [[Int]] = []
// Shuffle the columns
for x in 0...8 {
preCol.removeAll()
for i in 0...8 {
preCol.append(renewRow[x][colResult[i]])
}
finalCol.append(preCol)
}
// Shuffle the rows
for x in 0...8 {
prerow.removeAll()
for i in 0...8 {
prerow.append(finalCol[x][rowResult[i]])
}
finalRow.append(prerow)
}
// Final, create the array into the [[Int]].
return finalRow
}
var finalArray = [[Int]]
finalArray = getNumberSudoku()
#pz is a 9x9 numpy array
def PossibleValueAtPosition(pz:[], row:int, col:int):
r=row//3*3
c=col//3*3
return {1,2,3,4,5,6,7,8,9}.difference(set(pz[r:r+3,c:c+3].flat)).difference(set(pz[row,:])).difference(set(pz[:,col]))
def SolvePuzzle(pz:[], n:int, Nof_solution:int):# init Nof_solution = 0
if Nof_solution>1:
return Nof_solution # no need to further check
if n>=81:
Nof_solution+=1
return Nof_solution
(row,col) = divmod(n,9)
if pz[row][col]>0: # location filled, try next location
Nof_solution = SolvePuzzle(pz, n+1, Nof_solution)
else:
l = PossibleValueAtPosition(pz, row,col)
for v in l: # if l = empty set, bypass all
pz[row][col] = v # try to fill a possible value v
Nof_solution = SolvePuzzle(pz, n+1, Nof_solution)
pz[row][col] = 0
return Nof_solution # resume the value, blacktrack