简体   繁体   English

如何在更短的时间内更高效地生成数独 Python

[英]How can I Generate Sudoku Python in less time and more efficiently

My goal here is to fill a grid of N^2*N^2 with positive integers in the range 1 to N^2, satisfying the following rules: Each integer in the range 1 to N^2 occurs only once in each column, row and section N * N.我的目标是用 1 到 N^2 范围内的正整数填充 N^2*N^2 的网格,满足以下规则:1 到 N^2 范围内的每个整数在每列中只出现一次,行和节 N * N。

In order to solve this, i try to make a 3x3 sudoku code that can generate a sudoku with the cells we want filled, however whenever I try to generate the sudoku with all the cells filled, my pc can't handle it, where can I improve this type of algorithm.为了解决这个问题,我尝试制作一个 3x3 数独代码,它可以生成一个包含我们想要填充的单元格的数独,但是每当我尝试生成填充所有单元格的数独时,我的电脑无法处理它,哪里可以我改进了这种算法。

import random
 
def MakeSudoku():
    Grid = [[0 for x in range(9)] for y in range(9)]
            
    for i in range(9):
        for j in range(9):
            Grid[i][j] = 0
            
    # The range here is the amount
    # of numbers in the grid
    for i in range(5):
        #choose random numbers
        row = random.randrange(9)
        col = random.randrange(9)
        num = random.randrange(1,10)
        while(not CheckValid(Grid,row,col,num) or Grid[row][col] != 0): #if taken or not valid reroll
            row = random.randrange(9)
            col = random.randrange(9)
            num = random.randrange(1,10)
        Grid[row][col]= num;
        
    Printgrid(Grid)
 
def Printgrid(Grid):
    TableTB = "|--------------------------------|"
    TableMD = "|----------+----------+----------|"
    print(TableTB)
    for x in range(9):
        for y in range(9):
            if ((x == 3 or x == 6) and y == 0):
                print(TableMD)
            if (y == 0 or y == 3 or y== 6):
                print("|", end=" ")
            print(" " + str(Grid[x][y]), end=" ")
            if (y == 8):
                print("|")
    print(TableTB)
#     |-----------------------------|
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     |---------+---------+---------|
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     |---------+---------+---------|
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     | 0  0  0 | 0  0  0 | 0  0  0 |
#     |-----------------------------|
    
def CheckValid(Grid,row,col,num):
    #check if in row
    valid = True
    #check row and collumn
    for x in range(9):
        if (Grid[x][col] == num):
            valid = False
    for y in range(9):
        if (Grid[row][y] == num):
            valid = False
    rowsection = row // 3
    colsection = col // 3
    for x in range(3):
        for y in range(3):
            #check if section is valid
            if(Grid[rowsection*3 + x][colsection*3 + y] == num):
                valid = False
    return valid
 
MakeSudoku()

Part of the problem is or Grid[row][col]!=0 creating an infinite loop when you have a valid grid.部分问题是or Grid[row][col]!=0在您拥有有效网格时创建无限循环。

In general though, you're searching way too big of a space.但总的来说,您搜索的空间太大了。 Every valid 9x9 Sudoku has the digits 1-9 once in each row, and for every grid you generate with that property you'll find 1.8e27 which don't.每个有效的 9x9 数独在每一行中都有一个数字 1-9,对于您使用该属性生成的每个网格,您会发现 1.8e27 没有。 That's just barely more than my potato of a computer can handle.这只是我的电脑土豆所能处理的。

Most strategies I'm aware of fall back to either a breadth or depth first search of some flavor (start with an empty/partial solution, add to it a bit, go back one or more steps if it leads to a broken grid).我所知道的大多数策略都会退回到某种风格的广度或深度优先搜索(从一个空的/部分解决方案开始,添加一点,如果它导致网格损坏,则返回一个或多个步骤)。 I'll do the same here.我会在这里做同样的事情。

