Exact cover problem via dancing links - Combinatorics

Skip to content
Back to top
View this page
Edit this page

# Exact cover problem via dancing links

sage.combinat.dlx.AllExactCovers(M)

  Use A. Ajanki’s DLXMatrix class to solve the exact cover problem on the matrix M (treated as a dense binary matrix).

  EXAMPLES:

  Sage

      sage: # needs sage.modules
      sage: M = Matrix([[1,1,0],[1,0,1],[0,1,1]])  # no exact covers
      sage: for cover in AllExactCovers(M):
      ....:     print(cover)
      sage: M = Matrix([[1,1,0],[1,0,1],[0,0,1],[0,1,0]]) # two exact covers
      sage: for cover in AllExactCovers(M):
      ....:     print(cover)
      [(1, 1, 0), (0, 0, 1)]
      [(1, 0, 1), (0, 1, 0)]

  Python

      >>> from sage.all import *
      >>> # needs sage.modules
      >>> M = Matrix([[Integer(1),Integer(1),Integer(0)],[Integer(1),Integer(0),Integer(1)],[Integer(0),Integer(1),Integer(1)]])  # no exact covers
      >>> for cover in AllExactCovers(M):
      ...     print(cover)
      >>> M = Matrix([[Integer(1),Integer(1),Integer(0)],[Integer(1),Integer(0),Integer(1)],[Integer(0),Integer(0),Integer(1)],[Integer(0),Integer(1),Integer(0)]]) # two exact covers
      >>> for cover in AllExactCovers(M):
      ...     print(cover)
      [(1, 1, 0), (0, 0, 1)]
      [(1, 0, 1), (0, 1, 0)]

class sage.combinat.dlx.DLXMatrix(ones, initialsolution=None)

  Bases: `object`

  Solve the Exact Cover problem by using the Dancing Links algorithm described by Knuth.

  Consider a matrix M with entries of 0 and 1, and compute a subset of the rows of this matrix which sum to the vector of all 1s.

  The dancing links algorithm works particularly well for sparse matrices, so the input is a list of lists of the form: (note the 1-index!):

      [
       [1, [i_11,i_12,...,i_1r]]
       ...
       [m,[i_m1,i_m2,...,i_ms]]
      ]

  where M[j][i_jk] = 1.

  The first example below corresponds to the matrix:

      1110
      1010
      0100
      0001

  which is exactly covered by:

      1110
      0001

  and

      1010
      0100
      0001

  EXAMPLES:

  Sage

      sage: from sage.combinat.dlx import *
      sage: ones = [[1,[1,2,3]]]
      sage: ones+= [[2,[1,3]]]
      sage: ones+= [[3,[2]]]
      sage: ones+= [[4,[4]]]
      sage: DLXM = DLXMatrix(ones,[4])
      sage: for C in DLXM:
      ....:      print(C)
      [4, 1]
      [4, 2, 3]

  Python

      >>> from sage.all import *
      >>> from sage.combinat.dlx import *
      >>> ones = [[Integer(1),[Integer(1),Integer(2),Integer(3)]]]
      >>> ones+= [[Integer(2),[Integer(1),Integer(3)]]]
      >>> ones+= [[Integer(3),[Integer(2)]]]
      >>> ones+= [[Integer(4),[Integer(4)]]]
      >>> DLXM = DLXMatrix(ones,[Integer(4)])
      >>> for C in DLXM:
      ...      print(C)
      [4, 1]
      [4, 2, 3]

  Note

  The 0 entry is reserved internally for headers in the sparse representation, so rows and columns begin their indexing with 1. Apologies for any heartache this causes. Blame the original author, or fix it yourself.

  next()

    Search for the first solution we can find, and return it.

    Knuth describes the Dancing Links algorithm recursively, though actually implementing it as a recursive algorithm is permissible only for highly restricted problems. (for example, the original author implemented this for Sudoku, and it works beautifully there)

    What follows is an iterative description of DLX:

        stack <- [(NULL)]
        level <- 0
        while level >= 0:
            cur <- stack[level]
            if cur = NULL:
                if R[h] = h:
                    level <- level - 1
                    yield solution
                else:
                    cover(best_column)
                    stack[level] = best_column
            else if D[cur] != C[cur]:
                if cur != C[cur]:
                    delete solution[level]
                    for j in L[cur], L[L[cur]], ... , while j != cur:
                        uncover(C[j])
                cur <- D[cur]
                solution[level] <- cur
                stack[level] <- cur
                for j in R[cur], R[R[cur]], ... , while j != cur:
                    cover(C[j])
                level <- level + 1
                stack[level] <- (NULL)
            else:
                if C[cur] != cur:
                    delete solution[level]
                    for j in L[cur], L[L[cur]], ... , while j != cur:
                        uncover(C[j])
                uncover(cur)
                level <- level - 1

sage.combinat.dlx.OneExactCover(M)

  Use A. Ajanki’s DLXMatrix class to solve the exact cover problem on the matrix M (treated as a dense binary matrix).

  EXAMPLES:

  Sage

      sage: M = Matrix([[1,1,0],[1,0,1],[0,1,1]])  # no exact covers                  # needs sage.modules
      sage: OneExactCover(M)                                                          # needs sage.modules

      sage: M = Matrix([[1,1,0],[1,0,1],[0,0,1],[0,1,0]])  # two exact covers         # needs sage.modules
      sage: OneExactCover(M)                                                          # needs sage.modules
      [(1, 1, 0), (0, 0, 1)]

  Python

      >>> from sage.all import *
      >>> M = Matrix([[Integer(1),Integer(1),Integer(0)],[Integer(1),Integer(0),Integer(1)],[Integer(0),Integer(1),Integer(1)]])  # no exact covers                  # needs sage.modules
      >>> OneExactCover(M)                                                          # needs sage.modules

      >>> M = Matrix([[Integer(1),Integer(1),Integer(0)],[Integer(1),Integer(0),Integer(1)],[Integer(0),Integer(0),Integer(1)],[Integer(0),Integer(1),Integer(0)]])  # two exact covers         # needs sage.modules
      >>> OneExactCover(M)                                                          # needs sage.modules
      [(1, 1, 0), (0, 0, 1)]