Swift

时间:2017-04-26 06:12:09

标签: swift swift3

我正在研究N-Queens问题并检查女王是否已经被放置在左上角和左下角上,我发现难以制定循环条件。

func isPlaceable(row: Int, column: Int) -> Bool {

    // check if one same row another queen exits
    for i in 0..<column {
        if (solution[row][i] == 1) {
            return false
        }
    }

    // check upper left diagonal
    // in C or C++ I can do
    // for (int i = row, int j = col; i >= 0 && j >= 0; i--, j--) {
    //    if (board[i][j])
    //       return false;
    //}
}

Swifty的做法是什么,即梳理这两个条件?

3 个答案:

答案 0 :(得分:1)

一种可能的解决方案是您可以使用zip(_:_:)两个序列。

func isPlaceable(row: Int, column: Int) -> Bool {

    // check if one same row another queen exits
    for i in 0..<column {
        if (solution[row][i] == 1) {
            return false
        }
    }

    // check upper left diagonal
    let seq1 = (0...row).reversed()
    let seq2 = (0...column).reversed()
    for (i,j) in zip(seq1, seq2) {
        if (board[i][j]) {
            return false
        } 
    }
    //your code
}

答案 1 :(得分:0)

var i = row
var j = col
while (i >= 0 && j >= 0) {
    if (board[i][j])
        return false;
    i -= 1
    j -= 1
}

答案 2 :(得分:0)

这种类型的过程从双重间接和准备好的矩阵中获益良多。

例如,您可以为电路板上的每个线段指定一个标识符,并检查没有两个皇后使用相同的线段。

标准棋盘上有46个线段:

8垂直   8横   30对角线(每个15个)

(我将它们编号为1到46)

当皇后正确放置时,它们将分别使用其他女王使用的4个线段(轴)。集合是检查此非交叉联合的理想结构。通过在每行/每列准备一组4轴标识符的矩阵,8个皇后的集合的简单联合将告诉我们它们是否彼此对齐。

// vertical axes (1...8)
let vAxis =    [ [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ],
                 [ 1, 2, 3, 4, 5, 6, 7, 8 ]
               ]

// horizontal axes (9...16)                
let hAxis =  [ [ 9,   9,  9,  9,  9,  9,  9,  9 ],
               [ 10, 10, 10, 10, 10, 10, 10, 10 ],
               [ 11, 11, 11, 11, 11, 11, 11, 11 ],
               [ 12, 12, 12, 12, 12, 12, 12, 12 ],
               [ 13, 13, 13, 13, 13, 13, 13, 13 ],
               [ 14, 14, 14, 14, 14, 14, 14, 14 ],
               [ 15, 15, 15, 15, 15, 15, 15, 15 ],
               [ 16, 16, 16, 16, 16, 16, 16, 16 ],
             ]

// up right axes (17...31)                
let uAxis =  [ [ 17, 18, 19, 20, 21, 22, 23, 24 ],
               [ 18, 19, 20, 21, 22, 23, 24, 25 ],
               [ 19, 20, 21, 22, 23, 24, 25, 26 ],
               [ 20, 21, 22, 23, 24, 25, 26, 27 ],
               [ 21, 22, 23, 24, 25, 26, 27, 28 ],
               [ 22, 23, 24, 25, 26, 27, 28, 29 ],
               [ 23, 24, 25, 26, 27, 28, 29, 30 ],
               [ 24, 25, 26, 27, 28, 29, 30, 31 ],
             ]

// down right axes (32...46)                
let dAxis = [ [ 39, 40, 41, 42, 43, 44, 45, 46 ],
              [ 38, 39, 40, 41, 42, 43, 44, 45 ],
              [ 37, 38, 39, 40, 41, 42, 43, 44 ],
              [ 36, 37, 38, 39, 40, 41, 42, 43 ],
              [ 35, 36, 37, 38, 39, 40, 41, 42 ],
              [ 34, 35, 36, 37, 38, 39, 40, 41 ],
              [ 33, 34, 35, 36, 37, 38, 39, 40 ],
              [ 32, 33, 34, 35, 36, 37, 38, 39 ],
            ]

