- 浏览: 171458 次
- 性别:
- 来自: 广州
-
博客专栏
-
-
TCP/IP详解卷一>阅读...
浏览量:12649
文章分类
最新评论
-
master11:
你好,博主看完你的解释,好厉害啊!!佩服。如果想运行看一下效果 ...
数独人工解法的一些技巧及其python实现 -
evasiu:
chenxun1012033254 写道lz这么辛苦我也是醉了 ...
数独人工解法的一些技巧及其python实现 -
chenxun1012033254:
lz这么辛苦我也是醉了作为一名oier我说有一个算法叫做dan ...
数独人工解法的一些技巧及其python实现 -
xuyfiei:
lz很厉害,现在该是毕业了吧
恨毕业--读研一年总结 -
nphoenix:
呵呵 肯踏實的學東西已經很不錯了。畢業了工作之後,你就會發現個 ...
恨毕业--读研一年总结
折腾了一个星期,发现自己的大脑真的是短路了,智力水平下降到历史最低点,竟然折腾了那么久才理解了dancing link。所幸经过几天的反思,终于列出了接下来应该做的几件事:
1. 产生数独题:
1.1 实现解数独的算法dlx
1.2 从数独终盘中随机选择一个cell,判断该cell是否可以挖掉而不会造成解不唯一
2. 用pygame实现基本界面
今天完成了1.1的编码,借此总结一下。
老实说,我也不明白为什么dancing link X会花了我那么多时间去理解,尤其是当我最后理解了它并用三种方法把它实现了之后。也许是Donald 26页的论文[1]把我吓到了,也许是我真的变笨了,或者老了。。
在那篇26页的论文里,其实Donald主要讲了两件事(我关心的两件事,后面的我也没有仔细看):
1. 解决exact-cover问题的算法,也即Dancing link X里的那个算法X,Donald说他暂时没有想到一个更好的名字,所以管它叫algorithm X
2. 算法X的实现上的一些技巧,也即著名的dancing link,也就是说,dancing link并不是一种算法,它只是一种技巧而已~
Exact-Cover问题:
问题描述:给定一个0/1矩阵(一般是稀疏矩阵),选出一些行,使得由这些行构成的新矩阵中每一列有且仅有一个1。换个说法,把列看成域(universe)中的元素,把行看成域里的一个子集,exact-cover问题就是找出一些不相交的子集,它们的并集覆盖整个域。
问题举例:
有如下0/1矩阵:
矩阵的行(1,4,5)构成的新矩阵中每一行每一列都有且仅有一个1.
解决方案:
我们知道,当我们选择某一行时,其他与该行在某列上同时有一个1的行都将不能再入选。也就是说,这些行在下一次遴选前都将被删除,而相应的列也可以被删除,因为这些列已经有了1了。也就是说,我们可以通过删除矩阵的行、列来缩小问题的规模,直到最终矩阵为空,说明我们已经找到了一个合法的解。假如矩阵出现没有1的列,说明这个问题无解,因为它说明这个列没有办法得到一个1.下面是Donald总结的算法流程:
上面的描述便是前面提到的algorithm X。它其实是一个深搜问题。在行4,算法选择一个非确定性的(nondeterministic)行r,这将形成搜索树上的不同分枝,例如上面的例子我们确定性地选择列1做为我们的初始入口,选择行2或选择行4将形成搜索树的两个分支,我们首先沿着行2这个分枝搜索下去,如果找不到解,将一步一步回溯,最后返回root,然后再从行4这个分枝搜索。在这个过程中,算法将花费很多时间在矩阵的操作(删除行/列)上。
Donald在文章的开篇就提出了一件很是让人感到aha~!的事情:
假设x指向双向链表的一个元素,x.left指向x的左元素,x.right指向x的右元素,当我们决定删除x时,可以做如下操作:
而下面这两句精练的对称的语句将重新将x放回链表上:
是不是很美妙呢?而这,便是dancing link的精华了。
让我们换个数据结构来表现一个稀疏的0/1矩阵,形象一点,就是下图这样的表示:
每个矩阵元素,我们称之为data object,有四个指针,分别指向其上、下、左、右元素,矩阵的每一行、每一列都是一个双向循环链表,每一列还连带一个list header,我们称之为column object,以方便我们进行列的删除操作。column object同样也是双向链表中的一部分。每个data object除了那四个指针外,还带有一个column object的引用,以方便对该列进行删除。而column object则还另外包括一个size元素,说明该列总共有多少个元素。还记得我们前面提到的算法不?我们每次选一个列c,该c拥有的每一个行都将成为一个分支。实际上我们做的事情是在众多未知数中先假设某个未知数,从而缩小搜索的空间,再在未知数中做新的假设,,直到最后所有假设都符合题目限制于是成功获得结果或者某个限制被打破从而搜索失败必须回溯。该列有多少个元素将决定了搜索树在该子根下将有多少个分支,那我们当然希望越在上层的分枝越少就越好,(因为随着树的深入,可能很多未知量都变成已知的),因此就有了这个size,以方便我们进行列的选择。由column object组成的双向横向链表还包含一个root,用来标志矩阵是否为空,当root.right==root时。上面的算法通过改用这个数据结构,可以重新描述如下:
cover跟uncover的操作如下:
是不是相当简洁呢?对于上面的那个例子,下图是cover了column A后的矩阵:
当我们选择了行2后,将cover column D跟G,下面是cover了D跟G后的矩阵:
Dancing link使矩阵的行/列删除变得相当快捷,有如弹弹手指般容易。实现起来也相当地容易。不过由于我对python还是很不熟,而当时又感觉自己没有弄懂dancing link的样子,于是我只好从自己比较熟悉的c++开始编码了,于是就有了下面两个版本的dlx:
C++封装做得不是很好,呵呵。
下面是python的实现版本,显然比c++要简洁好多:
从数独到Exact-Cover问题:
那么,数独跟Exact-Cover有什么关系呢?我们来看看数独的限制:
1. 每个格里只能填一个数(1-9),有且只有一个
2. 每个数(1-9)在每一行里只能出现一次,有且只有一次
3. 每个数在每一列里只能出现一次,有且只有一次
4. 每个数在每一个块(block)只能出现一次,有且只有一次
这么多个“有且只有一次”显然在向我们发出强烈的信号:这是一个exact-cover问题!于是便有了下面的转换:
我们知道,在没有任何限制下,数独的第i行,第j列可以填入数字k,其中0<i,j,k<=9,因此我们实际上是从9^3个(i,j,k)中选择81个,同时使其满足以上4个限制。对于每一个(i,j,k),格(i,j)只能填一个数字的限制将表现为列(ci,cj)从k=[1,9]为1,所以我们只能选其中一行,也就是,在k=[1,9]中,只有一个(i,j,k0)被选中。对于每一个(i,j,k),行i只能出现一次k的限制将表现为列(ci, ck)从j=[1,9]为1,我们只能选择其中一行。对于限制3,4也是同样的道理。总共将需要81*4列,9^3行。这里[2]把这个问题说得很清楚。
下面分别是C++和python的数独求解:
最后最后,这里[3]提供了一个30行的dlx的python代码,非常的精致,建议大家都去看一看~!
__________________________________________
[1]Dancing Links, Donald E. Knuth(附件下载)
[2]http://cgi.cse.unsw.edu.au/~xche635/dlx_sodoku/
[3]http://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
1. 产生数独题:
1.1 实现解数独的算法dlx
1.2 从数独终盘中随机选择一个cell,判断该cell是否可以挖掉而不会造成解不唯一
2. 用pygame实现基本界面
今天完成了1.1的编码,借此总结一下。
老实说,我也不明白为什么dancing link X会花了我那么多时间去理解,尤其是当我最后理解了它并用三种方法把它实现了之后。也许是Donald 26页的论文[1]把我吓到了,也许是我真的变笨了,或者老了。。
在那篇26页的论文里,其实Donald主要讲了两件事(我关心的两件事,后面的我也没有仔细看):
1. 解决exact-cover问题的算法,也即Dancing link X里的那个算法X,Donald说他暂时没有想到一个更好的名字,所以管它叫algorithm X
2. 算法X的实现上的一些技巧,也即著名的dancing link,也就是说,dancing link并不是一种算法,它只是一种技巧而已~
Exact-Cover问题:
问题描述:给定一个0/1矩阵(一般是稀疏矩阵),选出一些行,使得由这些行构成的新矩阵中每一列有且仅有一个1。换个说法,把列看成域(universe)中的元素,把行看成域里的一个子集,exact-cover问题就是找出一些不相交的子集,它们的并集覆盖整个域。
问题举例:
有如下0/1矩阵:

