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

 

 

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