数独难题_如何玩和赢得数独-使用数学和机器学习解决每个数独难题

数独难题

Sudoku (and its predecessors) has been played for over a hundred years. When it first came out people had to actually solve the puzzles using only their minds. Now we have computers! (Ok, so most people still just use their minds...)

数独(及其前身)已经演奏了一百多年。 当它第一次出现时,人们实际上只能用他们的思想来解决难题。 现在我们有了电脑! (好吧,所以大多数人还是只用头脑...)

In this article, you will learn how to play and win Sudoku. But more importantly, you will learn how to use machine learning to easily solve every Sudoku puzzle. Who needs thinking when you can let the computer think for you. 😉

在本文中,您将学习如何玩并赢得数独。 但更重要的是,您将学习如何使用机器学习轻松解决每个数独难题。 当您可以让计算机为您思考时,谁需要思考。 😉

Peter Norvig developed an elegant program using Python to win sudoku using constraint propagation and search. Norvig's solution is considered a classic and is often referred to when people develop their own code to play Sudoku. After reviewing Sudoku and some strategies, I will break down Norvig's code step-by-step so you can understand how it works.

Peter Norvig使用Python开发了一个优雅的程序,通过约束传播和搜索来赢得数独。 Norvig的解决方案被认为是经典的解决方案,当人们开发自己的代码来玩Sudoku时经常被引用。 在回顾数独和一些策略之后,我将逐步分解Norvig的代码,以便您了解它的工作方式。

什么是数独? (What is Sudoku?)

Sudoku is a number-placement puzzle and there are a few different types. This article is about the most popular type.

数独是一个数字放置难题,有几种不同的类型。 本文是关于最受欢迎的类型。

The objective is to fill a 9x9 grid with digits (1-9) so that each column, row, and each of the nine 3x3 subgrids (also called boxes) all contain each of the digits from 1 to 9. The puzzles start with some numbers already on the grid and it's up to you to fill in the other numbers.

目的是用数字(1-9)填充9x9网格,以便每个列,行和9个3x3子网格(也称为框)中的每一个都包含从1到9的每个数字。已经存在于网格中的数字,您可以自行填写其他数字。

In the image below from a Sudoku game, the number that should go in the blue highlighted square cannot be in any of the yellow squares corresponding to the column, row, and 3x3 box.

在Sudoku游戏的下图中,应突出显示在蓝色突出显示的正方形中的数字不能在与列,行和3x3框相对应的任何黄色正方形中。

如何解决数独 (How to solve Sudoku)

When solving a Sudoku puzzle, you should be constantly doing two things. The first thing you should do is to eliminate numbers from rows, columns, and boxes (3x3 subgrids). The second thing you should do is to look for a single candidate.

解决数独难题时,您应该不断做两件事。 您应该做的第一件事是消除行,列和框(3x3子网格)中的数字。 您应该做的第二件事是寻找一个候选人。

In the example below, the possible numbers for each square are noted in a smaller font. The possible numbers were determined by eliminating all digits that occur in the same column, row, or box. Most people will determine the possible number for one box at a time, instead of for the full grid.

在下面的示例中,每个正方形的可能数字以较小的字体标注。 通过消除出现在同一列,行或框中的所有数字来确定可能的数字。 大多数人会一次确定一个框的可能数量,而不是整个网格的数量。

After you eliminate numbers, you can look for single candidates. That means to find a square that can only be one possible number. In the example below, the two yellow highlighted squares must contain 1 and 8 because all the other digits have been eliminated since they already appear in the column, row, or box of the square.

消除数字后,您可以寻找单个候选人。 这意味着找到一个只能是一个可能数字的正方形。 在下面的示例中,两个黄色突出显示的正方形必须包含18,因为所有其他数字已被删除,因为它们已经出现在正方形的列,行或框中。

Now that the two squares highlighted in yellow are known, that eliminates more possibilities from other squares. Now you know that the square highlighted in blue must be 7.

现在已知以黄色突出显示的两个正方形,这消除了其他正方形的更多可能性。 现在您知道以蓝色突出显示的正方形必须为7。

If you keep finding the single candidates and then eliminating options from other squares, you may eventually reach the point where there are no more single candidates.

如果您继续寻找单个候选者,然后从其他方格中消除选择,最终可能会达到没有单个候选者的地步。

At this point you can look for possible solutions to squares where the number is only in a single square in a box, row, or column. In the example below we can determine that the solution to the square highlighted in blue must be 6 since the number 6 does not occur in any other square in the yellow box.

在这一点上,您可以寻找方格的可能解决方案,在方格中,数字仅在框,行或列中的单个方格中。 在下面的示例中,我们可以确定蓝色突出显示的正方形的解必须为6,因为数字6不会出现在黄色框中的任何其他正方形中。

Sometimes the board will reach a state where it seems that every unsolved square could potentially be multiple values. That means there are multiple paths you could choose and it is not obvious which path will lead to solving the puzzle.

有时,董事会可能会达到一个状态,即每个未解决的平方可能都有多个值。 这意味着您可以选择多种路径,并且不清楚哪条路径可以解决难题。

At that point it is necessary to try each option. Choose one and keep solving until it becomes clear that the option you chose cannot be a solution. You will then have to backtrack and try a different option.

在这一点上,有必要尝试每个选项。 选择一个并继续求解,直到明确表明您选择的选项不能作为解决方案为止。 然后,您将不得不回溯并尝试其他选项。

This type of searching can be easily done with a computer using a binary search tree. When there is the option of two different numbers to solve a square, it is necessary to brach out into two different possibilities. A binary search tree will allow an algorithm to go down one branch of choices, and then try a different brach of choices.

可以使用二进制搜索树在计算机上轻松完成此类搜索。 当有两个不同的数字可以解决一个平方时,有必要引入两种不同的可能性。 二叉搜索树将允许算法沿选择的一个分支下降,然后尝试不同的选择。

Now we are going to see Python code that can solve Sudoku puzzles using a similar method to what was just described.

现在,我们将看到可以使用与上述方法类似的方法解决数独难题的Python代码。

彼得·诺维格(Peter Norvig)赢得数独计划 (Peter Norvig's program to win Sudoku)

Peter Norvig explained his approach to solving Sudoku and the code he used in his article Solving Every Sudoku Puzzle.

彼得·诺维格(Peter Norvig)在他的“ 解决每个数独难题”一文中解释了解决数独的方法和使用的代码。

Some may find his explanation a little hard to follow, especially beginners. I will break things down so it is easier to understand how Norvig's code works.

有些人可能会发现他的解释有些困难,尤其是初学者。 我将分解这些内容,以便更轻松地了解Norvig的代码如何工作。

In this article, Norvig's Python 2 code has been updated to Python 3. (Python 3 conversion by Naoki Shibuya.) I'll go through the code a few lines at a time but you can see the full code at the end of this article. For some people, it may be helpful to look over the full code before reading on.

在这篇文章中,弱势族群的的Python 2代码已经更新到Python 3(Python 3中的转换直树涩 。)我会去通过几行代码就在同一时间,但你可以在本文末尾看到完整的代码。 对于某些人来说,在继续阅读之前仔细阅读完整的代码可能会有所帮助。

First, we'll cover the basic setup and notation. Here's how Norvig describes the basic notation that he uses in his code:

首先,我们将介绍基本的设置和表示法。 以下是Norvig描述他在代码中使用的基本符号的方式:

A Sudoku puzzle is a grid of 81 squares; the majority of enthusiasts label the columns 1-9, the rows A-I, and call a collection of nine squares (column, row, or box) a unit and the squares that share a unit the peers.

数独拼图是由81个正方形组成的格; 大多数爱好者将第1至9列标记为AI,第AI列标记为一个单元 ,并称其为九个正方形(列,行或盒子)的集合, 一个单元共享一个正方形。

Here are the names of the squares:

这是正方形的名称:

A1 A2 A3| A4 A5 A6| A7 A8 A9
 B1 B2 B3| B4 B5 B6| B7 B8 B9
 C1 C2 C3| C4 C5 C6| C7 C8 C9
---------+---------+---------
 D1 D2 D3| D4 D5 D6| D7 D8 D9
 E1 E2 E3| E4 E5 E6| E7 E8 E9
 F1 F2 F3| F4 F5 F6| F7 F8 F9
---------+---------+---------
 G1 G2 G3| G4 G5 G6| G7 G8 G9
 H1 H2 H3| H4 H5 H6| H7 H8 H9
 I1 I2 I3| I4 I5 I6| I7 I8 I9

Norvig defines the digits, rows, and columns as strings:

Norvig将数字,行和列定义为字符串:

digits   = '123456789'
rows     = 'ABCDEFGHI'
cols     = digits

You will notice that cols is set to equal digits. While they are the same value, the represent different things. The digits variable represents the digits that go in a square to solve the puzzle. The cols variable represents the names of the columns of the grid.

您会注意到cols设置为相等的digits 。 尽管它们具有相同的值,但是它们表示不同的事物。 digits变量代表一个正方形以解决难题的数字。 cols变量表示网格列的名称。

The squares are also defined as strings but the strings are created with a function:

正方形也定义为字符串,但是这些字符串是通过以下函数创建的:

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

squares  = cross(rows, cols)

The return part of the cross function ( [a+b for a in A for b in B]) is just a fancy way of writing this code:

cross函数的返回部分( [a+b for a in A for b in B] )是编写此代码的一种好方法:

squares = []
for a in rows:
    for b in cols:
        squares.append(a+b)

The squares variable now equals a list of all the square names.

现在, squares变量等于所有正方形名称的列表。

['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9', 'C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9', 'D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7', 'D8', 'D9', 'E1', 'E2', 'E3', 'E4', 'E5', 'E6', 'E7', 'E8', 'E9', 'F1', 'F2', 'F3', 'F4', 'F5', 'F6', 'F7', 'F8', 'F9', 'G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8', 'G9', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'H7', 'H8', 'H9', 'I1', 'I2', 'I3', 'I4', 'I5', 'I6', 'I7', 'I8', 'I9']

Each square in the grid has 3 units and 20 peers. The units of a square are the row, column, and box that it appears in. The peers of a square are all the other squares in the units. For example, here are the units and peers for the square C2:

网格中的每个正方形都有3个单位和20个对等点。 正方形的单位是显示在其中的行,列和框。正方形的对等点是单位中的所有其他正方形。 例如,以下是平方C2的单位和对等点:

All the units for each square are created using the cross function with the following code:

使用带有以下代码的cross函数创建每个正方形的所有单位:

unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])