Some important points to note include:需要注意的一些要点包括:

  • We place each digit in order (1s, then 2s, ...).我们按顺序放置每个数字(1s,然后 2s,...)。 Most ways of doing so end in valid Sudokus, whereas especially for larger sudokus building a square at a time usually ends in invalid Sudokus.这样做的大多数方法都以有效的数独结束,而特别是对于一次构建一个正方形的较大数独,通常以无效的数独结束。

  • For each digit we process the rows in order.对于每个数字,我们按顺序处理行。 This isn't mandatory per se, but for the hacky data structure (variable idx ) we set up to track Sudoku conflicts it allows us to completely avoid cleanup costs as we backtrack through the implicit graph of potential solutions.这本身不是强制性的,但对于我们设置来跟踪数独冲突的 hacky 数据结构(变量idx ),它允许我们在回溯潜在解决方案的隐式图时完全避免清理成本。

  • Initializing the grid with values of M+2 just cleaned up a tiny bit of logic.M+2值初始化网格只是清理了一点点逻辑。 I think None is more self-descriptive, but then rather than el>=x you need to actually handle the None case with something like (not el or el >= x) .我认为None更具自我描述性,但是您需要实际处理None情况而不是el>=x ,例如(not el or el >= x)

  • For anything bigger than around 36x36 you'll need a more advanced method of some flavor.对于大于 36x36 左右的任何内容,您将需要某种更高级的方法。 Up to 45x45 or so you could probably get away with just using cython or similar to get compiled speed, but beyond that you'll want a better algorithm.高达 45x45 左右,您可能只需使用 cython 或类似工具即可获得编译速度,但除此之外,您还需要更好的算法。 IIRC, some aspects of Sudoku are NP-hard; IIRC,数独的某些方面是 NP 难的; I'm not sure if that's just on the solving/uniqueness side of things or if generating complete boards is also provably difficult.我不确定这是否只是在事物的解决/独特性方面,或者生成完整的电路板是否也被证明是困难的。 I've heard rumors of people generating 100x100 boards easily though, so regardless I suspect there exist better solutions than what I've posted.我听说人们很容易生成 100x100 板的传言,所以不管我怀疑存在比我发布的更好的解决方案。

from random import sample, shuffle
from collections import deque

def rand_grid(w, h):
    M = w*h
    grid = [[M+2]*M for _ in range(M)]

    # A stack of the next cells we'll try to fill in.
    # This is sorted by the cell value, then by the row index.
    stack = deque((0, k, 1) for k in sample(range(M), M))

    # We want a cheap/easy way to check if we're about to violate a sudoku
    # rule for a given placement, and we need it to remain cheap upon
    # backtracking. For any cell-value x, idx[x][i]==j means that
    # grid[i][j]==x. Since our stack is processed in row-order we can
    # ignore the tail of any sub-list and never have to clean up after
    # ourselves
    idx = [[None]*M for _ in range(1+M)]
    while True:
        i, j, x = stack.pop()
        idx[x][i] = j

        # We've successfully filled the last row in the grid for a given
        # cell-value x. Clean up and either move to x+1 or return the
        # completely filled grid.
        if i==M-1:
            # If we go forward/backward past this cell-value x we might
            # fill the grid with extra xs. We could handle that by treating
            # idx as the source of truth till we construct the final grid,
            # but we don't backtrack across cell-value changes often and
            # do need to enumerate empty cells in a given row often, so it's
            # cheaper overall to clean up the grid just a little right now.
            # Better data structures probably exist.
            for a,row in enumerate(grid):
                for b,el in enumerate(row):
                    if el==x:
                        row[b] = M+2
            
            # Since we've finished placing cell-value x everywhere, update the
            # grid to reflect that fact
            for a,b in enumerate(idx[x]):
                grid[a][b] = x
            
            # The cell-value we just placed was the last one available. Hooray!
            if x==M:
                return grid
            
            # The next place we'll try to fill is row 0 with cell-value x+1
            else:
                i, x = 0, x+1

        # The next place we'll try to fill is row i+1 with cell-value x
        else:
            i += 1
        
        # At this point i and x represent the row/value we're about to attempt to
        # fill. We construct a set s of the columns which have been filled with x,
        # and a set t of tuples representing the upper-left corner of each box which
        # has been filled with x in order to cheaply check for column/box conflicts.
        s = set(idx[x][:i])
        t = {(w*(a//w), h*(b//h)) for a,b in enumerate(idx[x][:i])}

        # Valid candidates don't conflict with existing smaller digits or with the
        # same digit in any already placed column/box.
        candidates = [(i, k, x) for k,el in enumerate(grid[i])
          if el>=x and k not in s and (w*(i//w), h*(k//h)) not in t]
        shuffle(candidates)
        stack.extend(candidates)

# rand_grid(3, 3) creates a 9x9 Sudoku
# rand_grid(4, 5) creates a 20x20 Sudoku with 4-wide by 5-tall boxes
# rand_grid(n, n) is the MakeSudoku(n) function you asked for

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM