为什么我的数独程序不返回输出?

Why doesn't my Sudoku program return an output?

本文关键字:返回 输出 程序 我的 为什么      更新时间:2023-10-16

因此,我试图通过回溯算法实现sudoku。我不明白为什么我的代码没有给出预期的输出。

我所做的是,我创建了一个循环,其中它在Sudoku中检查一个空单元(以0代表(。正如它发现的那样,它的坐标传递给了一个称为可能的entriesCheck((的函数。该函数将其写入一个称为可能的元素[9]的全球声明的数组,该数字最初可能会填充到该单元格中。

我从这些视频中学到了这种算法:https://www.youtube.com/watch?v=nuodn41ak3ghttps://www.youtube.com/watch?v=qi0diwmx3oy

预期的输出是已解决的Sudoku。它不会预期。相反,它冻结了。一点帮助将意味着很多。谢谢。

#include <stdio.h>
#include <stdlib.h>
int board[9][9] = {
                  {3, 0, 6, 5, 0, 8, 4, 0, 0},
                  {5, 2, 0, 0, 0, 0, 0, 0, 0},
                  {0, 8, 7, 0, 0, 0, 0, 3, 1},
                  {0, 0, 3, 0, 1, 0, 0, 8, 0},
                  {9, 0, 0, 8, 6, 3, 0, 0, 5},
                  {0, 5, 0, 0, 9, 0, 6, 0, 0},
                  {1, 3, 0, 0, 0, 0, 2, 5, 0},
                  {0, 0, 0, 0, 0, 0, 0, 7, 4},
                  {0, 0, 5, 2, 0, 6, 3, 0, 0},
                  };
int possibleEntries[9];
void possibleEntriescheck(int i, int j)
{
    int x,a=0,k,l,y;
    for(x=0;x<9;x++)
        possibleEntries[x]=0;
    for(x=0;x<9;x++)
    {
        if(board[i][x]!=0)
            possibleEntries[board[i][x]-1]=1;
    }
    for(x=0;x<9;x++)
    {
        if(board[x][j]!=0)
            possibleEntries[board[x][j]-1]=1;
    }
    if(i==0 || i==1 || i==2)
        k=0;
    else if(i==3 || i==4 || i==5)
        k=3;
    else
        k=6;
    if(j==0 || j==1 || j==2)
        l=0;
    else if(j==3 || j==4 || j==5)
        l=3;
    else
        l=6;
    for(x=k;x<k+3;x++)
    {
        for(y=l;y<l+3;y++)
            if(board[x][y]!=0)
                possibleEntries[board[x][y]-1]=1;
    }
    for(x=0;x<9;x++)
    {
        if(possibleEntries[x]==0)
            possibleEntries[x]=x+1;
        else
            possibleEntries[x]=0;
    }
}
int isFull()
{
    int i,j;
    for(i=0;i<9;i++)
    {
        for(j=0;j<9;j++)
        {
            if(board[i][j]==0)
                return 0;
        }
    }
    return 1;
}
void solveSudoku()
{
    int i,j,x,b=0,k;
    if(isFull())
    {
        printf("The sudoku board is:n");
        for(i=0;i<9;i++)
        {   
            for(j=0;j<9;j++)
                printf("t%d",board[i][j]);
            printf("n");
        }
    }
    else
    {
        for(i=0;i<9;i++)
        {
            for(j=0;j<9;j++)
            {
                if(board[i][j]==0)
                {
                    possibleEntriescheck(i,j);
                    for(x=0;x<9;x++)
                    {
                        if(possibleEntries[x]!=0)
                        {
                            board[i][j]=possibleEntries[x];
                            solveSudoku();
                            board[i][j]=0;
                        }
                    }   
                }
            }
        }
    }
    return;
}
int main()
{
    solveSudoku();
}

您实现了错误的回溯。正如视频中解释的那样,实际算法应该看起来像这样:

solve():
    if the sudoku is solved
        print field
        terminate
    x,y = the next vacant field
    for each possible value in that field
        assign value to x,y
        call solve() recursively to try with the assigned value
    clear vacant field

现在您的代码所做的是

solve():
    if the sudoku is solved
        print field
        return
    for each field in the sudoku
        if field is vacant
            for each possible value
                assign value
                solve recursively
                reset field to unassigned

现在,这实际上是解决了sudoku。但是这种方法有两个问题:
a :一旦解决了Sudoku,就不会终止。实际上,这个错误也在视频中列出的代码中。递归调用中的一个简单的return将终止当前呼叫上的方法,并继续进行递归"上方调用"。因此,基本上,算法在>每个中求解了sudoku(前提是有多个,否则,它只是尝试任何可能分配值的方法(。
b :这更严重。您的算法不仅生成所有可能的解决方案,而且还尝试分配可能找到的值的所有顺序。开销是巨大的,这是您的代码根本没有终止的原因。解决sudoku曾经已经花费了很长时间,但是您的代码确实很少。

如果您解决了这些问题,则应发现您的代码可行,只要其余即正确实施即可。我还建议您优化对空置字段的搜索和测试是否为空,因为这些字段可以相当简单,并且会提供一些加速。在开始时生成一个空置字段列表,在其上迭代(每个递归级别的一个字段(,并在处理整个列表后终止。例如:

solve(vacant, count):
    if count == 0
        print the field
        terminate
    x, y = vacant[count]
    count++
    for each possible value assignable to the field
        assign value to x, y
        call solve(vacant, count) recursively
    clear field

您将遇到的另一个问题,这将是相当丑陋的调试,这要归功于这一行:

int possibleEntries[9];

至少可以说,在递归中使用和覆盖的全局变量是一个坏主意。想象一下这样的程序的可能运行(Ident表示递归级别,没有身份意味着动作是全局的(:

solve
 |
 ---> board empty? Nope
      x,y <- next vacant field
possible values <- possible values for x, y
      field[x, y] <- first value from possible values
      solve
       |
       ---> board empty? Nope
            x, y <- next vacant field
possible values <- possible values for x, y (overwrites global variable!!!)
           field[x, y] <- first value from possible values
           solve
            |
            ---> ...
       <--- return
       field[x, y] <- second value from possible values (WRONG!!!)
       ... 

最后一个分配不会使用您当前正在处理的字段生成的可能值的列表,而是您在返回之前访问了递归中某个地方的另一个字段。您可以通过两种方式解决此问题:

  • 迭代从1到9,并分别检查每个数字是否可以分配给字段
  • 保留每个递归级别的单独列表