81、数独求解(策略求解、整数规划建模求解)

81、数独求解(策略求解、整数规划建模求解),第1张

81、数独求解(策略求解、整数规划建模求解)
import re
from pyscipopt import Model, quicksum

#set存value的可能性,当value值确认后,set清空
class Cell:
    def __init__(self):
        self.value = "_"
        self.set = {1,2,3,4,5,6,7,8,9}

    def __repr__(self):
        return str(self.value)

#读数据
def dataRead():
    dataPath = r"/Users/zhangchaoyu/PycharmProjects/pythonProject/Game/data/sudoku1.csv"
    data = []
    f = open(dataPath, encoding='UTF-8-sig')
    while True:
        row = f.readline().strip('n')
        if row == ',n' or row == '':
            break
        row = re.split(',', row)
        data.append(row)
    return data

def case():
    data = [["", "", "", "", 2, "", "", 8, 5],
            [6, "", 2, "", 7, "", "", "", ""],
            [3, "", "", "", "", "", "", 1, ""],
            [7, "", "", 9, "", 4, "", "", ""],
            ["", 4, "", "", "", "", "", 3, ""],
            ["", "", 9, "", "", 5, "", 2, ""],
            ["", "", 5, "", "", "", 2, 6, ""],
            [2, "", "", "", 8, 6, 1, "", ""],
            ["", 3, "", "", "", 2, "", 7, ""]]
    return data

#把该行的num可能清理
def XRowRefresh(X, idx, num):
    for j in range(9):
        if num in X[idx][j].set:
            X[idx][j].set.remove(num)

#把该列的num可能清理
def XColRefresh(X, idx, num):
    for i in range(9):
        if num in X[i][idx].set:
            X[i][idx].set.remove(num)

#把该block的num可能清理
def XBlockRefresh(X, row, col, num):
    x, y = row//3, col//3
    for i in range(3*x, 3*(x+1)):
        for j in range(3*y, 3*(y+1)):
            if num in X[i][j].set:
                X[i][j].set.remove(num)

#X初始化
def XInit(data):
    X = [[Cell() for _ in range(9)] for _ in range(9)]
    for i in range(9):
        for j in range(9):
            if data[i][j] != "":
                num = int(data[i][j])
                X[i][j].value = num
                X[i][j].set = {}
                XRowRefresh(X, i, num)
                XColRefresh(X, j, num)
                XBlockRefresh(X, i, j, num)
    return X

#打印X
def XPrint(X):
    for i in range(9):
        print(X[i])
    print("")

#贪心策略,如果一个格子只有一种可能性,则直接使用
def Greedy(X):
    flag = False
    for i in range(9):
        for j in range(9):
            if len(X[i][j].set) == 1:
                flag = True
                num = X[i][j].set.pop()
                print("i:", i, ", j:", j, ", num:", num)
                X[i][j].value = num
                XRowRefresh(X, i, num)
                XColRefresh(X, j, num)
                XBlockRefresh(X, i, j, num)
    return flag

def mustExistRow(X, num):
    flag = False
    for i in range(9):
        Xi = [X[i][j].value for j in range(9)]
        #如果该行存在则过
        if num in Xi:
            continue
        #如果不存在,则看能放下该值的位置是否唯一,唯一则更新
        idxs = []
        for j in range(9):
            if num in X[i][j].set:
                idxs.append(j)
        if len(idxs) == 1:
            flag = True
            j = idxs[0]
            print("i:", i, ", j:", j, ", num:", num)
            X[i][j].value = num
            X[i][j].set = {}
            XRowRefresh(X, i, num)
            XColRefresh(X, j, num)
            XBlockRefresh(X, i, j, num)
    return flag

def mustExistRCol(X, num):
    flag = False
    for j in range(9):
        Xj = [X[i][j].value for i in range(9)]
        # 如果该列存在则过
        if num in Xj:
            continue
        # 如果不存在,则看能放下该值的位置是否唯一,唯一则更新
        idxs = []
        for i in range(9):
            if num in X[i][j].set:
                idxs.append(i)
        if len(idxs) == 1:
            flag = True
            i = idxs[0]
            print("i:", i, ", j:", j, ", num:", num)
            X[i][j].value = num
            X[i][j].set = {}
            XRowRefresh(X, i, num)
            XColRefresh(X, j, num)
            XBlockRefresh(X, i, j, num)
    return flag

