🔙 Back to Top

Mon Oct 12 01:20:08 JST 2015

N-Queens Problem

Scala ぎįˇ´įŋ’としãĻ、ãƒĒ゚トぎãĒã„æ–ščĄ¨č¨˜ãŽįˇ´įŋ’としãĻ、 10.1 N クã‚Ŗãƒŧãƒŗå•éĄŒ (The N-Queens Problem) をやãŖãĻた. 大部分はそこãĢ書いãĻあるとおりだけお、č‡Ē分ぎč§Ŗį­”としãĻ

import scala.math._
import scala.collection.mutable.{Queue, Set}
import scala.collection.immutable._
import scala.util.Random
 
object Main {

  def queens(n: Int): List[List[Int]] = {
    def isSafe(c: Int, qs: List[Int], d: Int): Boolean = {
      qs match {
        case List() => true
        case q::qs if c+d == q || c-d == q || c == q => false
        case q::qs => isSafe(c, qs, d+1)
      }
    }
    def sub(k: Int): List[List[Int]] = {
      if (k == 0) return List(List())
      for { qs <- sub(k-1)
          c <- Range(0, n)
        if isSafe(c, qs, 1) }
        yield c::qs
    }
    sub(n)
  }

  def show(n: Int, qs: List[Int]) {
    for (i <- Range(0, n)) {
      for (j <- Range(0, n)) {
        print('.')
        if (qs(i) == j) print('q')
        else print('_')
      }
      println("")
    }
  }

  def main(args: Array[String]) {
    for (n <- Range(4, 16)) {
      val ans = queens(n)
      println(n, ans.length)
    }
    /*
    for (ls <- ans) {
      for (x <- ls) print(x + " ")
      println("")
      show(n, ls)
    }
    */
  }
}

æ¨Ąį¯„č§Ŗį­”だと思う. å¤å…¸å•éĄŒã ã—. 計įŽ—量ぎ上限をæ¸ŦãŖãĻãŋることãĢした.

é•ˇã• n ぎãƒĒ゚トぎæœĢ尞からäŊœãŖãĻいくことãĢ成る. æœĢ尞ぎčĻį´ ã¯ n 通りある (åˆļį´„がãĒいぎで 0..(n-1) ぎäŊ•ãŒããĻã‚‚č‰¯ã„). æœĢ尞から2つį›ŽãŽčĻį´ ãĢついãĻは、 æœĢ尞ぎčĻį´  n 通りそれぞれãĢついãĻ、 やはり 0..(n-1) ぎ n 通りを isSafe ãĢよãŖãĻčŠĻしãĻã€å•éĄŒãĒいもぎをæŦĄãĢ通す. isSafe ã¯é•ˇã•ãĢæ¯”äž‹ã™ã‚‹č¨ˆįŽ—量だから、ここでは 1.

こぎようãĢしãĻã€č¨ˆįŽ—量ぎ上限としãĻは、

\(n \times n * 1 \times n * 2 \times \ldots \times n * (n-1) = n^n * (n-1)!\)

ãŖãĻãĒå…ˇåˆ. 原際ãĢは、isSafe ぎ部分で枝į‹ŠãŖãĻčĄŒãã“ã¨ãĢãĒるぎで、もãŖと小さい. 原際、最įĩ‚įš„ãĢ垗られるį­”えぎ数はもãŖとずãŖと小さい.

上ぎå‡ē力

(4,2)
(5,10)
(6,4)
(7,40)
(8,92)
(9,352)
(10,724)
(11,2680)
(12,14200)
(13,73712)

とはいえ n=13 あたりからきつくãĒるぎで、 遊åŊ“ãĒäŊ“感でいうと O(n!) かãĒãŖãĻ思ãŖãĄã‚ƒã†.

Fast N-Queens

ググãŖたらすぐãĢčĻ‹ã¤ã‹ãŖた.

  1. A polynomial time algorithm for the N-Queens problem
  2. IEEE Xplore Abstract - Fast search algorithms for the n-queens problem

2つめはåŽļからã‚ĸクã‚ģ゚できãĒかãŖたんで、 1つめをčĒ­ã‚“でãŋた. æĩã—čĒ­ãŋでčĒ­ã‚“だけお、 čĻã™ã‚‹ãĢ Figure 1 ということらしい.

ただし、

という違いがある.

Figure 1 ぎ原čŖ…

į­”えはįĸēかãĢおうせ [0 .. n-1] ぎįŊŽæ›ã§ã‚ã‚‹. ã‚ĸãƒĢゴãƒĒã‚ēムはį°Ąå˜ãĢ

愚į›´ãĢ原čŖ…するとäģĨ下通り. ただし、į„Ąé™ãƒĢãƒŧプ部分は1000回ぎ上限をつけた. List[Int] は Array[Int] ãĢした.

import scala.math._
import scala.collection.mutable.{Queue, Set}
import scala.collection.immutable._
import scala.util.Random
 
object Main {