In Python a dictionary is a collection of key value pairs. The following lines of code creates dictionaries that use the square names as the keys and the three units or 20 peers as the values.

在Python中,字典是键值对的集合。 以下代码行创建字典,这些字典使用正方形名称作为键,并使用三个单位或20个对等体作为值。

units = dict((s, [u for u in unitlist if s in u]) 
             for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s]))
             for s in squares)

Now, the 3 units of ‘C2’ can be accessed with units['C2'] and will give the following result:

现在,可以使用units['C2']来访问'C2'的3个单位,并且将得到以下结果:

[['A2', 'B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'], ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9'], ['A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']]

Next we'll need two representations of the full Sudoku playing grid. A textual format named grid will be the initial state of the puzzle.

接下来,我们需要完整的数独游戏网格的两种表示形式。 名为grid的文本格式将是拼图的初始状态。

Another representation of the grid will also be needed to internally describe the current state of a puzzle. It will keep track of all remaining possible values for each square and be named values.

还需要网格的另一种表示形式来内部描述难题的当前状态。 它将跟踪每个正方形的所有剩余可能值,并将其命名为values

Similar to units and peers, values will be a dictionary with squares as keys. The value of each key will be a string of digits that are the possible digits for the square. If the digit was given in the puzzle or has been figured out, there will only be one digit in the key. For example, if there is a grid where A1 is 6 and A2 is empty, values would look like {'A1': '6', 'A2': '123456789', ...}.

类似于unitspeersvalues将是一个以正方形为键的字典。 每个键的值将是一串数字,该数字是该正方形的可能数字。 如果数字是在拼图中给出的或已经弄清楚了,则密钥中将只有一位数字。 例如,如果存在一个网格,其中A1为6而A2为空,则values看起来像{'A1': '6', 'A2': '123456789', ...}

解析网格和网格值函数 (Parse Grid and Grid Values Functions)

The parse_grid function (code below) converts the grid to a dictionary of possible values.  The grid is the given Sukou puzzle. The grid_values function extracts the important values which are digits, 0, and .. In the values dictionary, the squares are the keys and the given digits in the grid are the values.

parse_grid函数(下面的代码)将网格转换为可能值的字典。 grid是给定的Sukou难题。 grid_values函数提取数字0和的重要值. 。 在values字典中,正方形是键,网格中的给定数字是值。

For each square with a given value, the assign function is used to assign the value to the square and eliminate the value from the peers. The assign function is covered soon. If anything goes wrong, the function returns False.

对于具有给定值的每个平方,使用assign函数将值分配给该平方并从对等方消除该值。 assign功能即将涵盖。 如果发生任何错误,该函数将返回False。

Here is the code for the parse_grid and grid_values functions.

这是parse_gridgrid_values函数的代码。

def parse_grid(grid):
    """Convert grid to a dict of possible values, {square: digits}, or
    return False if a contradiction is detected."""
    ## To start, every square can be any digit; then assign values from the grid.
    values = dict((s, digits) for s in squares)
    for s,d in grid_values(grid).items():
        if d in digits and not assign(values, s, d):
            return False ## (Fail if we can't assign d to square s.)
    return values

def grid_values(grid):
    "Convert grid into a dict of {square: char} with '0' or '.' for empties."
    chars = [c for c in grid if c in digits or c in '0.']
    assert len(chars) == 81
    return dict(zip(squares, chars))

约束传播 (Constraint Propagation)

The initial values for the squares will be either specific digits (1-9) or an empty value. We can apply constraints to each square and eliminate values that are impossible.

平方的初始值将是特定数字(1-9)或空值。 我们可以将约束应用于每个平方并消除不可能的值。

Norvig uses two strategies to help determine the correct values for the squares (which correspond to the strategies above):

Norvig使用两种策略来帮助确定平方的正确值(与上面的策略相对应):

(1) If a square has only one possible value, then eliminate that value from the square's peers.

(1)如果一个正方形只有一个可能的值,则从该正方形的对等方消除该值。

(1) If a square has only one possible value, then eliminate that value from the square's peers.(2) If a unit has only one possible place for a value, then put the value there.

(1)如果一个正方形只有一个可能的值,则从该正方形的对等方中消除该值。 (2)如果一个单位在一个值中只有一个可能的位置,则将值放在该位置。

An example of the first strategy is that if we know that A1 has a value of 5, then 5 can be removed from all 20 of its peers.

第一种策略的一个示例是,如果我们知道A1的值为5,则可以从其所有20个对等方中删除5个。

Here is an example of the second strategy: if it can be determined that none of A1 through A8 contains 9 as a possible value, then we can be sure that A9 has a value of 9 since 9 must occur somewhere in the unit.

这是第二种策略的示例:如果可以确定A1到A8都不包含9作为可能的值,那么我们可以确定A9的值为9,因为9必须出现在单位中的某个位置。

Every time a square is updated, it will cause possible updates to all its peers. This process will keep continuing and it is called constraint propagation.

每次更新方块时,都会导致其所有对等方可能更新。 此过程将继续进行,这称为约束传播

分配功能 (Assign Function)

The assign(values, s, d) function is called inside the parse_grid function. It returns the updated values. It accepts three arguments: values, s, and d.

parse_grid函数内部调用了assign(values, s, d)函数。 它返回更新的值。 它接受三个参数: valuessd

Remember, values is a dictionary that associates each square to all possible digit values for that square. s is the square we are assigning a value to and d is the value that needs to be assigned to the square. At the start d comes from the given puzzle we are solving.

请记住, values是一个字典,将每个正方形与该正方形的所有可能的数字值相关联。 s是我们要为其分配值的平方, d是需要分配给该平方的值。 首先, d来自我们正在解决的给定难题。

It calls the function eliminate(values, s, d) to eliminate every value from s except d.

它调用函数eliminate(values, s, d)从s中除d之外的所有值。

If there is a contradiction, such as two squares being assigned the same number, the eliminate function will return False.

如果存在矛盾,例如两个正方形被分配了相同的数字,则消除函数将返回False。

def assign(values, s, d):
    """Eliminate all the other values (except d) from values[s] and propagate.
    Return values, except return False if a contradiction is detected."""
    other_values = values[s].replace(d, '')
    if all(eliminate(values, s, d2) for d2 in other_values):
        return values
    else:
        return False

消除功能 (Eliminate Function)

We saw that the assign function calls the eliminate function. The eliminate function is called like this: eliminate(values, s, d2) for d2 in other_values)