def mustExistBlock(X, num):
    flag = False
    for x in range(3):
        for y in range(3):
            Xxy = [X[i][j].value for i in range(3*x, 3*(x+1)) for j in range(3*y, 3*(y+1))]
            # 如果该列存在则过
            if num in Xxy:
                continue
            # 如果不存在,则看能放下该值的位置是否唯一,唯一则更新
            idxs = []
            for i in range(3*x, 3*(x+1)):
                for j in range(3*y, 3*(y+1)):
                    if num in X[i][j].set:
                        idxs.append((i,j))
            if len(idxs) == 1:
                flag = True
                i,j = idxs[0]
                print("i:", i, ", j:", j, ", num:", num)
                X[i][j].value = num
                X[i][j].set = {}
                XRowRefresh(X, i, num)
                XColRefresh(X, j, num)
                XBlockRefresh(X, i, j, num)
    return flag

#反向找,一行,一列,一个九宫格内只有一个格能放下某个数,那么没错,就是它了
def mustExist(X):
    flag = False
    for num in range(1, 10):
        flagRow = mustExistRow(X, num)
        flagCol = mustExistRCol(X, num)
        flagBlock = mustExistBlock(X, num)
        flag = flag|flagRow|flagCol|flagBlock
    return flag

#各种搜索
def XRefresh(X):
    XPrint(X)
    while True:
        flagGreedy = Greedy(X)
        flagmustExist = mustExist(X)
        if flagGreedy|flagmustExist:
            XPrint(X)
        else:
            break

def subIP(X, timeLimit=100, isPrint=True):
    # 建模求解
    model = Model("IP")
    Y = {(i,j):[model.addVar(vtype="B", name="Y[%s, %s, %s]"%(i, j, k)) for k in range(9)] for i in range(9) for j in range(9)}

    #随便设置个目标
    model.setObjective(Y[0,0][0], "minimize")

    #每个数每行,每列只能出现一次
    for k in range(9):
        for i in range(9):
            model.addCons(quicksum(Y[i, j][k] for j in range(9)) == 1)
        for j in range(9):
            model.addCons(quicksum(Y[i, j][k] for i in range(9)) == 1)
        for x in range(3):
            for y in range(3):
                model.addCons(quicksum(Y[i, j][k] for i in range(3*x, 3*(x+1)) for j in range(3*y, 3*(y+1))) == 1)

    #每个地方只能出现一个数
    for i in range(9):
        for j in range(9):
            model.addCons(quicksum(Y[i, j][k] for k in range(9)) == 1)

    #现有的数要满足
    for i in range(9):
        for j in range(9):
            if X[i][j].value != "_":
                k = X[i][j].value-1
                model.addCons(Y[i, j][k] == 1)
                for l in range(9):
                    if l == k:
                        continue
                    model.addCons(Y[i, j][l] == 0)
            else:
                for k in range(9):
                    if k+1 not in X[i][j].set:
                        model.addCons(Y[i, j][k] == 0)

    # 设置求解时间
    model.setRealParam("limits/time", timeLimit)
    model.hideOutput()
    model.optimize()

    if isPrint:
        print("ngap:", model.getGap())

    Y1 = {(i,j):[round(model.getVal(Y[i,j][k])) for k in range(9)] for i in range(9) for j in range(9)}

    return Y1

def IP(X):
    #建模求解
    Y = subIP(X)
    #刷新X
    for i in range(9):
        for j in range(9):
            if X[i][j].value != "_":
                continue
            for k in range(9):
                if Y[i, j][k] == 1:
                    X[i][j].value = k+1
                    break
    XPrint(X)

if __name__ == '__main__':
    #数据录入
    # data = dataRead()
    data = case()
    #X初始化
    X = XInit(data)
    #X迭代
    XRefresh(X)
    #IP求解
    IP(X)

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5480850.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-12
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存