用类似Dijkstra算法的思想求解数独(Python)

Dijkstra算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,适用于求解有向图的最短路径。在每次扩展跟新时获得一个最短路径的点,然后下一次更新时以该点作为中介点更新与之相联的点,同时用一个表记录求得了解值的点,避免下一次循环时被重复处理。
下面,我将用Python举例运用这种思路求解数独。


问题描述:
数独由9×9个小方格组成,每三行三列相交为一宫,每格需填入1到9中的一个数字,其中,部分位置上的数已给出,要求在剩下的格子中填入数字,需满足每一行、每一列、每一宫中的数字均不重复。


解题思路:
先用列表记录所有点可能的解,已知点的解为所给值,未知点的可能解为1、2、3、4、5、6、7、8、9,每次循环用已求解的点来更新该点所在行、列、宫的所有其他待求点的可能解,即在其他点的可能解中去除该点的值。同时用一个表来记录所有点的状态,默认为True,当已求得解值并且更新了所有与该点关联的点的可能的解值后,状态值置为False,该点退出循环。


先建立数据结构,sukudo表用于记录每个点的可能的解的列表,processed表用于记录每个点的状态,标记每个点是否未解,Jiugongge字典帮助判断每个点更新了所在行和所在列以后后,其所在的九宫格内剩下的四个与之关联的点的坐标的偏移量。

sukudo=[[],[],[],[],[],[],[],[],[]] #记录每个点的可能的解
processed=[[],[],[],[],[],[],[],[],[]]  #用于标记每个点是否未解
Jiugongge={0:[1,2],1:[-1,1],2:[-2,-1]} #辅助求解与某点在同一个九宫格内的点

update函数用于更新有为一解的点所在的行、列、宫的所有关联点的解。

def update(m,n,num):
    for i in range(9):
        if num in sukudo[m][i] and n!=i: #更新坐标(m,n)点所在的行的点的解
            sukudo[m][i].remove(num)
        if num in sukudo[i][n] and m!=i:#更新坐标(m,n)点所在的列的点的解
            sukudo[i][n].remove(num)
    for i in Jiugongge[m%3]: #更新坐标(m,n)点所九宫格剩下四点的解
        for j in Jiugongge[n%3]:
            if num in sukudo[m+i][n+j]:
                sukudo[m+i][n+j].remove(num)

notEnd()函数通过遍历processed表中所有点的状态来判断是否存在点的唯一解未求得,返回True表示还有点未求得,程序继续,返回False则所有点求解完毕。

def notEnd():
    for i in range(9):
        if True in processed[i]:
            return True
        return False

主程序先通过用户输入获得待求解的数独,然后把所有点的初始可能解存放在sukudo表中,并初始化processed表所有值为True,然后循环更新,每当发现一个点的可能解只有一个并且没有用该点解更新过所在的行、列、宫时,更新,并将该点在processed表中的状态置为False,避免循环处理。

i=0
while i<9:
    print('请输入第'+str(i+1)+'行的数字,未知数字输入0')
    a=input()
    if a.isdecimal() and len(a)==9: #输入格式判断
        for j in range(9):
            processed[i].append(True) #初始化processed表所有值为True
            if a[j]=='0': #待求点
                sukudo[i].append([1,2,3,4,5,6,7,8,9])
            else:  #已知点
                sukudo[i].append([int(a[j])])
        i=i+1
    else:
        print('格式错误,请重新输入',end='')
while notEnd():
    for i in range(9):
        for j in range(9):
            #当sukudo表中点(i,j)仅有一可能解,且没有用该点解更新过所在的行、列、宫
            if len(sukudo[i][j])==1 and processed[i][j]:
                update(i,j,sukudo[i][j][0]) #用该点解更新过所在的行、列、宫
                processed[i][j]=False #将该点在processed表中的状态置为False
print('结果为:')
for i in sukudo:
    for j in i:
        print(str(j[0]),end='')
    print('\n')

测试结果如下:
输出结果
与笔算结果一致:

在这里插入图片描述

但是此算法只能求得只有一个解的情况,不能求解有多个解的数独。