我们看到assign函数调用了eliminate函数。 消除函数的调用方式如下: eliminate(values, s, d2) for d2 in other_values)

The eliminate function will eliminate values that we know can't be a solution using the two strategies mentioned above. The first strategy is that when there is only one potential value for s, that value is removed from the peers of s. The second strategy is that when there is only one location that a value d can go, that value is removed from all the peers.

eliminate功能将消除使用上述两种策略无法解决的值。 第一种策略是,当s仅有一个潜在值时,该值将从s的同位体中删除。 第二种策略是,当值d只能存在一个位置时,该值将从所有对等方中删除。

Here is the full function:

这是全部功能:

def eliminate(values, s, d):
    """Eliminate d from values[s]; propagate when values or places <= 2.
    Return values, except return False if a contradiction is detected."""
    if d not in values[s]:
        return values ## Already eliminated
    values[s] = values[s].replace(d,'')
    ## (1) If a square s is reduced to one value d2, then eliminate d2 from the peers.
    if len(values[s]) == 0:
        return False ## Contradiction: removed last value
    elif len(values[s]) == 1:
        d2 = values[s]
        if not all(eliminate(values, s2, d2) for s2 in peers[s]):
            return False
    ## (2) If a unit u is reduced to only one place for a value d, then put it there.
    for u in units[s]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False ## Contradiction: no place for this value
        elif len(dplaces) == 1:
        # d can only be in one place in unit; assign it there
            if not assign(values, dplaces[0], d):
                return False
    return values