  def queens(n: Int, uplimit: Int = 1000): Option[Array[Int]] = {

    def collision(qs: Array[Int]): Int = { // 衝įĒãŽæ•°ã‚’数える
      var c = 0
      for (i <- Range(0, n-1)) {
        for (j <- Range(i+1, n)) {
          val d = j - i
          if (qs(i) == qs(j) || qs(i) == qs(j) - d || qs(i) == qs(j) + d) c += 1
        }
      }
      c
    }

    var tryNumber = 0
    for (t <- Range(0, uplimit)) {
      tryNumber = t + 1
      val qs = Random shuffle Range(0, n) toArray;
      var c = collision(qs)
      for (i <- Range(0, n-1)) {
        val tmp = qs(i)
        for (j <- Range(i+1, n)) {
          qs(i) = qs(j)
          qs(j) = tmp
          val c2 = collision(qs)
          if (c2 >= c) { // when swap doesn't reduce
            qs(j) = qs(i)
            qs(i) = tmp
          } else {
            c = c2
          }
        }
      }
      if (c == 0) {
        println("čŠĻčĄŒå›žæ•°: " + tryNumber)
        return Some(qs)
      }
    }
    return None
  }

  def show(n: Int, qs: Array[Int]) {
    for (i <- Range(0, n)) {
      for (j <- Range(0, n)) {
        print('.')
        if (qs(i) == j) print('q')
        else print('_')
      }
      println("")
    }
  }

  def main(args: Array[String]) {
    val n = 10;
    val ans = queens(n)
    ans match {
      case Some(qs) => show(n, qs)
      case None => println("no answers")
    }
  }
}

ã‚ŗãƒŧドはかãĒã‚Šé•ˇããĒãŖた.

čŠĻčĄŒå›žæ•°: 257
._._._._._.q._._._._
._._._.q._._._._._._
._.q._._._._._._._._
._._._._._._._.q._._
._._.q._._._._._._._
._._._._._._.q._._._
._._._._._._._._._.q
.q._._._._._._._._._
._._._._._._._._.q._
._._._._.q._._._._._

įĸēかãĢ動いãĻるし、čŠĻčĄŒå›žæ•°ã¯ãã“ãžã§å¤§ãããĒいようãĢも思える. 一回ぎæ–ŊčĄŒãŽč¨ˆįŽ—量は \(O(n^4)\) だけおãĒ. \(n=12\) くらいから、1000回ぎãƒĢãƒŧプではæ€ĒしくãĒãŖãĻきた.

Figure 2

一つぎ、ナãƒŗダムãĢį”Ÿæˆã—たįŊŽæ›ãĢついãĻ、 swap を一åēĻã§ã‚‚čĄŒãŖたもぎを、もう少しįļšã‘ãĻčŠĻすというヒãƒĨãƒŧãƒĒ゚テã‚Ŗク゚を導å…Ĩする.

import scala.math._
import scala.collection.mutable.{Queue, Set}
import scala.collection.immutable._
import scala.util.Random
 
object Main {

  def queens(n: Int, uplimit: Int = 1000): Option[Array[Int]] = {

    def collision(qs: Array[Int]): Int = { // 衝įĒãŽæ•°ã‚’数える
      var c = 0
      for (i <- Range(0, n-1)) {
        for (j <- Range(i+1, n)) {
          val d = j - i
          if (qs(i) == qs(j) || qs(i) == qs(j) - d || qs(i) == qs(j) + d) c += 1
        }
      }
      c
    }

    var tryNumber = 0
    for (t <- Range(0, uplimit)) {
      val qs = Random shuffle Range(0, n) toArray;
      var c = collision(qs)
      var grad = true
      while (grad) {
        tryNumber += 1
        var performed = 0
        for (i <- Range(0, n-1)) {
          val tmp = qs(i)
          for (j <- Range(i+1, n)) {
            qs(i) = qs(j)
            qs(j) = tmp
            val c2 = collision(qs)
            if (c2 >= c) { // when swap doesn't reduce
              qs(j) = qs(i)
              qs(i) = tmp
            } else {
              c = c2
              performed += 1
            }
          }
        }
        if (performed == 0) grad = false
        if (c == 0) {
          println(tryNumber)
          return Some(qs)
        }
      }
    }
    return None
  }

  def show(n: Int, qs: Array[Int]) {
    for (i <- Range(0, n)) {
      for (j <- Range(0, n)) {
        print('.')
        if (qs(i) == j) print('q')
        else print('_')
      }
      println("")
    }
  }

  def main(args: Array[String]) {
    val n = 12;
    val ans = queens(n, 1000000)
    if (ans == None) println("None")
  }
}

\(n=12\) でぎčŠĻčĄŒå›žæ•°

% for i in `seq 10`; do scala test.scala; done
3955
1754
51
1140
1155
88
2479
307
370
1471

čĢ–æ–‡ãŽčĄ¨ã ã¨ã‚‚ãŖと少ãĒくやãŖãĻるãŊいけお. äŊ•ãŒé•ãŖたかãĒ.