矩阵的行(1,4,5)构成的新矩阵中每一行每一列都有且仅有一个1.
解决方案:
我们知道,当我们选择某一行时,其他与该行在某列上同时有一个1的行都将不能再入选。也就是说,这些行在下一次遴选前都将被删除,而相应的列也可以被删除,因为这些列已经有了1了。也就是说,我们可以通过删除矩阵的行、列来缩小问题的规模,直到最终矩阵为空,说明我们已经找到了一个合法的解。假如矩阵出现没有1的列,说明这个问题无解,因为它说明这个列没有办法得到一个1.下面是Donald总结的算法流程:
Exact_Cover(A): if A is empty: problem solve, return else pick a column c: choose a row, r, such that A[r,c]=1 include r in the partial solution for each j such that A[r,j]=1: delete column j from matrix A for each i such that A[i,j]=1: delete row i from matrix A repeat this algorithm recursively on the reduced matrix A
上面的描述便是前面提到的algorithm X。它其实是一个深搜问题。在行4,算法选择一个非确定性的(nondeterministic)行r,这将形成搜索树上的不同分枝,例如上面的例子我们确定性地选择列1做为我们的初始入口,选择行2或选择行4将形成搜索树的两个分支,我们首先沿着行2这个分枝搜索下去,如果找不到解,将一步一步回溯,最后返回root,然后再从行4这个分枝搜索。在这个过程中,算法将花费很多时间在矩阵的操作(删除行/列)上。
Donald在文章的开篇就提出了一件很是让人感到aha~!的事情:
假设x指向双向链表的一个元素,x.left指向x的左元素,x.right指向x的右元素,当我们决定删除x时,可以做如下操作:
x.left.right = x.right x.right.left = x.left
而下面这两句精练的对称的语句将重新将x放回链表上:
x.left.right = x x.right.left = x
是不是很美妙呢?而这,便是dancing link的精华了。
让我们换个数据结构来表现一个稀疏的0/1矩阵,形象一点,就是下图这样的表示:

每个矩阵元素,我们称之为data object,有四个指针,分别指向其上、下、左、右元素,矩阵的每一行、每一列都是一个双向循环链表,每一列还连带一个list header,我们称之为column object,以方便我们进行列的删除操作。column object同样也是双向链表中的一部分。每个data object除了那四个指针外,还带有一个column object的引用,以方便对该列进行删除。而column object则还另外包括一个size元素,说明该列总共有多少个元素。还记得我们前面提到的算法不?我们每次选一个列c,该c拥有的每一个行都将成为一个分支。实际上我们做的事情是在众多未知数中先假设某个未知数,从而缩小搜索的空间,再在未知数中做新的假设,,直到最后所有假设都符合题目限制于是成功获得结果或者某个限制被打破从而搜索失败必须回溯。该列有多少个元素将决定了搜索树在该子根下将有多少个分支,那我们当然希望越在上层的分枝越少就越好,(因为随着树的深入,可能很多未知量都变成已知的),因此就有了这个size,以方便我们进行列的选择。由column object组成的双向横向链表还包含一个root,用来标志矩阵是否为空,当root.right==root时。上面的算法通过改用这个数据结构,可以重新描述如下:
search(k): if root.right == root: solution found, return true or print it~ { as we've mentioned before, choose one column that has least elements} choose a column c cover column c {remember c is a column object} for each r <- c.down, c.down.down, ..., while r!=c solution[k]=r for each j <- r.right, r.right.right, ..., while j!=r cover column j if search(k+1): return true {doing clean up jobs} for each j <- r.left, r.left.left, ..., while j!=r uncover column j {if we get here, no solution is found} uncover column c return false
cover跟uncover的操作如下:
cover( column object c ): c.left.right = c.right c.right.left = c.left for each i <- c.down, c.down.down, ..., while i!=c: for each j <- i.right, i.right.right, ..., while j!=i: j.down.up = j.up j.up.down = j.down j.column.size-- uncover( column object c ): for each i <- c.up, c.up.up, ..., while i!=c: for each j <- i.left, i.left.left, ..., while j!=i: j.down.up = j j.up.down = j j.column.size++ c.left.right = c c.right.left = c
是不是相当简洁呢?对于上面的那个例子,下图是cover了column A后的矩阵:

当我们选择了行2后,将cover column D跟G,下面是cover了D跟G后的矩阵:

Dancing link使矩阵的行/列删除变得相当快捷,有如弹弹手指般容易。实现起来也相当地容易。不过由于我对python还是很不熟,而当时又感觉自己没有弄懂dancing link的样子,于是我只好从自己比较熟悉的c++开始编码了,于是就有了下面两个版本的dlx:
C++封装做得不是很好,呵呵。
//ExtractCoverMatrix.h #include<iostream> using namespace std; struct Node { Node* left, *right, *up, *down; int col, row; Node(){ left = NULL; right = NULL; up = NULL; down = NULL; col = 0; row = 0; } Node( int r, int c ) { left = NULL; right = NULL; up = NULL; down = NULL; col = c; row = r; } }; struct ColunmHeader : public Node { int size; ColunmHeader(){ size = 0; } }; class ExactCoverMatrix { public: int ROWS, COLS; //这是存储结果的数组 int* disjointSubet; //接收矩阵其及维度 ExactCoverMatrix( int rows, int cols, int** matrix ); //释放内存 ~ExactCoverMatrix(); //在行r列c的位置上插入一个元素 void insert( int r, int c ); //即我们的cover和uncover操作了 void cover( int c ); void uncover( int c ); //即我们的algorithm X的实现了 int search( int k=0 ); private: ColunmHeader* root; ColunmHeader* ColIndex; Node* RowIndex; }; //ExactCoverMatrix.cpp #include "ExtracCoverMatrix.h" ExtracCoverMatrix::ExtracCoverMatrix( int rows, int cols, int** matrix ) { ROWS = rows; COLS = cols; disjointSubet = new int[rows+1]; ColIndex = new ColunmHeader[cols+1]; RowIndex = new Node[rows]; root = &ColIndex[0]; ColIndex[0].left = &ColIndex[COLS]; ColIndex[0].right = &ColIndex[1]; ColIndex[COLS].right = &ColIndex[0]; ColIndex[COLS].left = &ColIndex[COLS-1]; for( int i=1; i<cols; i++ ) { ColIndex[i].left = &ColIndex[i-1]; ColIndex[i].right = &ColIndex[i+1]; } for ( int i=0; i<=cols; i++ ) { ColIndex[i].up = &ColIndex[i]; ColIndex[i].down = &ColIndex[i]; ColIndex[i].col = i; } ColIndex[0].down = &RowIndex[0]; for( int i=0; i<rows; i++ ) for( int j=0; j<cols&&matrix[i][j]>0; j++ ) { insert( i, matrix[i][j] ); } } ExtracCoverMatrix::~ExtracCoverMatrix() { delete[] disjointSubet; for( int i=1; i<=COLS; i++ ) { Node* cur = ColIndex[i].down; Node* del = cur->down; while( cur != &ColIndex[i] ) { delete cur; cur = del; del = cur->down; } } delete[] RowIndex; delete[] ColIndex; } void ExtracCoverMatrix::insert( int r, int c ) { Node* cur = &ColIndex[c]; ColIndex[c].size++; Node* newNode = new Node( r, c ); while( cur->down != &ColIndex[c] && cur->down->row < r ) cur = cur->down; newNode->down = cur->down; newNode->up = cur; cur->down->up = newNode; cur->down = newNode; if( RowIndex[r].right == NULL ) { RowIndex[r].right = newNode; newNode->left = newNode; newNode->right = newNode; } else { Node* rowHead = RowIndex[r].right; cur = rowHead; while( cur->right != rowHead && cur->right->col < c ) cur = cur->right; newNode->right = cur->right; newNode->left = cur; cur->right->left = newNode; cur->right = newNode; } } void ExtracCoverMatrix::cover( int c ) { ColunmHeader* col = &ColIndex[c]; col->right->left = col->left; col->left->right = col->right; Node* curR, *curC; curC = col->down; while( curC != col ) { Node* noteR = curC; curR = noteR->right; while( curR != noteR ) { curR->down->up = curR->up; curR->up->down = curR->down; ColIndex[curR->col].size--; curR = curR->right; } curC = curC->down; } } void ExtracCoverMatrix::uncover( int c ) { Node* curR, *curC; ColunmHeader* col = &ColIndex[c]; curC = col->up; while( curC != col ) { Node* noteR = curC; curR = curC->left; while( curR != noteR ) { ColIndex[curR->col].size++; curR->down->up = curR; curR->up->down = curR; curR = curR->left; } curC = curC->up; } col->right->left = col; col->left->right = col; } int ExtracCoverMatrix::search( int k ) { if( root->right == root ) return k; ColunmHeader* choose = (ColunmHeader*)root->right, *cur=choose; while( cur != root ) { if( choose->size > cur->size ) choose = cur; cur = (ColunmHeader*)cur->right; } if( choose->size <= 0 ) return -1; cover( choose->col ); Node* curC = choose->down; while( curC != choose ) { disjointSubet[k] = curC->row; Node* noteR = curC; Node* curR = curC->right; while( curR != noteR ) { cover( curR->col ); curR = curR->right; } int res=-1; if( (res = search( k+1 ))>0 ) return res; curR = noteR->left; while( curR != noteR ) { uncover( curR->col ); curR = curR->left; } curC = curC->down; } uncover( choose->col ); return -1; }
下面是python的实现版本,显然比c++要简洁好多:
#这段代码基本上来自http://code.google.com/p/mycodeplayground/ #我更多的是理解和欣赏python程序的写法。。。 class Node(object): '''data object in dancing link Basic element for a sparse matrix doubly linked in both horizontal and vertical direction''' def __init__( self, left = None, right = None, up = None, down = None, column_header = None, row_header = None ): self.left = left or self self.right = right or self self.up = up or self self.down = down or self self.column_header = column_header self.row_header = row_header def nodes_in_direction( self, dir ): '''Generator for nodes in different direction :Parameters: dir: str dir can be 'up', 'down', 'left', 'right' ''' node = getattr( self, dir ) while node != self: #print node.column_header.size yield node node = getattr( node, dir ) class ColumnHeader(Node): '''list header, or column object in dancing link has an extra element of size recording the number of data object in this column''' def __init__( self, *args, **kargs ): Node.__init__( self, *args, **kargs ) self.size = 0 class RowHeader(Node): '''RowHeader is an extra element for dancing link to record the row no. of a row so that we can trace down which rows are picked up''' def __init__( self, rowno, *args, **kargs ): Node.__init__( self, *args, **kargs ) self.rowno = rowno self.row_header = self class DLXSolver(object): def __init__( self ): pass def solve(self, matrix, num_columns): '''solve the exact cover problem of a given matrix :Parameters: matrix is in the form as: { k:[x0, x1, ..., xn] } where k is the line number while xi(0<=xi<num_columns) is the column of the matrix where there is a 1 num_columns: number of columns in the matrix :Return: solution: [k] solution is a list of line number that is picked ''' self._partial_answer = {} self._k = 0 self._construct( matrix, num_columns ) self._search(0) return [self._partial_answer[k] for k in xrange(self._k)] def _construct( self, matrix, num_columns ): '''construct a matrix into a sparse matrix using doubly link list :Parameter: parameters are same as solve ''' self.root = Node() self.column_headers = [] #constructing column_headers for i in xrange(num_columns): new_column_header = ColumnHeader(left=self.root.left, right=self.root) self.root.left.right = new_column_header self.root.left = new_column_header self.column_headers.append( new_column_header ) #inserting nodes into the sparse matrix for k in matrix: if not matrix[k]: continue column_id_sorted = sorted(matrix[k]) column_header = self.column_headers[column_id_sorted[0]] column_header.size += 1 new_row_header = RowHeader(k, up=column_header.up, down=column_header, column_header=column_header) column_header.up.down = new_row_header column_header.up = new_row_header column_id_sorted.pop(0) #constructing remaining nodes for i in column_id_sorted: column_header = self.column_headers[i] column_header.size += 1 new_node = Node( left=new_row_header.left, right=new_row_header, up = column_header.up, down = column_header, column_header = column_header, row_header = new_row_header ) column_header.up.down = new_node column_header.up = new_node new_row_header.left.right = new_node new_row_header.left = new_node def _cover( self, column_header ): '''cover a column of a matrix as what donald said''' column_header.left.right = column_header.right column_header.right.left = column_header.left for eachNode in column_header.nodes_in_direction("down"): for node in eachNode.nodes_in_direction("right"): node.column_header.size -= 1 node.up.down = node.down node.down.up = node.up def _uncover( self, column_header ): '''uncover a column of a matrix in the reverse order as cover do''' for eachNode in column_header.nodes_in_direction("up"): for node in eachNode.nodes_in_direction("left"): node.column_header.size += 1 node.down.up = node node.up.down = node column_header.left.right = column_header column_header.right.left = column_header def _search( self, k ): if self.root.right == self.root: self._k = k return True column_header=min([header for header in self.root.nodes_in_direction("right")], key=lambda h:h.size) self._cover( column_header ) for eachNode in column_header.nodes_in_direction("down"): self._partial_answer[k] = eachNode.row_header.rowno for node in eachNode.nodes_in_direction("right"): self._cover(node.column_header) if self._search( k+1 ): return True for node in eachNode.nodes_in_direction("left"): self._uncover(node.column_header) self._uncover(column_header) return False '''test case for dlxSolver''' if __name__ == "__main__": matrix = { 1:[2, 4,5], 2:[0,3,6], 3:[1,2,5], 4:[0,3], 5:[1,6], 6:[3,4,6]} testSolver = DLXSolver() print testSolver.solve( matrix, 7 )
从数独到Exact-Cover问题:
那么,数独跟Exact-Cover有什么关系呢?我们来看看数独的限制:
1. 每个格里只能填一个数(1-9),有且只有一个
2. 每个数(1-9)在每一行里只能出现一次,有且只有一次
3. 每个数在每一列里只能出现一次,有且只有一次
4. 每个数在每一个块(block)只能出现一次,有且只有一次
这么多个“有且只有一次”显然在向我们发出强烈的信号:这是一个exact-cover问题!于是便有了下面的转换:
我们知道,在没有任何限制下,数独的第i行,第j列可以填入数字k,其中0<i,j,k<=9,因此我们实际上是从9^3个(i,j,k)中选择81个,同时使其满足以上4个限制。对于每一个(i,j,k),格(i,j)只能填一个数字的限制将表现为列(ci,cj)从k=[1,9]为1,所以我们只能选其中一行,也就是,在k=[1,9]中,只有一个(i,j,k0)被选中。对于每一个(i,j,k),行i只能出现一次k的限制将表现为列(ci, ck)从j=[1,9]为1,我们只能选择其中一行。对于限制3,4也是同样的道理。总共将需要81*4列,9^3行。这里[2]把这个问题说得很清楚。
下面分别是C++和python的数独求解:
#include<iostream> #include "ExactCoverMatrix.h" using namespace std; const int N_Cell = 3; const int N = 9; const int OFFSET = 9*9; const int ROWS = N*N*N; const int COLS = N*N*4; void solve_sudoku( int (*grid)[N] ) { //tranform a sudoku to a exact cover problem int** sudoku_matrix = new int*[ROWS]; for( int i=0; i<ROWS; i++ ) sudoku_matrix[i] = new int[5]; for( int i=0; i<N; i++ ) for( int j=0; j<N; j++ ) { int b = (i/N_Cell)*N_Cell + (j/N_Cell); int r = i*N*N + j*N; for( int k=1; k<=N; k++ ) { sudoku_matrix[r][0]=i*N+j+1; sudoku_matrix[r][1]=OFFSET+(i*N+k); sudoku_matrix[r][2]=OFFSET*2+(j*N+k); sudoku_matrix[r][3]=OFFSET*3+(b*N+k); sudoku_matrix[r][4]=-1; r++; } } SparseMatrix sudoku( ROWS, COLS, sudoku_matrix ); //cover those have been filled for( int i=0; i<N; i++ ) for( int j=0; j<N; j++ ) { int k; if( k = grid[i][j] ) { /* corresponding to insert( r, i*N+j+1 ); insert( r, OFFSET+(i*N+k) ); insert( r, OFFSET*2+(j*N+k) ); insert( r, OFFSET*3+(b*N+k) ); */ int b = (i/N_Cell)*N_Cell + j/N_Cell; sudoku.cover( i*N+j+1 ); sudoku.cover( OFFSET+( i*N+k ) ); sudoku.cover( OFFSET*2+(j*N+k) ); sudoku.cover( OFFSET*3+(b*N+k) ); } } int solved=0; if( (solved=sudoku.search())>0 ) { int c, r, k; for( int i=0; i<solved; i++ ) { r = sudoku.disjointSubet[i]/(N*N); c = (sudoku.disjointSubet[i]/N)%N; k = sudoku.disjointSubet[i]%N; grid[r][c]=k+1; } cout<<"here is the result: "<<endl; for( int i=0; i<N; i++ ) { for( int j=0; j<N; j++ ) cout<<grid[i][j]<<' '; cout<<endl; } } else cout<<"no solution for this sudoku"<<endl; } bool verify_puzzle( int (*grid)[N] ) { int line[N+1]={0}; for( int i=0; i<N; i++ ) { for( int j=0; j<=N; j++ ) line[j] = 0; for( int j=0; j<N; j++ ) { if( line[grid[i][j]] ) { cout<<"row wrong: "<<i<<endl; return false; } else line[grid[i][j]] = 1; } } for( int i=0; i<N; i++ ) { for( int j=0; j<N+1; j++ ) line[j] = 0; for( int j=0; j<N; j++ ) { if( line[grid[j][i]] ) { cout<<"column wrong"<<endl; return false; } else line[grid[j][i]] = 1; } } for( int i=0; i<N_Cell; i++ ) for( int j=0; j<N_Cell; j++ ) { for( int x=0; x<=N; x++ ) line[x] = 0; for( int k=0; k<N_Cell; k++ ) for( int t=0; t<N_Cell; t++ ) { int x = k+i*N_Cell; int y = t+j*N_Cell; if( line[grid[x][y]] ) { cout<<"block wrong"<<endl; return false; } else line[grid[x][y]] = 1; } } return true; } int main() { int puzzle[N][N]={ {0, 0, 0, 0, 0, 3, 0, 8, 1}, {2, 0, 0, 4, 0, 0, 0, 0, 0}, {0, 5, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 2, 3, 0, 7, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 5, 0}, {0, 0, 8, 6, 0, 0, 0, 0, 0}, {7, 0, 0, 0, 0, 0, 4, 0, 0}, {0, 9, 0, 0, 8, 0, 0, 0, 0}, {0, 0, 0, 0, 5, 0, 2, 0, 0}}; solve_sudoku( puzzle ); if( !verify_puzzle( puzzle ) ) cout<<"wrong answer!"<<endl; }
from __future__ import division from dlx_1 import DLXSolver class SudokuSolver(object): digits = set(map(str, range(1,10))) GRIDOFFSET=0 #column 0~80 is for testing each cell has only one digit ROWOFFSET=81 #column 81~161 is for testing each line contain each number exactly once COLOFFSET=162 #column 162~242 is for testing each column contain each number exactly once BLKOFFSET=243 #colunm 243~323 is for testing each block contain each number exactly once COLUNMS = 324 def __init__( self ): self.dlx_solver = DLXSolver() def solve( self, puzzle ): '''solve the given sudoku puzzle unfilled cell is represented with other symbols except 1~9 ''' dlx_matrix = self._construct(puzzle) result = self.dlx_solver.solve( dlx_matrix, self.COLUNMS ) solved_sudoku=list(puzzle) for row, col, val in result: solved_sudoku[row*9+col]=val+1 return solved_sudoku def _construct( self, puzzle ): '''construct a sudoku puzzle to a dlx_matrix''' linear_puzzle = ''.join(puzzle.split()) if len(linear_puzzle) != 81: print 'invalid puzzle!' return dlx_matrix={} for i, c in enumerate(linear_puzzle): row, col = divmod(i,9) if c in self.digits: val = int(c)-1 dlx_matrix[(row, col, val)]=self._get_ones(row, col, val) else: for val in range(9): dlx_matrix[(row, col, val)]=self._get_ones(row, col, val) return dlx_matrix def _get_ones(self, row, col, val): blk = (row//3)*3 + col//3 ones = [row*9+col+self.GRIDOFFSET, row*9+val+self.ROWOFFSET, col*9+val+self.COLOFFSET, blk*9+val+self.BLKOFFSET] return ones if __name__=="__main__": puzzle = "4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......" solver = SudokuSolver() res = solver.solve(puzzle) for i in range(9): print res[i*9: i*9+9]
最后最后,这里[3]提供了一个30行的dlx的python代码,非常的精致,建议大家都去看一看~!
__________________________________________
[1]Dancing Links, Donald E. Knuth(附件下载)
[2]http://cgi.cse.unsw.edu.au/~xche635/dlx_sodoku/
[3]http://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
发表评论
-
我对KM算法的理解
2012-12-26 15:47 26032一般对KM算法的描述, ... -
一个小题目(单词统计)
2012-08-14 23:12 1343今天休息的时候看到一个关于单词统计的小题目: 统计一段由字符 ... -
数独人工解法的一些技巧及其python实现
2012-06-13 16:31 6644这段日子实现了十几种 ... -
Eva'Sudoku-0.1新鲜出炉啦~~
2012-05-27 21:06 1353呵呵,经过将近一个星期的对pygame的了解与熟悉,我终于磕磕 ... -
产生数独迷题
2012-05-24 18:13 1996随着数独解题算法DLX的 ... -
总共有多少个数独?
2012-05-12 15:31 12798憋屈地看了一个星期的 ... -
编程之美续
2012-04-06 15:37 1155看完编程之美后看很多题,都会发现原来只是里面一些题目的变种(也 ... -
编程之美
2012-04-02 16:54 1490前段日子又看了编程之美,后来闲着无聊学python去了,想着书 ... -
数据结构——2-3树
2012-02-10 14:25 7185年前实现了一个2-3树, ... -
数据结构 -- 二叉树(BST, AVLTree, RBTree, SplayTree)
2012-01-17 21:31 2785在《基于树的索引结构介绍》(http://philoscien ... -
编程珠玑--关于查找(二分法、自组织链、哈希)
2011-12-31 19:36 2129查找是我们现实生活中 ... -
编程珠玑 -- 关于堆
2011-12-28 15:31 1154堆是这样一种数据结构,它首先是一棵二叉树,而且还是一棵完全二叉 ... -
编程珠玑 -- 关于排序
2011-12-27 20:12 1019《编程珠玑》主要提到 ... -
Heritrix总结及消重算法初探
2011-06-02 12:38 2644好久没有更新博客了。最后一次更新居然已经是一个月以前的事了。忍 ... -
图论--旅行商问题
2011-05-04 14:57 1500五一果然基本献给了数据压缩(除了两个晚上用于打球),看了小波的 ... -
图论--寻找欧拉回路
2011-04-27 21:05 1809首先介绍一下fleury算法。 大概描述是这样子的: (1 ... -
图论--中国邮递员问题
2011-04-27 20:25 14015中国邮递员问题就比较 ... -
图论--关键路径
2011-04-27 19:37 1864最近忙着做作业。主要是《代数与图论》的一些算法的实现,五一估计 ... -
C语言中的文件操作
2011-04-16 11:34 807常常觉得,我对很多东西都是要求会用就好,不求甚解。比如说每次一 ...
相关推荐
KUKA机器人相关资料
内容概要:本文详细介绍了利用Matlab实现模拟退火算法来优化旅行商问题(TSP)。首先阐述了TSP的基本概念及其在路径规划、物流配送等领域的重要性和挑战。接着深入讲解了模拟退火算法的工作原理,包括高温状态下随机探索、逐步降温过程中选择较优解或以一定概率接受较差解的过程。随后展示了具体的Matlab代码实现步骤,涵盖城市坐标的定义、路径长度的计算方法、模拟退火主循环的设计等方面。并通过多个实例演示了不同参数配置下的优化效果,强调了参数调优的重要性。最后讨论了该算法的实际应用场景,如物流配送路线优化,并提供了实用技巧和注意事项。 适合人群:对路径规划、物流配送优化感兴趣的科研人员、工程师及高校学生。 使用场景及目标:适用于需要解决复杂路径规划问题的场合,特别是涉及多个节点间最优路径选择的情况。通过本算法可以有效地减少路径长度,提高配送效率,降低成本。 其他说明:文中不仅给出了完整的Matlab代码,还包括了一些优化建议和技术细节,帮助读者更好地理解和应用这一算法。此外,还提到了一些常见的陷阱和解决方案,有助于初学者避开常见错误。
内容概要:本文详细介绍了如何利用Simulink进行自动代码生成,在STM32平台上实现带57次谐波抑制功能的霍尔场定向控制(FOC)。首先,文章讲解了所需的软件环境准备,包括MATLAB/Simulink及其硬件支持包的安装。接着,阐述了构建永磁同步电机(PMSM)霍尔FOC控制模型的具体步骤,涵盖电机模型、坐标变换模块(如Clark和Park变换)、PI调节器、SVPWM模块以及用于抑制特定谐波的陷波器的设计。随后,描述了硬件目标配置、代码生成过程中的注意事项,以及生成后的C代码结构。此外,还讨论了霍尔传感器的位置估算、谐波补偿器的实现细节、ADC配置技巧、PWM死区时间和换相逻辑的优化。最后,分享了一些实用的工程集成经验,并推荐了几篇有助于深入了解相关技术和优化控制效果的研究论文。 适合人群:从事电机控制系统开发的技术人员,尤其是那些希望掌握基于Simulink的自动代码生成技术,以提高开发效率和控制精度的专业人士。 使用场景及目标:适用于需要精确控制永磁同步电机的应用场合,特别是在面对高次谐波干扰导致的电流波形失真问题时。通过采用文中提供的解决方案,可以显著改善系统的稳定性和性能,降低噪声水平,提升用户体验。 其他说明:文中不仅提供了详细的理论解释和技术指导,还包括了许多实践经验教训,如霍尔传感器处理、谐波抑制策略的选择、代码生成配置等方面的实际案例。这对于初学者来说是非常宝贵的参考资料。
内容概要:本文详细介绍了基于西门子S7-200 PLC和组态王的机械手搬运控制系统的实现方案。首先,文章展示了梯形图程序的关键逻辑,如急停连锁保护、水平移动互锁以及定时器的应用。接着,详细解释了IO分配的具体配置,包括数字输入、数字输出和模拟量接口的功能划分。此外,还讨论了接线图的设计注意事项,强调了电磁阀供电和继电器隔离的重要性。组态王的画面设计部分涵盖了三层画面结构(总览页、参数页、调试页)及其动画脚本的编写。最后,分享了调试过程中遇到的问题及解决方案,如传感器抖动、输出互锁设计等。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是对PLC编程和组态软件有一定基础的读者。 使用场景及目标:适用于自动化生产线中机械手搬运控制系统的开发与调试。目标是帮助读者掌握从硬件接线到软件逻辑的完整实现过程,提高系统的稳定性和可靠性。 其他说明:文中提供了大量实践经验,包括常见的错误和解决方案,有助于读者在实际工作中少走弯路。
内容概要:本文详细介绍了基于西门子1200PLC的污水处理项目,涵盖了PLC程序结构、通信配置、HMI设计以及CAD原理图等多个方面。PLC程序采用梯形图和SCL语言相结合的方式,实现了复杂的控制逻辑,如水位控制、曝气量模糊控制等。通讯配置采用了Modbus TCP和Profinet双协议,确保了设备间高效稳定的通信。HMI设计则注重用户体验,提供了详细的报警记录和趋势图展示。此外,CAD图纸详尽标注了设备位号,便于后期维护。操作说明书中包含了应急操作流程和定期维护建议,确保系统的长期稳定运行。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对PLC编程、HMI设计和通信配置感兴趣的从业者。 使用场景及目标:适用于污水处理厂及其他类似工业控制系统的设计、实施和维护。目标是帮助工程师掌握完整的项目开发流程,提高系统的可靠性和效率。 其他说明:文中提供的具体代码片段和设计思路对于理解和解决实际问题非常有价值,建议读者结合实际项目进行深入学习和实践。
内容概要:本文详细介绍了基于5电平三相模块化多电平变流器(MMC)的虚拟同步发电机(VSG)控制系统的构建与仿真。首先,文章描述了MMC的基本结构和参数设置,包括子模块电容电压均衡策略和载波移相策略。接着,深入探讨了VSG控制算法的设计,特别是有功-频率和无功-电压下垂控制的具体实现方法。文中还展示了通过MATLAB-Simulink进行仿真的具体步骤,包括设置理想的直流电源和可编程三相源来模拟电网扰动。仿真结果显示,VSG控制系统能够在面对频率和电压扰动时迅速恢复稳定,表现出良好的调频调压性能。 适合人群:从事电力电子、电力系统自动化及相关领域的研究人员和技术人员。 使用场景及目标:适用于研究和开发新型电力电子设备,特别是在新能源接入电网时提高系统的稳定性。目标是通过仿真验证VSG控制的有效性,为实际应用提供理论支持和技术指导。 其他说明:文章提供了详细的代码片段和仿真配置,帮助读者更好地理解和重现实验结果。此外,还提到了一些常见的调试技巧和注意事项,如选择合适的仿真步长和参数配对调整。
内容概要:本文详细介绍了在一个复杂的工业自动化项目中,如何利用西门子S7-1200 PLC为核心,结合基恩士视觉相机、ABB机器人以及G120变频器等多种设备,构建了一个高效的立体库码垛系统。文中不仅探讨了不同设备之间的通信协议(如Modbus TCP和Profinet),还展示了SCL和梯形图混合编程的具体应用场景和技术细节。例如,通过SCL进行视觉坐标解析、机器人通信心跳维护等功能的实现,而梯形图则用于处理简单的状态切换和安全回路。此外,作者分享了许多实际调试过程中遇到的问题及其解决方案,强调了良好的注释习惯对于提高代码可维护性的关键作用。 适用人群:从事工业自动化领域的工程师和技术人员,尤其是对PLC编程、机器人控制及多种通信协议感兴趣的从业者。 使用场景及目标:适用于需要整合多种工业设备并确保它们能够稳定协作的工作环境。主要目标是在保证系统高精度的同时降低故障率,从而提升生产效率。 其他说明:文中提到的一些具体技术和方法可以作为类似项目的参考指南,帮助开发者更好地理解和应对复杂的工业控制系统挑战。
KUKA机器人相关资料
java脱敏工具类
内容概要:本文详细介绍了基于自抗扰控制(ADRC)的表贴式永磁同步电机(SPMSM)双环控制系统的建模与实现方法。该系统采用速度环一阶ADRC控制和电流环PI控制相结合的方式,旨在提高电机在复杂工况下的稳定性和响应速度。文章首先解释了选择ADRC的原因及其优势,接着展示了ADRC和PI控制器的具体实现代码,并讨论了在Matlab/Simulink环境中搭建模型的方法和注意事项。通过对不同工况下的仿真测试,验证了该控制策略的有效性,特别是在负载突变情况下的优越表现。 适合人群:从事电机控制、自动化控制及相关领域的研究人员和技术人员,尤其是对自抗扰控制感兴趣的工程师。 使用场景及目标:适用于需要高精度、高响应速度的工业伺服系统和其他高性能电机应用场景。目标是提升电机在复杂环境下的稳定性和抗扰能力,减少转速波动和恢复时间。 其他说明:文中提供了详细的代码示例和调试技巧,帮助读者更好地理解和实施该控制策略。同时,强调了在实际应用中需要注意的问题,如参数调整、输出限幅等。
java设计模式之责任链的使用demo
内容概要:本文详细介绍了两相交错并联Buck/Boost变换器的硬件结构和三种控制方式(开环、电压单环、双环)的实现方法及仿真结果。文中首先描述了该变换器的硬件结构特点,即四个MOS管组成的H桥结构,两相电感交错180度工作,从而有效减少电流纹波。接着,针对每种控制方式,具体讲解了其配置步骤、关键参数设置以及仿真过程中需要注意的问题。例如,在开环模式下,通过固定PWM占空比来观察原始波形;电压单环则引入PI控制器进行电压反馈调节;双环控制进一步增加了电流内环,实现了更为精确的电流控制。此外,文章还探讨了单向结构的特点,并提供了仿真技巧和避坑指南。 适合人群:从事电力电子研究的技术人员、高校相关专业师生。 使用场景及目标:适用于希望深入了解两相交错并联Buck/Boost变换器的工作原理和技术细节的研究者,旨在帮助他们掌握不同控制方式的设计思路和仿真方法。 其他说明:文中不仅提供了详细的理论解释,还有丰富的实例代码片段,便于读者理解和实践。同时,作者分享了许多宝贵的实践经验,有助于避免常见的仿真错误。
第二场c++A组
数控磨床编程.ppt
内容概要:本文详细介绍了利用COMSOL软件进行N2和CO2混合气体在热-流-固三场耦合作用下增强煤层气抽采的数值模拟。首先,通过设定煤岩材料参数,如热导率、杨氏模量等,构建了煤岩物理模型。接着,引入达西定律和Maxwell-Stefan扩散方程,建立了混合气体运移方程,考虑了气体膨胀系数和吸附特性。在应力场求解方面,采用自适应步长和阻尼系数调整,确保模型稳定。同时,探讨了温度场与气体运移的耦合机制,特别是在低温条件下CO2注入对煤体裂隙扩展的影响。最后,通过粒子追踪和流线图展示了气体运移路径和抽采效率的变化。 适合人群:从事煤层气开采、数值模拟以及相关领域的科研人员和技术工程师。 使用场景及目标:适用于需要优化煤层气抽采工艺的研究机构和企业,旨在通过数值模拟提高抽采效率并减少环境影响。 其他说明:文中提供了详细的MATLAB和COMSOL代码片段,帮助读者理解和复现模型。此外,强调了模型参数选择和求解器配置的重要性,分享了作者的实际经验和常见问题解决方法。
基于Bode的引线补偿器设计 计算给定G、相位裕度、交叉频率和安全裕度要求的引线补偿器。 计算给定电厂G、PM和Wc要求的铅补偿器,并运行ControlSystemDesigner进行验证。
KUKA机器人相关文档
包括:源程序工程文件、Proteus仿真工程文件、配套技术手册等 1、采用51/52单片机作为主控芯片; 2、采用数码管显示计时秒数,单个操作均为20秒; 3、采用继电器控制进水、排水; 4、采用L298驱动电机; 5、具有强洗、标准洗、弱洗、甩干四种模式; 6、强洗流程:进水、三轮洗涤、排水、甩干、进水、漂洗、排水、甩干; 7、标准洗流程:进水、两轮洗涤、排水、甩干、进水、漂洗、排水、甩干; 8、弱洗流程:进水、一轮洗涤、排水、甩干、进水、漂洗、排水、甩干;
内容概要:本文详细介绍了如何利用MATLAB 2018b搭建微电网并网逆变器模型,采用电压电流双环控制配合SPWM调制技术,实现稳定的并网控制。文中涵盖了PI参数整定、下垂系数计算、SPWM载波生成、PLL改进等多个关键技术环节,并分享了调试经验和常见问题解决方案。通过具体的代码示例展示了各模块的具体实现方法,强调了电流环和电压环的设计要点以及下垂控制的应用。 适合人群:具有一定电力电子和控制系统基础知识的研究人员和技术人员,尤其是从事微电网研究和开发的专业人士。 使用场景及目标:适用于希望深入了解微电网并网控制机制及其具体实现方式的学习者和从业者。主要目标是掌握电压电流双环控制与SPWM调制相结合的方法,理解下垂控制的工作原理,并能够独立完成相关系统的建模与调试。 其他说明:文中提供的代码片段可以直接用于MATLAB/Simulink环境进行实验验证,同时附带了许多宝贵的实践经验,如参数选择、波形分析等,有助于提高实际项目的成功率。此外,还特别提到了一些容易被忽视但至关重要的细节,比如载波频率设置、死区时间和谐波抑制等问题。