显示功能 (Display Function)

The display function will display the result after calling parse_grid.

display函数将在调用parse_grid之后显示结果。

def display(values):
    "Display these values as a 2-D grid."
    width = 1+max(len(values[s]) for s in squares)
    line = '+'.join(['-'*(width*3)]*3)
    for r in rows:
        print(''.join(values[r+c].center(width)+('|' if c in '36' else '') for c in cols))
        if r in 'CF': 
            print(line)
    print()

Here is an example of what the grid will look like after calling the display function after parsing a grid that is a hard puzzle.

这是一个示例,它说明在解析一个难题之后调用显示函数之后,栅格将是什么样子。

You will notice that many of the squares have multiple potential values, while some are completely solved. This grid above is the result of rote application of the two strategies from above. But as you can see, those strategies alone are not enough to completely solve the puzzle.

您会注意到,许多平方具有多个潜在值,而有些则完全解决了。 上面的网格是上面两种策略死记硬背应用的结果。 但是正如您所看到的,仅靠这些策略还不足以完全解决难题。

There are many ways to solve a Sukoku problem but some are much more efficient than others. Norvig suggests a specific type of search algorithm.

解决Sukoku问题的​​方法有很多,但有些方法效率更高。 Norvig建议使用一种特定类型的搜索算法。

There are a few things the search algorithm does. First, it makes sure that no solution or contrition have already been found. Then, it chooses an unfilled square and considers all values that are still possible. Finally, one at a time, it tries to assign the square each value, and searches from the resulting position.

搜索算法可以做一些事情。 首先,确保没有找到解决方案或贡献。 然后,它选择一个未填充的正方形并考虑所有可能的值。 最后,它一次一次尝试为平方分配每个值,并从结果位置进行搜索。

Variable ordering is used to choose which square to start exploring. Here is how Norvig describes it:

可变顺序用于选择要开始探索的正方形。 这是Norvig的描述方式:

we will use a common heuristic called minimum remaining values, which means that we choose the (or one of the) square with the minimum number of possible values. Why? Consider grid2 above. Suppose we chose B3 first. It has 7 possibilities (1256789), so we’d expect to guess wrong with probability 6/7. If instead we chose G2, which only has 2 possibilities (89), we’d expect to be wrong with probability only 1/2. Thus we choose the square with the fewest possibilities and the best chance of guessing right.
我们将使用一种称为最小剩余值的通用启发式方法,这意味着我们选择具有最小可能值数目的正方形(或其中之一)。 为什么? 考虑上面的grid2。 假设我们首先选择了B3。 它有7种可能性(1256789),因此我们希望以6/7的概率猜测错误。 相反,如果我们选择G2,它只有2种可能性(89),那么我们期望错的概率只有1/2。 因此,我们选择可能性最小,猜测正确的机会最大的正方形。