参考资料:
Dijkstra算法实现最快路径

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Dijkstra 算法的基本思想,另外还有算法实现. 当然了,这个算法当路径点上万的时候效率上会降低。 我有另外的改进实现, 上万个点也是在200毫秒内完成。但是不知道怎么添加, 我只能在这里贴关键代码了 : static std::list<Node*> vecNodes; static std::list<Edge*> vecEdges; bool CDijkstras::DijkstrasFindPath(Node* psrcNode, Node* pdstNode, std::list<Node*>& vec, double& fromSrcDist) { if (psrcNode == 0 || pdstNode == 0) return false; if (psrcNode == pdstNode) { vec.push_back(pdstNode); return false; } std::list<Node*>::const_iterator it; for (it=vecNodes.begin(); it!=vecNodes.end(); it++) { (*it)->bAdded = false; (*it)->previous = 0; (*it)->distanceFromStart = MAXDOUBLE; (*it)->smallest = 0; } bool bFindDst = DijkstrasRouteInitialize(psrcNode, pdstNode); fromSrcDist = pdstNode->distanceFromStart; Node* previous = pdstNode; while (previous) { vec.push_back(previous); previous = previous->previous; } m_pDstNode = pdstNode; return bFindDst; } bool CDijkstras::DijkstrasRouteInitialize(Node* psrcNode, Node* pdstNode) { bool bFindDst = false; psrcNode->distanceFromStart = 0; Node* smallest = psrcNode; smallest->bAdded = true; std::list<Node*>::const_iterator it, ait; std::list<Node*> AdjAdjNodes ; for (it=psrcNode->connectNodes.begin(); it!=psrcNode->connectNodes.end(); it++) { if ((*it)->bAdded) continue; (*it)->smallest = psrcNode; (*it)->bAdded = true; AdjAdjNodes.push_back(*it); } while (1) { std::list<Node*> tempAdjAdjNodes; for (it=AdjAdjNodes.begin(); it!=AdjAdjNodes.end(); it++) { Node* curNode = *it; for (ait=curNode->connectNodes.begin(); ait!=curNode->connectNodes.end(); ait++) { Node* pns = *ait; double distance = Distance(pns, curNode) + pns->distanceFromStart; if (distance < curNode->distanceFromStart) { curNode->distanceFromStart = distance; curNode->previous = pns; } if (pns->bAdded == false) { tempAdjAdjNodes.push_back(pns); pns->bAdded = true; } } if (curNode == pdstNode) { bFindDst = true; } } if (bFindDst) break; if (tempAdjAdjNodes.size() == 0) break; AdjAdjNodes.clear(); AdjAdjNodes = tempAdjAdjNodes; } return bFindDst; } // Return distance between two connected nodes float CDijkstras::Distance(Node* node1, Node* node2) { std::list<Edge*>::const_iterator it; for (it=node1->connectEdges.begin(); it!=node1->connectEdges.end(); it++) { if ( (*it)->node1 == node2 || (*it)->node2 == node2 ) return (*it)->distance; } #ifdef _DEBUG __asm {int 3}; #endif return (float)ULONG_MAX; } /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ //得到区域的Key// __int64 CDijkstras::GetRegionKey( float x, float z ) { long xRegion = (long)(x / m_regionWidth); long zRegion = (long)(z / m_regionHeight); __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //得到区域的Key// __int64 CDijkstras::GetRegionKey( long tx, long tz ) { long xRegion = tx ; long zRegion = tz ; __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //取得一个区域内的所有的路径点, 返回添加的路径点的个数// unsigned long CDijkstras::GetRegionWaypoint (__int64 rkey, std::vector<Node*>& vec) { unsigned long i = 0; SAME_RANGE_NODE rangeNode = mmapWaypoint.equal_range(rkey); for (CRWPIT it=rangeNode.first; it!=rangeNode.second; it++) { i++; Node* pn = it->second; vec.push_back(pn); } return i; } inline bool cmdDistanceNode (Node* pNode1, Node* pNode2) { return pNode1->cmpFromStart < pNode2->cmpFromStart; }; //添加一个路径点// Node* CDijkstras::AddNode (unsigned long id, float x, float y, float z) { Node* pNode = new Node(id, x, y, z); __int64 rkey = GetRegionKey(x, z); mmapWaypoint.insert(make_pair(rkey, pNode)); mapID2Node[id] = pNode; return pNode; } //添加一条边// Edge* CDijkstras::AddEdge (Node* node1, Node* node2, float fCost) { Edge* e = new Edge (node1, node2, fCost); return e; } //通过路径点ID得到路径点的指针// Node* CDijkstras::GetNodeByID (unsigned long nid) { std::map<unsigned long, Node*>::const_iterator it; it = mapID2Node.find(nid); if (it!=mapID2Node.end()) return it->second; return NULL; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值