N皇后不互吃问题的scala实现

基本介绍:

 

  1. 国际象棋是8*8的棋盘,每个位置可以放置一个棋子。国际象棋中的皇后棋子,可以向横、向竖、向斜行走,并吃掉路线上的其他棋子
  2. N皇后不互吃问题,为:在N * N 的棋盘上,放置N个皇后棋子;其中任意两个皇后彼此不会互吃。找出所有可能的位置集合


思路:

因为皇后能横竖斜吃子的设定,所以存在一个基本条件:要在N * N的棋盘上放置N个皇后不互吃,那么每一行有且只有一个皇后。

所以我们的思路是:

  • 在每一行放一个皇后的基本条件下,列举出N * N上放皇后的所有可能的情况。这里需要注意是所有可能情况,就是说:先不考虑互吃,求所有的每行只放一个皇后的排列集合。例如:N个皇后放在同一列的情况也会被列举出来
  • 随后从上面的排列集合中,去除掉所有的:此位置排列方案中,至少存在两个皇后会互吃的情况。则剩下的方案集合,就是两两都不会互吃的最终结果集合


简化并实现:

  • 上面思路中:先列出所有排列情况、再做筛选的方式,用代码实现起来并不容易,判断也比较复杂
  • 如果我们使用递归思想,将剔除互吃情况提前在组合子集合的递归中,则可以简化代码,比较轻松的实现。具体过程如下:
  1. 对于递归首项(初始数据):当我们在N * N的棋盘上,只在第一行放一个皇后。那这个皇后放在第一行的哪一列都可以。所以首项总共有N种放法,并且没有冲突(没有互吃现象)
  2. 对于第二行:本步骤实现目的--基于递归首项(1中求到的N种放法),在每种放法的第二行再放一个皇后棋子。这一步需要实际考察此放法中,第一行皇后的位置。对于能被已存在的第一行皇后吃到的位置,都不可以放第二个皇后。所以对于第一行求到的每个放法、后续放置第二个皇后时,都有N-2(第一个皇后在两边的端点)或N-3(第一个皇后不在端点)种方法。最终放前两个皇后的总集合,共有 N*(N-3)< X_second < N * (N - 2) 种放法,保证这两个皇后不会互吃
  3. 对于第三行,放置第三个皇后时,也需要判断与前两个皇后的互吃问题。但是我们会基于前面的结果,即:只在第二步、总共有 X_second 的方法内进行第三个皇后的放置判断。因为只有保证前两个皇后不互吃,才能保证再加第三个皇后之后,彼此两两都不互吃。因此通过这种思想,成功将放置前两个皇后的 N * N的可能性缩小为 X_second,并且保证X_second中,都是不可互吃的,简化了判断。
  4. 对于第四行、第五行、···、第N行,思路与第二、三个皇后放置思想也一样,都是在前一行已实现的两两不互吃可能结果的基础上,再分析当前行放置皇后的问题。
  5. 但如果基于之前的某种放法,进行当前行的放置分析发现:当前行的N个位置中,任意位置放置一个皇后,都会出现互吃问题,则说明这种放法已经没有实现两两不互吃的可能性了,分析到了尽头。则可以放弃此放法的后续分析(不进行后续递归),转而分析下一条放法。
  6. 随着行数分析的深入,放置皇后越来越多,每种放法后续:当前行可放皇后的位置集合,会越来越少,甚至出现5的情况,任何位置都无法再放皇后,舍弃当前放法的情况。因此,放法数量可能会减少
  7. 上述2 -- 6 的过程,抽象为递归思想,就是:假设之前已判断出 前k -1 (k -1 < N) 行放置k - 1个皇后两两不互吃的结果,共有 X_k-1 种可能性,则基于这X_k-1 种可能性,逐个分析 在第 k 行任意位置放置第k个皇后,并保证两两不互吃的可能性,把所有的情况分析完毕后,flatten合并整理,并返回这 X_k 种的可能性,供下次递归调用。当k = N 时,最终 X_n 种可能性,就是N皇后问题的最终解的集合

 

Scala递归实现

