方案中如何最好地实施裸单和隐藏单

Posted

技术标签:

【中文标题】方案中如何最好地实施裸单和隐藏单【英文标题】:How best to Implement naked single and hidden single in scheme 【发布时间】:2016-05-11 11:13:50 【问题描述】:

我正在写一个数独求解器。我将板单元表示为 3x3 向量的 3x3 向量,其中包含每个单元中的候选编号列表。因此,例如一个空白板并更新其中一个单元格是

    (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 个数字,则空单元格必须包含最后一个剩余数字,并且必须从同一行、列和 3x3 块中的单元格中删除该数字。

例如,在 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 循环强制它)。

【问题讨论】:

我喜欢这个问题作为一个谜题,但这里没有足够的工作能够以任何好的方式提供帮助。只需使用您的代码运行(blank-board),您就会发现您已经遇到了问题。 谢谢我修复了你提到的代码。我不想把所有的实用程序代码都放在我必须防止帖子混乱的板上。但我可以添加可能有用的内容。 【参考方案1】:

您可以通过一些冗余来简化和加快您的方法。


板单元应仅包含 2 种类型的值 - 一个数字或一个特殊值,表示该值仍需要确定。这样可以快速找到所有尚未确定的单元格。

同时保留一组可能的值

每行 每列 每个单元格

在创建空白板时使用所有可能的值(1 到 9)进行初始化。


创建一个设置单元格值的过程(在最初从外部格式读取板时使用,或者在您找到要设置的值时使用,并确保

您在板上设置单元格的值 从行的值集中删除该值 从列的值集中删除该值 清除可能值的单元格集(可选)

在棋盘上迭代(我称之为“pass 1”),对于尚未确定的每个单元格,计算行、列和单元格的集合交集。如果只剩下一个值,您就可以了,请使用前面描述的过程。如果没有留下任何值,则该板是无法解决的。 “裸单”和“暗单”没有区别。

迭代直到你通过并且没有找到任何东西。

保持要确定的单元格数量,并在将单元格设置为特定值时递减。这样你就会知道板子什么时候解决了。


许多数独谜题都可以通过这种方式解决,但对于一些数独谜题,您需要“通过 2”,递归地尝试一个单元格的所有值,看看这是否有助于您找到其他单元格。每当您“尝试”一个值时,回退到通过 1,那会更快。请注意,您需要在第 2 步中复制您的电路板,其中副本与原版没有任何结构。

【讨论】:

我可以添加一些代码,或者链接到我用 Racket 编写的数独求解器,它实现了我所描述的所有内容。但我觉得您可能更喜欢自己创建大部分代码。随意询问任何特定部分,甚至整个代码。我把它留给你。 “请注意,您需要在通道 2 中复制您的电路板,其中副本与原件没有结构。”我最近的尝试是通过一个树形结构来实现这个板子,它可以让你免费回溯。

以上是关于方案中如何最好地实施裸单和隐藏单的主要内容,如果未能解决你的问题,请参考以下文章

unity UI如何开启(显示)或者关闭(隐藏)Panel界面最好?

如何有条件地隐藏 ag 网格中的列

如何最好地为 WordPress 博客实施 Google CSE

如何隐藏安卓软键盘? [复制]

如何隐藏 Google Invisible reCAPTCHA 徽章

如何在 ChartJs 中隐藏 y 轴线?