偶然看到《谈谈 Sudoku (数独)》[1]的博文,心血来潮把文章的算法实现了一番。有关Sudoku的具体介绍可参考维基百科。
具体解法有:回溯、精确匹配。回溯解法《谈谈 Sudoku (数独)》有比较详细的阐述,所以本文只记录一下精确覆盖的解法。
精确覆盖[2]
数度到矩阵的转换
Algorithm X使用了矩阵的表示方法,因此在需要把数度表示成矩阵的形式。具体转换如下:假设一个给定的9 * 9数度中,有N个空格需要求解,那么矩阵M的行数R=N * 9,列数C=4 * N。其中:
a.M[i][j]=1, 0<=j<N,表示第j个空格考虑行、列、块以后可以取数字i%9 + 1
b.M[i][j]=1, N<=j<2N,表示第j个空格只考虑行的情况下可以取数字i%9 + 1
c.M[i][j]=1, 2N<=j<3N,表示第j个空格只考虑列的情况下可以取数字i%9 + 1
1. http://blog.youkuaiyun.com/Solstice/article/details/2096209
2. http://en.wikipedia.org/wiki/Exact_cover
3. http://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X
4. http://lanl.arxiv.org/PS_cache/cs/pdf/0011/0011047v1.pdf
具体解法有:回溯、精确匹配。回溯解法《谈谈 Sudoku (数独)》有比较详细的阐述,所以本文只记录一下精确覆盖的解法。
精确覆盖[2]
- 1.精确覆盖
- 给定集合X、S、T。S是X的子集的集合,T是S的子集,如果X中每个元素都只被T中的一个元素包含,那么T就是X的精确覆盖
- 2.精确命中(exact hitting)
- 给定集合X、S、Y。S是X的子集的集合,Y是X的子集,如果Y中每个元素都只被S中的一个元素包含,那么Y就是S的精确命中
- 3.表示方法
- 列表、矩阵
- 4.实现
- Knuth's Algorithm X[3],解决精确覆盖的深度优先、递归、不确定算法。
大概思想与技巧[4]:利用矩阵的表示方法,同时把行列表示成双向环列表(Dancing Link),在删除列表中的元素时,只更新前后(上下)的相关指针值。
数度到矩阵的转换
Algorithm X使用了矩阵的表示方法,因此在需要把数度表示成矩阵的形式。具体转换如下:假设一个给定的9 * 9数度中,有N个空格需要求解,那么矩阵M的行数R=N * 9,列数C=4 * N。其中:
a.M[i][j]=1, 0<=j<N,表示第j个空格考虑行、列、块以后可以取数字i%9 + 1
b.M[i][j]=1, N<=j<2N,表示第j个空格只考虑行的情况下可以取数字i%9 + 1
c.M[i][j]=1, 2N<=j<3N,表示第j个空格只考虑列的情况下可以取数字i%9 + 1
d.M[i][j]=1, 3N<=j<4N,表示第j个空格只考虑块的情况下可以取数字i%9 + 1
代码如下:
#include <stdlib.h>
#include <stdio.h>
struct column_object;
typedef struct data_obj_ {
struct data_obj_ *l, *r, *u, *d;
struct column_object *c;
int real_r;
} data_object;
typedef data_object list_header;
struct column_object {
data_object* data;
int s;
int n;
};
static inline void
append_right_tail(data_object *tail, data_object *p2);
static inline void
append_down_tail(data_object *tail, data_object *p2);
static void inline
init_data_obj(data_object *p);
static void inline
init_column_obj(struct column_object *c);
static inline void
free_resource(struct column_object *h);
static data_object *
create_dlx(int *matrix, int r, int c, struct column_object **h);
static int
search_dlx(struct column_object *h, int c, int k, data_object **out,
int **r_out, int *r_cnt_out);
static inline void
cover_column(data_object *c);
static inline void
uncover_column(data_object *c);
static data_object *
create_dlx(int *matrix, int r, int c, struct column_object **h)
{
int i, j, ele_cnt;
data_object *data, *right_most, *cur, *tail;
struct column_object *root;
for (i = 0, ele_cnt = 0; i < r; ++i)
for (j = 0; j < c; ele_cnt += matrix[i * c + j], ++j)
;
data = (list_header *) malloc(sizeof(data_object) * (ele_cnt + 1 + c));
root = (struct column_object *) malloc(sizeof (struct column_object) * (c + 1));
*h = root;
root->data = data++;
init_column_obj(root);
root->s = r + 1;
for (i = 0, ++root; i < c; ++i, ++root) {
cur = root->data = data++;
init_column_obj(root);
root->n = i;
append_right_tail((root - 1)->data, root->data);
}
for (i = 0; i < r; ++i) {
right_most = NULL;
for (j = 0; j < c; ++j) {
if (matrix[i * c + j] == 0) continue;
cur = data++;
cur->real_r = i;
if (right_most != NULL) {
append_right_tail(right_most, cur);
} else {
init_data_obj(cur);
right_most = cur;
}
tail = (*h + j + 1)->data->u;
append_down_tail(tail, cur);
}
}
return (*h)->data + c + 1;
}
static inline void
free_resource(struct column_object *h)
{
free(h->data);
free(h);
}
int
search(int *matrix, int r, int c, int **r_out, int *r_cnt_out)
{
struct column_object *h;
create_dlx(matrix, r, c, &h);
data_object **out = (data_object **) malloc(sizeof (data_object *) * r);
int ans = search_dlx(h, c, 0, out, r_out, r_cnt_out);
free_resource(h);
free(out);
return ans;
}
static int
search_dlx(struct column_object *h, int c, int k, data_object **out,
int **r_out, int *r_cnt_out)
{
int c_it;
data_object *id_it, *sel_d, *jd_it;
if (h->data->r == h->data) {
for (c_it = 0; c_it < k; ++c_it)
(*r_out)[c_it] = out[c_it]->real_r;
*r_cnt_out = c_it;
return 0;
}
for (sel_d = h->data->d, id_it = sel_d->r;
id_it != sel_d; id_it = id_it->r)
if (sel_d->c->s > id_it->c->s)
sel_d = id_it;
/*conver column sel_d*/
cover_column(sel_d);
for (id_it = sel_d->d; id_it != sel_d; id_it = id_it->d) {
out[k] = id_it;
for (jd_it = id_it->r; jd_it != id_it; jd_it = jd_it->r)
cover_column(jd_it->c->data);
if (search_dlx(h, c, k + 1, out, r_out, r_cnt_out) == 0) return 0;
for (jd_it = id_it->l; jd_it != id_it; jd_it = jd_it->l)
uncover_column(jd_it->c->data);
}
uncover_column(sel_d);
return -1;
}
static inline void
cover_column(data_object *c)
{
data_object *i, *j;
c->r->l = c->l; c->l->r = c->r;
for (i = c->d; i != c; i = i->d) {
for (j = i->r; j != i; j = j->r) {
j->d->u = j->u; j->u->d = j->d;
j->c->s -= 1;
}
}
}
static inline void
uncover_column(data_object *c)
{
data_object *i, *j;
for (i = c->u; i != c; i = i->u) {
for (j = i->l; j != i; j = j->l) {
j->c->s += 1;
j->d->u = j; j->u->d = j;
}
}
c->r->l = c; c->l->r = c;
}
static inline void
append_right_tail(data_object *tail, data_object *p2)
{
p2->l = tail;
p2->r = tail->r;
tail->r->l = p2;
tail->r = p2;
}
static inline void
append_down_tail(data_object *tail, data_object *p2)
{
p2->c = tail->c;
p2->u = tail;
p2->d = tail->d;
tail->d->u = p2;
tail->d = p2;
p2->c->s += 1;
}
static void inline
init_data_obj(data_object *p)
{
p->l = p->r = p->d = p->u = p;
}
static void inline
init_column_obj(struct column_object *c)
{
data_object *p = c->data;
init_data_obj(p);
p->c = c;
c->s = 0;
}
1. http://blog.youkuaiyun.com/Solstice/article/details/2096209
2. http://en.wikipedia.org/wiki/Exact_cover
3. http://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X
4. http://lanl.arxiv.org/PS_cache/cs/pdf/0011/0011047v1.pdf