使用scala实现递归,采用了倒序调用递归的方式,进一步简化:

  1. 定义递归方法为def findSafePossibilityList(row: Int),参数row为要处理的行数。此方法业务意义为: 传入行数row,返回row个皇后在前row行内放置并且两两不相吃的所有放法集合 List[List[(Int, Int)]] 。
  2. 最里面的元组(Int,Int)为每个皇后的横纵座标。
  3. 所以List[(Int, Int)]表示的是一组皇后座标的List队列,此List表示:[row个皇后]的安全放置方法实现,其中的任意两个皇后都不互吃。因此一条List[(Int, Int)]数据,就是某一种的安全放置方法的具体实现
  4. List[List[(Int, Int)]] 则是row个皇后安全放置方法的所有可能实现的集合List
  5. 递归调用从第k = N项开始,采用倒序排列。可以看到代码内直接调用了findSafePossibilityList(row -1),即代表:取到了(row -1)个皇后安全放置方法的所有可能实现的List,并用此来处理第row行数据,这样就起到了层层调用,递归的思想。这里采用倒序排列,可以只有一个入参,表示第row行,最终递归到初始项1或者0,大大简化了方法实现和重复入参,是scala递归的常用方式。
  6. 递归需要回归到首项,之前分析到首项是第一行的N个位置,即N种方法。数据上即是N个List的集合List,具体为List(List((1,1)), List((1,2)), List((1,3)), ..., List((1, N))),但这个结果不好初始化。我们进一步分析可以找到其中的共性:每一个元素都是一个:只含有一个元组元素的List。而这个数据结构能套用我们之前总结的递归算法findSafePossibilityList(row),可以看到for循环 yield 返回的数据为(row, column) :: preSafePossibility,对于row =1 时,前面的元组即是(1,1), (1,2), ..., (1, N)。也就是说,如果让preSafePossibility为Nil(findSafePossibilityList(0) = Nil),就可拼出初始化的List。 所以我们初始化扩展到虚拟的row = 0, 返回Nil,即模式匹配中 case 0 => List(List()) 

总代码如下:

/** N皇后不互吃问题
  * author: leisu
  * date 2018/8/18 17:20
  */
class QueenQuestion {

  final val N = 8 //定义棋盘宽度为8 * 8 可以扩展为任意数

  def findSafePossibilityList(row: Int): List[List[(Int, Int)]] = {

    /**
      * 判断是否与已存在的皇后互吃
      * @param newAxis     新皇后位置
      * @param possibility 已存在的皇后位置List
      * @return
      */
    def isEatEachOther(newAxis: (Int, Int), possibility: List[(Int, Int)]): Boolean = {
      possibility.exists((oldAxis: (Int, Int)) => newAxis._1 == oldAxis._1 || //横相吃
        newAxis._2 == oldAxis._2 || //竖相吃
        (newAxis._1 - oldAxis._1).abs == (newAxis._2 - oldAxis._2).abs) //斜相吃
    }

    val possibilityList: List[List[(Int, Int)]] = row match {
      case 0 => List(List()) //起始项为List(),也是递归终结点
      case _ => for {
        //preSafePossibility:前row - 1行的每种皇后安全放置的可能性
        preSafePossibility <- findSafePossibilityList(row - 1) //使用递归
        column <- 1 to N  //通过循环,创建第row行内每个位置(row,column)与之前row -1 个皇后都不出现互吃情况
        if (!isEatEachOther((row, column), preSafePossibility))
      } yield (row, column) :: preSafePossibility //返回新的可能性的List
    }
    possibilityList
  }
}

object QueenQuestion extends App {
  val queenQuestion = new QueenQuestion
  val answer = queenQuestion.findSafePossibilityList(8)
  answer.foreach(println)
  println(s"size == ${answer.size}")
}

最终执行结果如下:

