V ← {rcols←1..} ##.X M ⍝ Exact cover: Knuth's Algorithm X. Given a set of requirements and set of actions, each of which satisfies zero or more of the requirements, choose a subset of the actions such that each require- ment is satisfied exactly once. The problem can be represented as a boolean matrix, where each row is an action with 1s at the column positions of the requirements it satisfies. 1 2 3 4 ┌───┬───┬───┬───┐ Action A satisfies requirements 2 and 4 A │ 0 │ 1 │ 0 │ 1 │ .. B .. .. .. 1 .. 2 ├───┼───┼───┼───┤ .. C .. .. .. 1 .. 3 B │ 1 │ 1 │ 0 │ 0 │ ├───┼───┼───┼───┤ In this case a solution is 1 0 1, as A and C C │ 1 │ 0 │ 1 │ 0 │ together satisfy all requirements exactly once. └───┴───┴───┴───┘ Boolean result vector [V] identifies a subset of the rows of boolean argument matrix [M] with exactly one 1 in each column: 1∧.=+⌿V⌿M. The selected rows are said to form an "exact cover" of the argument matrix. If no exact cover is possible, _scalar_ 0 is returned. This means that a test for failure might be V≡0 or 0=⍴⍴V. Many problems, including →sudoku←, →queens← and pentomino-tiling can be reduced to the exact cover problem. See →sudokuX←, →queensX← and ##.scripts.pentominos For some problems, such as →queensX←, some of the columns are "optional" and so need not be covered. Optional boolean left argument vector [rcols], default all 1s, specifies which columns are required to be covered. Knuth's algorithm is described as being "recursive with backtracking": [1] If ⍵ has no columns (c=0) the algorithm succeeds with a result of r⍴0 where r c←⍴⍵. [2] Otherwise, choose any non-empty row r as a potential cover. [3] Identify a sub-matrix of ⍵: columns not covered by r and rows not covered by r-covered columns (because each column must be covered only once). [4] If the sub-matrix has an exact cover, merge this with r as result. [5] Otherwise, if there are un-tried rows besides r, try these. [6] Otherwise, return failure. Knuth suggests a heuristic: given that each column must be covered once and only once, we can choose rows r by selecting a column with a minimum number of 1s and examining only those rows selected by 1s in this column. As an example: 1 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┐ A │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ B │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ ├───┼───┼───┼───┼───┼───┼───┤ C │ 0 │ 0 │ 0 │ 1 │ 1 │ 0 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ D │ 0 │ 0 │ 1 │ 0 │ 1 │ 1 │ 0 │ ├───┼───┼───┼───┼───┼───┼───┤ E │ 0 │ 1 │ 1 │ 0 │ 0 │ 1 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ F │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │ └───┴───┴───┴───┴───┴───┴───┘ Select any column; Knuth's heuristic recommends choosing one with a minimal num- ber of marked rows (1s). Let's select the leftmost column [1]: [1] 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┐ A │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ B │ 1 │ 0 │ 0 │ 1 │ 0 │ 0 │ 0 │ ├───┼───┼───┼───┼───┼───┼───┤ C │ 0 │ 0 │ 0 │ 1 │ 1 │ 0 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ D │ 0 │ 0 │ 1 │ 0 │ 1 │ 1 │ 0 │ ├───┼───┼───┼───┼───┼───┼───┤ E │ 0 │ 1 │ 1 │ 0 │ 0 │ 1 │ 1 │ ├───┼───┼───┼───┼───┼───┼───┤ F │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │ └───┴───┴───┴───┴───┴───┴───┘ Selecting any marked row in this column identifies an "uncovered" sub-matrix. First we mark columns selected by this row and then we mark rows selected by any of the marked columns. Arbitrarily choosing the second row and marking the covered rows and columns: ┌───────────┬───────────────── Columns marked by second row. │ │ [1] 2 3 4 5 6 7 ┌───┬───┬───┬───┬───┬───┬───┐ A │[1]│ 0 │ 0 │[1]│ 0 │ 0 │ 1 │─┬─ Rows marked by marked columns. ├───┼───┼───┼───┼───┼───┼───┤ │ [B]│[1]│ 0 │ 0 │[1]│ 0 │ 0 │ 0 │─┤ ├───┼───┼───┼───┼───┼───┼───┤ │ C │ 0 │ 0 │ 0 │[1]│ 1 │ 0 │ 1 │─┘ ├───┼───┼───┼───┼───┼───┼───┤ D │ 0 │ 0 │ 1 │ 0 │ 1 │ 1 │ 0 │─┬─ Unmarked rows ├───┼───┼───┼───┼───┼───┼───┤ │ E │ 0 │ 1 │ 1 │ 0 │ 0 │ 1 │ 1 │─┤ ├───┼───┼───┼───┼───┼───┼───┤ │ F │ 0 │ 1 │ 0 │ 0 │ 0 │ 0 │ 1 │─┘ └───┴───┴───┴───┴───┴───┴───┘ │ │ │ │ │ └───┴───────┴───┴───┴───── Unmarked columns The algorithm is applied recursively to the unmarked submatrix, in this case the matrix: 2 3 5 6 7 ┌───┬───┬───┬───┬───┐ D │ 0 │ 1 │ 1 │ 1 │ 0 │ ├───┼───┼───┼───┼───┤ E │ 1 │ 1 │ 0 │ 1 │ 1 │ ├───┼───┼───┼───┼───┤ F │ 1 │ 0 │ 0 │ 0 │ 1 │ └───┴───┴───┴───┴───┘ If the recursive call fails to find an exact cover, it "backtracks" and a diff- erent row is examined. If there are no untried rows, the search fails. Otherwise, if the recursive call succeeds, the resulting covering vector, ext- ended with the marked row, is returned as result. Technical note: In general, APL provides two ways to identify a subset of the rows and columns of a matrix: the first is to use indices M[I;] or M[;I] and the second employs a boolean selection vector B/M or B⌿M. The coding of X arbitrarily chooses the second (boolean selection) method. Ref: http://en.wikipedia.org/wiki/Algorithm_X Alternative ----------- Phil Last provides this alternative coding, which is good for small arrays: X←{⍵{⍉(⍵⍴2)⊤{⍵/⍳⍴⍵}1∧.=(⍉⍺)+.∧(⍵⍴2)⊤⍳2*⍵}⍬⍴⍴⍵} Example: M ⍝ Constraint matrix. 1 0 0 1 0 0 1 1 0 0 1 0 0 0 0 0 0 1 1 0 1 0 0 1 0 1 1 0 0 1 1 0 0 1 1 0 1 0 0 0 0 1 X M ⍝ Rows that form exact cover. 0 1 0 1 0 1 (X M)⌿M ⍝ selected covering rows. 1 0 0 1 0 0 0 0 0 1 0 1 1 0 0 1 0 0 0 0 1 1∧.=+⌿(X M)⌿M ⍝ condition for exact cover. 1 ⍝ See also →sudokuX← ⍝ Exact cover Sudoku solver. ⍝ See also →queensX← ⍝ Exact cover N-Queens solver. ⍝ See also ##.scripts.pentominos ⍝ Exact cover Pentomino tiling. See also: Graphs assign sudoku sudokuX queensX Back to: contents Back to: Workspaces