The digits are considered in numeric order.

这些数字按数字顺序考虑。

Here is the search function, along with the solve function that parses the initial grid and calls search.

下面是search功能,与一起solve函数解析初始网格和调用search

def solve(grid): return search(parse_grid(grid))

def search(values):
    "Using depth-first search and propagation, try all possible values."
    if values is False:
        return False ## Failed earlier
    if all(len(values[s]) == 1 for s in squares): 
        return values ## Solved!
    ## Chose the unfilled square s with the fewest possibilities
    n,s = min((len(values[s]), s) for s in squares if len(values[s]) > 1)
    return some(search(assign(values.copy(), s, d)) 
        for d in values[s])

Per the rules of Sudoku, the puzzle is solved when each square has only one value. The search function is called recursively until the puzzle is solved. values is copied to avoid complexity.

根据数独的规则,当每个正方形只有一个值时,难题就解决了。 递归调用search功能,直到解决难题为止。 values被复制以避免复杂性。

Here is the some function used to check if an attempt succeeds to solve the puzzle.

这是用于检查尝试是否成功解决难题的some功能。

def some(seq):
    "Return some element of seq that is true."
    for e in seq:
        if e: return e
    return False

This code will now solve every Sudoku puzzle. You can view the full code below.

该代码现在将解决每个数独难题。 您可以在下面查看完整的代码。

完整的数独求解器代码 (Full Sudoku solver code)

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

digits   = '123456789'
rows     = 'ABCDEFGHI'
cols     = digits
squares  = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])
units = dict((s, [u for u in unitlist if s in u]) 
             for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s]))
             for s in squares)

def parse_grid(grid):
    """Convert grid to a dict of possible values, {square: digits}, or
    return False if a contradiction is detected."""
    ## To start, every square can be any digit; then assign values from the grid.
    values = dict((s, digits) for s in squares)
    for s,d in grid_values(grid).items():
        if d in digits and not assign(values, s, d):
            return False ## (Fail if we can't assign d to square s.)
    return values

def grid_values(grid):
    "Convert grid into a dict of {square: char} with '0' or '.' for empties."
    chars = [c for c in grid if c in digits or c in '0.']
    assert len(chars) == 81
    return dict(zip(squares, chars))

def assign(values, s, d):
    """Eliminate all the other values (except d) from values[s] and propagate.
    Return values, except return False if a contradiction is detected."""
    other_values = values[s].replace(d, '')
    if all(eliminate(values, s, d2) for d2 in other_values):
        return values
    else:
        return False

def eliminate(values, s, d):
    """Eliminate d from values[s]; propagate when values or places <= 2.
    Return values, except return False if a contradiction is detected."""
    if d not in values[s]:
        return values ## Already eliminated
    values[s] = values[s].replace(d,'')
    ## (1) If a square s is reduced to one value d2, then eliminate d2 from the peers.
    if len(values[s]) == 0:
        return False ## Contradiction: removed last value
    elif len(values[s]) == 1:
        d2 = values[s]
        if not all(eliminate(values, s2, d2) for s2 in peers[s]):
            return False
    ## (2) If a unit u is reduced to only one place for a value d, then put it there.
    for u in units[s]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False ## Contradiction: no place for this value
        elif len(dplaces) == 1:
            # d can only be in one place in unit; assign it there
            if not assign(values, dplaces[0], d):
                return False
    return values

def solve(grid): return search(parse_grid(grid))

def search(values):
    "Using depth-first search and propagation, try all possible values."
    if values is False:
        return False ## Failed earlier
    if all(len(values[s]) == 1 for s in squares): 
        return values ## Solved!
    ## Chose the unfilled square s with the fewest possibilities
    n,s = min((len(values[s]), s) for s in squares if len(values[s]) > 1)
    return some(search(assign(values.copy(), s, d)) 
        for d in values[s])

def some(seq):
    "Return some element of seq that is true."
    for e in seq:
        if e: return e
    return False

翻译自: https://www.freecodecamp.org/news/how-to-play-and-win-sudoku-using-math-and-machine-learning-to-solve-every-sudoku-puzzle/

数独难题

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值