List((8,4), (7,2), (6,7), (5,3), (4,6), (3,8), (2,5), (1,1))
List((8,5), (7,2), (6,4), (5,7), (4,3), (3,8), (2,6), (1,1))
List((8,3), (7,5), (6,2), (5,8), (4,6), (3,4), (2,7), (1,1))
List((8,3), (7,6), (6,4), (5,2), (4,8), (3,5), (2,7), (1,1))
List((8,5), (7,7), (6,1), (5,3), (4,8), (3,6), (2,4), (1,2))
List((8,4), (7,6), (6,8), (5,3), (4,1), (3,7), (2,5), (1,2))
List((8,3), (7,6), (6,8), (5,1), (4,4), (3,7), (2,5), (1,2))
List((8,5), (7,3), (6,8), (5,4), (4,7), (3,1), (2,6), (1,2))
List((8,5), (7,7), (6,4), (5,1), (4,3), (3,8), (2,6), (1,2))
List((8,4), (7,1), (6,5), (5,8), (4,6), (3,3), (2,7), (1,2))
List((8,3), (7,6), (6,4), (5,1), (4,8), (3,5), (2,7), (1,2))
List((8,4), (7,7), (6,5), (5,3), (4,1), (3,6), (2,8), (1,2))
List((8,6), (7,4), (6,2), (5,8), (4,5), (3,7), (2,1), (1,3))
List((8,6), (7,4), (6,7), (5,1), (4,8), (3,2), (2,5), (1,3))
List((8,1), (7,7), (6,4), (5,6), (4,8), (3,2), (2,5), (1,3))
List((8,6), (7,8), (6,2), (5,4), (4,1), (3,7), (2,5), (1,3))
List((8,6), (7,2), (6,7), (5,1), (4,4), (3,8), (2,5), (1,3))
List((8,4), (7,7), (6,1), (5,8), (4,5), (3,2), (2,6), (1,3))
List((8,5), (7,8), (6,4), (5,1), (4,7), (3,2), (2,6), (1,3))
List((8,4), (7,8), (6,1), (5,5), (4,7), (3,2), (2,6), (1,3))
List((8,2), (7,7), (6,5), (5,8), (4,1), (3,4), (2,6), (1,3))
List((8,1), (7,7), (6,5), (5,8), (4,2), (3,4), (2,6), (1,3))
List((8,2), (7,5), (6,7), (5,4), (4,1), (3,8), (2,6), (1,3))
List((8,4), (7,2), (6,7), (5,5), (4,1), (3,8), (2,6), (1,3))
List((8,5), (7,7), (6,1), (5,4), (4,2), (3,8), (2,6), (1,3))
List((8,6), (7,4), (6,1), (5,5), (4,8), (3,2), (2,7), (1,3))
List((8,5), (7,1), (6,4), (5,6), (4,8), (3,2), (2,7), (1,3))
List((8,5), (7,2), (6,6), (5,1), (4,7), (3,4), (2,8), (1,3))
List((8,6), (7,3), (6,7), (5,2), (4,8), (3,5), (2,1), (1,4))
List((8,2), (7,7), (6,3), (5,6), (4,8), (3,5), (2,1), (1,4))
List((8,7), (7,3), (6,1), (5,6), (4,8), (3,5), (2,2), (1,4))
List((8,5), (7,1), (6,8), (5,6), (4,3), (3,7), (2,2), (1,4))
List((8,1), (7,5), (6,8), (5,6), (4,3), (3,7), (2,2), (1,4))
List((8,3), (7,6), (6,8), (5,1), (4,5), (3,7), (2,2), (1,4))
List((8,6), (7,3), (6,1), (5,7), (4,5), (3,8), (2,2), (1,4))
List((8,7), (7,5), (6,3), (5,1), (4,6), (3,8), (2,2), (1,4))
List((8,7), (7,3), (6,8), (5,2), (4,5), (3,1), (2,6), (1,4))
List((8,5), (7,3), (6,1), (5,7), (4,2), (3,8), (2,6), (1,4))
List((8,2), (7,5), (6,7), (5,1), (4,3), (3,8), (2,6), (1,4))
List((8,3), (7,6), (6,2), (5,5), (4,8), (3,1), (2,7), (1,4))
List((8,6), (7,1), (6,5), (5,2), (4,8), (3,3), (2,7), (1,4))
List((8,8), (7,3), (6,1), (5,6), (4,2), (3,5), (2,7), (1,4))
List((8,2), (7,8), (6,6), (5,1), (4,3), (3,5), (2,7), (1,4))
List((8,5), (7,7), (6,2), (5,6), (4,3), (3,1), (2,8), (1,4))
List((8,3), (7,6), (6,2), (5,7), (4,5), (3,1), (2,8), (1,4))
List((8,6), (7,2), (6,7), (5,1), (4,3), (3,5), (2,8), (1,4))
List((8,3), (7,7), (6,2), (5,8), (4,6), (3,4), (2,1), (1,5))
List((8,6), (7,3), (6,7), (5,2), (4,4), (3,8), (2,1), (1,5))
List((8,4), (7,2), (6,7), (5,3), (4,6), (3,8), (2,1), (1,5))
List((8,7), (7,1), (6,3), (5,8), (4,6), (3,4), (2,2), (1,5))
List((8,1), (7,6), (6,8), (5,3), (4,7), (3,4), (2,2), (1,5))
List((8,3), (7,8), (6,4), (5,7), (4,1), (3,6), (2,2), (1,5))
List((8,6), (7,3), (6,7), (5,4), (4,1), (3,8), (2,2), (1,5))
List((8,7), (7,4), (6,2), (5,8), (4,6), (3,1), (2,3), (1,5))
List((8,4), (7,6), (6,8), (5,2), (4,7), (3,1), (2,3), (1,5))
List((8,2), (7,6), (6,1), (5,7), (4,4), (3,8), (2,3), (1,5))
List((8,2), (7,4), (6,6), (5,8), (4,3), (3,1), (2,7), (1,5))
List((8,3), (7,6), (6,8), (5,2), (4,4), (3,1), (2,7), (1,5))
List((8,6), (7,3), (6,1), (5,8), (4,4), (3,2), (2,7), (1,5))
List((8,8), (7,4), (6,1), (5,3), (4,6), (3,2), (2,7), (1,5))
List((8,4), (7,8), (6,1), (5,3), (4,6), (3,2), (2,7), (1,5))
List((8,2), (7,6), (6,8), (5,3), (4,1), (3,4), (2,7), (1,5))
List((8,7), (7,2), (6,6), (5,3), (4,1), (3,4), (2,8), (1,5))
List((8,3), (7,6), (6,2), (5,7), (4,1), (3,4), (2,8), (1,5))
List((8,4), (7,7), (6,3), (5,8), (4,2), (3,5), (2,1), (1,6))
List((8,4), (7,8), (6,5), (5,3), (4,1), (3,7), (2,2), (1,6))
List((8,3), (7,5), (6,8), (5,4), (4,1), (3,7), (2,2), (1,6))
List((8,4), (7,2), (6,8), (5,5), (4,7), (3,1), (2,3), (1,6))
List((8,5), (7,7), (6,2), (5,4), (4,8), (3,1), (2,3), (1,6))
List((8,7), (7,4), (6,2), (5,5), (4,8), (3,1), (2,3), (1,6))
List((8,8), (7,2), (6,4), (5,1), (4,7), (3,5), (2,3), (1,6))
List((8,7), (7,2), (6,4), (5,1), (4,8), (3,5), (2,3), (1,6))
List((8,5), (7,1), (6,8), (5,4), (4,2), (3,7), (2,3), (1,6))
List((8,4), (7,1), (6,5), (5,8), (4,2), (3,7), (2,3), (1,6))
List((8,5), (7,2), (6,8), (5,1), (4,4), (3,7), (2,3), (1,6))
List((8,3), (7,7), (6,2), (5,8), (4,5), (3,1), (2,4), (1,6))
List((8,3), (7,1), (6,7), (5,5), (4,8), (3,2), (2,4), (1,6))
List((8,8), (7,2), (6,5), (5,3), (4,1), (3,7), (2,4), (1,6))
List((8,3), (7,5), (6,2), (5,8), (4,1), (3,7), (2,4), (1,6))
List((8,3), (7,5), (6,7), (5,1), (4,4), (3,2), (2,8), (1,6))
List((8,5), (7,2), (6,4), (5,6), (4,8), (3,3), (2,1), (1,7))
List((8,6), (7,3), (6,5), (5,8), (4,1), (3,4), (2,2), (1,7))
List((8,5), (7,8), (6,4), (5,1), (4,3), (3,6), (2,2), (1,7))
List((8,4), (7,2), (6,5), (5,8), (4,6), (3,1), (2,3), (1,7))
List((8,4), (7,6), (6,1), (5,5), (4,2), (3,8), (2,3), (1,7))
List((8,6), (7,3), (6,1), (5,8), (4,5), (3,2), (2,4), (1,7))
List((8,5), (7,3), (6,1), (5,6), (4,8), (3,2), (2,4), (1,7))
List((8,4), (7,2), (6,8), (5,6), (4,1), (3,3), (2,5), (1,7))
List((8,6), (7,3), (6,5), (5,7), (4,1), (3,4), (2,2), (1,8))
List((8,6), (7,4), (6,7), (5,1), (4,3), (3,5), (2,2), (1,8))
List((8,4), (7,7), (6,5), (5,2), (4,6), (3,1), (2,3), (1,8))
List((8,5), (7,7), (6,2), (5,6), (4,3), (3,1), (2,4), (1,8))
size == 92

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章