Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/dlx.py
4058 views
1
"""
2
Exact Cover Problem via Dancing Links
3
"""
4
# dlx.py
5
# Copyright (c) 2006,2008 Antti Ajanki <[email protected]>
6
7
# Heavily Modified Feb 2008, Tom Boothby
8
# * Added a function which takes a Sage matrix and solves
9
# the exact cover problem for it.
10
# * Recursive search is now iterative
11
# * Removed callback functionality
12
# * Revamped the class to be a pythonic generator; new usage:
13
# for cover in DLXMatrix(ones,initialsolution):
14
# blah(cover)
15
# * DOCUMENTATION AND TESTS GALORE HOLYCRAP 100% COVERAGE!!!!!1!!111@%^%*QQ!@E~
16
17
# DLXMatrix class is used to store and solve an exact cover problem
18
# with help of Dancing Links [1] technique by Donald Knuth. The
19
# problem can be stated as an attempt to leave out rows of a 0/1
20
# matrix until remaining matrix has exactly one 1 in each column.
21
# Knuth proposes a fast solution technique based on clever trick with
22
# double linked list.
23
#
24
# This implementation will return row numbers of the solution instead
25
# column indexes (like in the Knuth's paper).
26
#
27
# [1] Donald E Knuth, Dancing links, preprint, available at
28
# http://www-cs-faculty.stanford.edu/~knuth/preprints.html
29
30
# This program is free software; you can redistribute it and/or
31
# modify it under the terms of the GNU General Public License
32
# as published by the Free Software Foundation; either version 2
33
# of the License, or (at your option) any later version.
34
35
36
37
ROOTNODE = 0
38
39
# Node's attributes
40
# LEFT, RIGHT, UP and DOWN are identifiers of corresponding neighbors
41
# INDEX is the (row) index of the node (None in the header nodes)
42
# COLUMN/COUNT is column index on a regular node and count of nodes on
43
# a header node
44
LEFT = 0
45
RIGHT = 1
46
UP = 2
47
DOWN = 3
48
COLUMN = 4
49
INDEX = 5
50
COUNT = 5
51
52
53
class DLXMatrix:
54
def __init__(self, ones, initialsolution=None):
55
"""
56
Solves the Exact Cover problem by using the Dancing Links algorithm
57
described by Knuth.
58
59
Consider a matrix M with entries of 0 and 1, and compute a subset
60
of the rows of this matrix which sum to the vector of all 1's.
61
62
The dancing links algorithm works particularly well for sparse
63
matrices, so the input is a list of lists of the form: (note the
64
1-index!)::
65
66
[
67
[1, [i_11,i_12,...,i_1r]]
68
...
69
[m,[i_m1,i_m2,...,i_ms]]
70
]
71
72
where M[j][i_jk] = 1.
73
74
The first example below corresponds to the matrix::
75
76
1110
77
1010
78
0100
79
0001
80
81
which is exactly covered by::
82
83
1110
84
0001
85
86
and
87
88
::
89
90
1010
91
0100
92
0001
93
94
EXAMPLES::
95
96
sage: from sage.combinat.dlx import *
97
sage: ones = [[1,[1,2,3]]]
98
sage: ones+= [[2,[1,3]]]
99
sage: ones+= [[3,[2]]]
100
sage: ones+= [[4,[4]]]
101
sage: DLXM = DLXMatrix(ones,[4])
102
sage: for C in DLXM:
103
... print C
104
[4, 1]
105
[4, 2, 3]
106
107
.. note::
108
109
The 0 entry is reserved internally for headers in the
110
sparse representation, so rows and columns begin their
111
indexing with 1. Apologies for any heartache this
112
causes. Blame the original author, or fix it yourself.
113
"""
114
if initialsolution is None: initialsolution = []
115
self._cursolution = []
116
self._nodes = [[0, 0, None, None, None, None]]
117
self._constructmatrix(ones, initialsolution)
118
self._level = 0
119
self._stack = [(None,None)]
120
121
def __eq__(self, other):
122
r"""
123
Return ``True`` if every attribute of
124
``other`` matches the attribute of
125
``self``.
126
127
INPUT:
128
129
130
- ``other`` - a DLX matrix
131
132
133
EXAMPLE::
134
135
sage: from sage.combinat.dlx import *
136
sage: M = DLXMatrix([[1,[1]]])
137
sage: M == loads(dumps(M))
138
True
139
"""
140
if not isinstance(other, DLXMatrix):
141
return False
142
return self.__dict__ == other.__dict__
143
144
def __iter__(self):
145
"""
146
Returns self.
147
148
TESTS::
149
150
sage: from sage.combinat.dlx import *
151
sage: M = DLXMatrix([[1,[1]]])
152
sage: print M.__iter__() is M
153
True
154
"""
155
156
return self
157
158
def _walknodes(self, firstnode, direction):
159
"""
160
Generator for iterating over all nodes in given direction (not
161
including firstnode).
162
163
TESTS::
164
165
sage: from sage.combinat.dlx import *
166
sage: ones = [[1,[1,2,3]]]
167
sage: ones+= [[2,[1,3]]]
168
sage: ones+= [[3,[2]]]
169
sage: ones+= [[4,[4]]]
170
sage: DLX = DLXMatrix(ones,[4])
171
sage: count = 0
172
sage: for c in DLX._walknodes(ROOTNODE,RIGHT):
173
... count += DLX._nodes[c][COUNT]
174
... for d in DLX._walknodes(c,DOWN):
175
... count -= 1
176
sage: print count
177
0
178
"""
179
nodetable=self._nodes
180
n = nodetable[firstnode][direction]
181
while n != firstnode:
182
yield n
183
n = nodetable[n][direction]
184
185
def _constructmatrix(self, ones, initialsolution=None):
186
"""
187
Construct the (sparse) DLX matrix based on list 'ones'. The first
188
component in the list elements is row index (which will be returned
189
by solve) and the second component is list of column indexes of
190
ones in given row.
191
192
'initialsolution' is list of row indexes that are required to be
193
part of the solution. They will be removed from the matrix.
194
195
Note: rows and cols are 1-indexed - the zero index is reserved for
196
the root node and column heads.
197
198
TESTS::
199
200
sage: from sage.combinat.dlx import *
201
sage: ones = [[1,[1,2,3]]]
202
sage: ones+= [[2,[1,3]]]
203
sage: ones+= [[3,[2]]]
204
sage: ones+= [[4,[4]]]
205
sage: DLX = DLXMatrix([[1,[1]]])
206
sage: DLX._constructmatrix(ones,[4])
207
sage: c = DLX._nodes[ROOTNODE][RIGHT]
208
sage: fullcount = 0
209
sage: while c != ROOTNODE:
210
... fullcount += DLX._nodes[c][COUNT]
211
... d = DLX._nodes[c][DOWN]
212
... while d != c:
213
... bad = DLX._nodes[DLX._nodes[d][DOWN]][UP] != d
214
... bad|= DLX._nodes[DLX._nodes[d][UP]][DOWN] != d
215
... bad|= DLX._nodes[DLX._nodes[d][LEFT]][RIGHT] != d
216
... bad|= DLX._nodes[DLX._nodes[d][RIGHT]][LEFT] != d
217
... if bad:
218
... raise RuntimeError, "Linked list inconsistent."
219
... d = DLX._nodes[d][DOWN]
220
... c = DLX._nodes[c][RIGHT]
221
sage: print fullcount
222
6
223
"""
224
if initialsolution is None: initialsolution = []
225
self._cursolution = []
226
# LEFT, RIGHT, UP, DOWN, COLUMN, INDEX/COUNT
227
self._nodes = [[ROOTNODE, ROOTNODE, None, None, None, None]]
228
229
# optimization: local variables are faster
230
nodetable = self._nodes
231
ones.sort()
232
pruneNodes = []
233
headers = [ROOTNODE] # indexes of header nodes for faster access
234
for r in ones:
235
curRow = r[0] # row index
236
columns = r[1] # column indexes
237
if len(columns) == 0: continue
238
columns.sort()
239
240
# Do we need more headers?
241
while len(headers) <= columns[-1]:
242
lastheader = headers[-1]
243
newind = len(nodetable)
244
nodetable.append([lastheader, ROOTNODE, newind, newind, None, 0])
245
nodetable[ROOTNODE][LEFT] = newind
246
nodetable[lastheader][RIGHT] = newind
247
headers.append(newind)
248
249
# Add new nodes to indexes newind..newind+len(columns)-1
250
# LEFT and RIGHT links can be calculated when created the
251
# node, only UP, DOWN and COUNT have to be updated when
252
# adding new nodes
253
newind = len(nodetable)
254
for i, c in enumerate(columns):
255
h = headers[c]
256
l = newind + ((i-1) % len(columns))
257
r = newind + ((i+1) % len(columns))
258
nodetable.append([l, r, nodetable[h][UP], h, h, curRow])
259
nodetable[nodetable[h][UP]][DOWN] = newind+i
260
nodetable[h][UP] = newind+i
261
nodetable[h][COUNT] += 1
262
263
if curRow in initialsolution:
264
pruneNodes.append(newind)
265
266
267
# Remove columns that are required to be in the solution
268
for n in pruneNodes:
269
self._cursolution += [nodetable[n][INDEX]]
270
self._covercolumn(nodetable[n][COLUMN])
271
for j in self._walknodes(n, RIGHT):
272
self._covercolumn(nodetable[j][COLUMN])
273
274
def _covercolumn(self, c):
275
"""
276
Performs the column covering operation, as described by Knuth's
277
pseudocode::
278
279
cover(c):
280
i <- D[c]
281
while i != c:
282
j <- R[i]
283
while j != i
284
D[U[j]] <- D[j]
285
U[D[j]] <- U[j]
286
N[C[j]] <- N[C[j]] - 1
287
j <- R[j]
288
i <- D[i]
289
290
This is undone by the uncover operation.
291
292
TESTS::
293
294
sage: from sage.combinat.dlx import *
295
sage: M = DLXMatrix([[1,[1,3]],[2,[1,2]],[3,[2]]])
296
sage: one = M._nodes[ROOTNODE][RIGHT]
297
sage: M._covercolumn(one)
298
sage: two = M._nodes[ROOTNODE][RIGHT]
299
sage: three = M._nodes[two][RIGHT]
300
sage: print M._nodes[three][RIGHT] == ROOTNODE
301
True
302
sage: print M._nodes[two][COUNT]
303
1
304
sage: print M._nodes[three][COUNT]
305
0
306
"""
307
308
nodetable = self._nodes
309
nodetable[nodetable[c][RIGHT]][LEFT] = nodetable[c][LEFT]
310
nodetable[nodetable[c][LEFT]][RIGHT] = nodetable[c][RIGHT]
311
for i in self._walknodes(c, DOWN):
312
for j in self._walknodes(i, RIGHT):
313
nodetable[nodetable[j][DOWN]][UP] = nodetable[j][UP]
314
nodetable[nodetable[j][UP]][DOWN] = nodetable[j][DOWN]
315
nodetable[nodetable[j][COLUMN]][COUNT] -= 1
316
317
def _uncovercolumn(self, c):
318
"""
319
Performs the column uncovering operation, as described by Knuth's
320
pseudocode::
321
322
uncover(c):
323
i <- U[c]
324
while i != c:
325
j <- L[i]
326
while j != i
327
U[j] <- U[D[j]]
328
D[j] <- D[U[j]]
329
N[C[j]] <- N[C[j]] + 1
330
j <- L[j]
331
i <- U[i]
332
333
This undoes by the cover operation since everything is done in the
334
reverse order.
335
336
TESTS::
337
338
sage: from sage.combinat.dlx import *
339
sage: M = DLXMatrix([[1,[1,3]],[2,[1,2]],[3,[2]]])
340
sage: one = M._nodes[ROOTNODE][RIGHT]
341
sage: M._covercolumn(one)
342
sage: two = M._nodes[ROOTNODE][RIGHT]
343
sage: M._uncovercolumn(one)
344
sage: print M._nodes[two][LEFT] == one
345
True
346
sage: print M._nodes[two][COUNT]
347
2
348
"""
349
nodetable = self._nodes
350
for i in self._walknodes(c, UP):
351
for j in self._walknodes(i, LEFT):
352
nodetable[nodetable[j][DOWN]][UP] = j
353
nodetable[nodetable[j][UP]][DOWN] = j
354
nodetable[nodetable[j][COLUMN]][COUNT] += 1
355
nodetable[nodetable[c][RIGHT]][LEFT] = c
356
nodetable[nodetable[c][LEFT]][RIGHT] = c
357
358
def next(self):
359
"""
360
Search for the first solution we can find, and return it.
361
362
Knuth describes the Dancing Links algorithm recursively, though
363
actually implementing it as a recursive algorithm is permissible
364
only for highly restricted problems. (for example, the original
365
author implemented this for Sudoku, and it works beautifully
366
there)
367
368
What follows is an iterative description of DLX::
369
370
stack <- [(NULL)]
371
level <- 0
372
while level >= 0:
373
cur <- stack[level]
374
if cur = NULL:
375
if R[h] = h:
376
level <- level - 1
377
yield solution
378
else:
379
cover(best_column)
380
stack[level] = best_column
381
else if D[cur] != C[cur]:
382
if cur != C[cur]:
383
delete solution[level]
384
for j in L[cur], L[L[cur]], ... , while j != cur:
385
uncover(C[j])
386
cur <- D[cur]
387
solution[level] <- cur
388
stack[level] <- cur
389
for j in R[cur], R[R[cur]], ... , while j != cur:
390
cover(C[j])
391
level <- level + 1
392
stack[level] <- (NULL)
393
else:
394
if C[cur] != cur:
395
delete solution[level]
396
for j in L[cur], L[L[cur]], ... , while j != cur:
397
uncover(C[j])
398
uncover(cur)
399
level <- level - 1
400
401
TESTS::
402
403
sage: from sage.combinat.dlx import *
404
sage: M = DLXMatrix([[1,[1,2]],[2,[2,3]],[3,[1,3]]])
405
sage: while 1:
406
... try:
407
... C = M.next()
408
... except StopIteration:
409
... print "StopIteration"
410
... break
411
... print C
412
StopIteration
413
sage: M = DLXMatrix([[1,[1,2]],[2,[2,3]],[3,[3]]])
414
sage: for C in M:
415
... print C
416
[1, 3]
417
sage: M = DLXMatrix([[1,[1]],[2,[2,3]],[3,[2]],[4,[3]]])
418
sage: for C in M:
419
... print C
420
[1, 2]
421
[1, 3, 4]
422
"""
423
nodetable = self._nodes # optimization: local variables are faster
424
425
while self._level >= 0:
426
c,r = self._stack[self._level]
427
if c is None:
428
if nodetable[ROOTNODE][RIGHT] == ROOTNODE:
429
self._level -= 1
430
return self._cursolution
431
else:
432
c = nodetable[ROOTNODE][RIGHT]
433
maxcount = nodetable[nodetable[ROOTNODE][RIGHT]][COUNT]
434
for j in self._walknodes(ROOTNODE, RIGHT):
435
if nodetable[j][COUNT] < maxcount:
436
c = j
437
maxcount = nodetable[j][COUNT]
438
self._covercolumn(c)
439
self._stack[self._level] = (c,c)
440
elif nodetable[r][DOWN] != c:
441
if c != r:
442
self._cursolution = self._cursolution[:-1]
443
for j in self._walknodes(r, LEFT):
444
self._uncovercolumn(nodetable[j][COLUMN])
445
r = nodetable[r][DOWN]
446
self._cursolution += [nodetable[r][INDEX]]
447
for j in self._walknodes(r, RIGHT):
448
self._covercolumn(nodetable[j][COLUMN])
449
self._stack[self._level] = (c,r)
450
self._level += 1
451
if len(self._stack) == self._level:
452
self._stack.append((None,None))
453
else:
454
self._stack[self._level] = (None,None)
455
else:
456
if c != r:
457
self._cursolution = self._cursolution[:-1]
458
for j in self._walknodes(r, LEFT):
459
self._uncovercolumn(nodetable[j][COLUMN])
460
self._uncovercolumn(c)
461
self._level -= 1
462
463
raise StopIteration
464
465
466
467
def AllExactCovers(M):
468
"""
469
Utilizes A. Ajanki's DLXMatrix class to solve the exact cover
470
problem on the matrix M (treated as a dense binary matrix).
471
472
EXAMPLES::
473
474
sage: M = Matrix([[1,1,0],[1,0,1],[0,1,1]]) #no exact covers
475
sage: for cover in AllExactCovers(M):
476
... print cover
477
sage: M = Matrix([[1,1,0],[1,0,1],[0,0,1],[0,1,0]]) #two exact covers
478
sage: for cover in AllExactCovers(M):
479
... print cover
480
[(1, 1, 0), (0, 0, 1)]
481
[(1, 0, 1), (0, 1, 0)]
482
"""
483
ones = []
484
r = 1 #damn 1-indexing
485
for R in M.rows():
486
row = []
487
for i in range(len(R)):
488
if R[i]:
489
row.append(i+1) #damn 1-indexing
490
ones.append([r,row])
491
r+=1
492
for s in DLXMatrix(ones):
493
yield [M.row(i-1) for i in s] #damn 1-indexing
494
495
def OneExactCover(M):
496
"""
497
Utilizes A. Ajanki's DLXMatrix class to solve the exact cover
498
problem on the matrix M (treated as a dense binary matrix).
499
500
EXAMPLES::
501
502
sage: M = Matrix([[1,1,0],[1,0,1],[0,1,1]]) #no exact covers
503
sage: print OneExactCover(M)
504
None
505
sage: M = Matrix([[1,1,0],[1,0,1],[0,0,1],[0,1,0]]) #two exact covers
506
sage: print OneExactCover(M)
507
[(1, 1, 0), (0, 0, 1)]
508
"""
509
510
for s in AllExactCovers(M):
511
return s
512
513