// Set of axis numbers for each [row][col] of the board                  
let axes = (0..<8).map()
           { 
             row in (0..<8).map()
             { Set([ vAxis[row][$0], hAxis[row][$0], uAxis[row][$0], dAxis[row][$0] ]) }
           }

// position of each queen ( column number at each row )                  
var queenCols = [5, 3, 6, 0, 7, 1, 4, 2]

// Check if each queen in queenCols is on its own 4 axes 
// We will have 32 (8 x 4) different axes used if no queen aligns with another

let fullCover = queenCols.enumerated()
                .reduce(Set<Int>()){ $0.union(axes[$1.0][$1.1]) }
                .count == 32

此“fullCover”检查可用于所有16,777,216组合的暴力循环,或者可以对其进行细化以在优化的搜索树中执行增量检查。 (顺便说一下,蛮力解决方案只需80秒即可在MacBook Pro上进行计算)

所以,最后,你可以完全避免循环。

[EDIT]功能在强力循环中找到92个解决方案:

public func queenPositions() -> [[Int]]
{
   var result : [[Int]] = []
   let rows   : [Int]   = Array(0..<8)
   for i in 0..<16777216
   {
      var N:Int = i
      let queenCols = rows.map{ _ -> Int in let col = N % 8; N = N / 8; return col}
      let fullCover = queenCols.enumerated()
                      .reduce(Set<Int>()){ $0.union(axes[$1.0][$1.1]) }
                      .count == 32
      if fullCover { result.append(queenCols) }
   }
   return result
}

[EDIT2]在优化的树搜索中使用集合矩阵在0.03秒内生成92个解。

这是优化(和通用)功能:

public func queenPositions2(boardSize:Int = 8) -> [[Int]]
{
   let vAxis     = (0..<boardSize).map{ _ in (0..<boardSize).map{$0} }
   let hAxis     = (0..<boardSize).map{ Array(repeating:$0+boardSize, count:boardSize) }
   let uAxis     = (0..<boardSize).map{ row in (0..<boardSize).map{ 2 * boardSize + row + $0} }
   let dAxis     = (0..<boardSize).map{ row in (0..<boardSize).map{ 5 * boardSize - row + $0} }
   let axes      = (0..<boardSize).map()
                   { 
                     row in (0..<boardSize).map()
                     { Set([ vAxis[row][$0], hAxis[row][$0], uAxis[row][$0], dAxis[row][$0] ]) }
                   }

   var result    : [[Int]] = []
   var queenCols : [Int]   = Array(repeating:0,          count:boardSize)
   var colAxes             = Array(repeating:Set<Int>(), count:boardSize)
   var queenAxes           = Set<Int>()
   var row                 = 0
   while row >= 0
   {          
      if queenCols[row] < boardSize
      {
         let newAxes = axes[row][queenCols[row]]
         if newAxes.isDisjoint(with: queenAxes)
         {
            if row == boardSize - 1 
            { 
              result.append(queenCols)
              queenCols[row] = boardSize
              continue
            }
            else
            { 
              colAxes[row] = newAxes
              queenAxes.formUnion(newAxes)
              row += 1
              queenCols[row] = 0
              continue
            }
         }
      }
      else
      {
         row -= 1
         if row < 0 { break }
      }
      queenAxes.subtract(colAxes[row])
      colAxes[row] = Set<Int>()
      queenCols[row] += 1 

   }
   return result
}

考虑到10x10电路板,724解决方案的生成时间为0.11秒。

[EDIT3]一个班轮“for loop”......

您可以为给定位置的4个轴生成(行,列)坐标数组,并将其用作for循环中的数据:

func isPlaceable(row: Int, column: Int) -> Bool 
{
   var coverage  = (0..<8).map{($0,column)}  // horizontal
       coverage += (0..<8).map{(row,$0)}     // vertical
       coverage += zip((max(0,row-column)..<8),(max(0,column-row)..<8)) // diagonal down
       coverage += zip((0...min(7,row+column)).reversed(),(max(0,column+row-7)..<8)) // diagonal up

   // return !coverage.contains{solution[$0][$1] == 1}   

   for (r,c) in coverage
   { 
      if solution[r][c] == 1 { return false }
   }             
   return true

} 

每次重建整个覆盖列表都是浪费。我会为每个坐标计算一次,并将其放在行/列矩阵中,以便在isPlaceable()函数中重用。