2014-12-14 2 views
2

8 개의 퀸즈 퍼즐은 8 개의 체스 보드에 8 개의 체스 퀸즈를 놓아 두 왕비가 서로를 위협하지 않도록하는 문제입니다. 따라서 해결책은 2 명의 여왕이 동일한 행, 열 또는 대각선을 공유하지 않아야합니다. 8 개의 퀸즈 퍼즐은 n = 2 또는 n = 3을 제외한 모든 자연수 n에 대한 해가 존재하는 n × n 체스 보드에 n 개의 여왕을 배치하는보다 일반적인 n-queens 문제의 예입니다.8 개의 퀸즈 퍼즐을 풀기위한 빠른 코드는 다음과 같습니다

그러나 아무도 나를 재귀 메서드 동안 무한 루프에 대한이 문제를 해결할 수 있습니까?

ps : 놀이터에 복사하여 붙여 넣기 만하면됩니다. 감사합니다!

class ChessBoard { 

    var limit: Int 
    var queens = [Queen]() 

    init(limit: Int) { 
     self.limit = limit 
    } 

    // Check if (i,j) is a safe position for queen 
    func isSafeForQueen(atRow row: Int, col: Int) -> Bool { 

     for q in queens { 
      // not in same row 
      if q.row == row { return false } 
      // not in same column 
      if q.col == col { return false } 
      // not in same diagol line 
      if abs(q.row-row) == abs(q.col-col) { return false } 
     } 

     return true 
    } 

    // recursive method 
    func dropQueen(atRow r: Int, c: Int) { 

     // running into last row 
     if r == limit { 
      if queens.count < 8 { 
       queens.removeLast() 
       let q = queens.last! 
       dropQueen(atRow: q.row, c: q.col+1) 
      } 
      output() // if success, log out the positions 
      return 
     } 
     // running into last column of current row 
     if c == limit { 
      queens.removeLast() 
      let q = queens.last! 
      // if no position for queen at current row, then back to last row 
      dropQueen(atRow: r-1, c: q.col+1) 
     } 
     // if this postion is safe for queen, then drop the queen and try next row; if not, try next spot 
     if isSafeForQueen(atRow: r, col: c) { 
      let q = Queen(row: r, col: c) 
      queens.append(q) 
      dropQueen(atRow: r+1, c: c) 
     } else { 
      dropQueen(atRow: r, c: c+1) 
     } 
    } 

    func play() { 
     dropQueen(atRow: 0, c: 0) // game will start at(0,0) 
    } 

    func output() -> String { 
     var s = "" 
     for q in queens { 
      s += "(\(q.row),\(q.col))" 
     } 
     return s 
    } 
} 

struct Queen { 
    var row: Int 
    var col: Int 
} 

// Tesing: 
let b = ChessBoard(limit: 8) 
//b.play() 

답변

1

재귀 개념을 파악하는 데 문제가있는 것처럼 보입니다. 재귀는 을 의미하지 않습니다.은 재귀 호출이됩니다. 함수 dropQueen에서 재귀 호출은 마치 멍청한 것처럼 사용하고 있습니다.

특히이 하나

역 추적을 할 수있는 시도가 분명히이다
dropQueen(atRow: r-1, c: q.col+1) 

. 당신의 마음을 확인하십시오; 다시 추적하거나 재귀를 사용하십시오! 재귀에서 이전 의사 결정으로 돌아 오는 것은 return입니다. 필요한 경우 부울을 반환하여 호출자가 재귀 호출에서 솔루션 (return true)을 찾았는지 여부 (return false)를 알 수 있도록합니다. 프로그램이 모두 가능한 솔루션이 아닌 첫 번째 솔루션이되도록하려는 경우에는 필요하지 않습니다.

내 관심을 끌었 또 다른 전화 :

dropQueen(atRow: r, c: c+1) 

재귀 호출 과잉 여기, 그리고 혼란; 가능한 모든 열을 스캔하려면 루프를 사용하십시오. 그런데이 변경을하면 두 번째 함수 매개 변수 전체가 중복되는 것을 알 수 있습니다.

이것은 우리에게 단지 하나의 부끄러운 호출을 남겨 둡니다; 그것은 충분하다.

vacawama가 지적한 것처럼, 두 번째 매개 변수 c: c은 잘못된 것처럼 보입니다. 행 (r+1)을 전진 할 때 왜 보드에 마지막으로 놓인 여왕 이 남았는지으로 배제하고 싶습니까?

재귀를 수행하려면 재귀 함수가 수행해야하는 작업에 대해 생각하십시오. 일반적으로 자리에 (8- r) 더 많은 왕비가 여왕이 차지하고 있습니다. 행 단위로 작업하고 있으므로 접근 방식이 인 경우은 현재 행 번호입니다. 행 행의 문제에 대한 해결책을 행 +1에 대한 해결책으로 표현할 수있는 방법을 생각해보십시오. 마지막 행에 대해 특별한 예외를 만든다. r이 한도와 같으면 솔루션은 사소합니다 (제로 퀸을 배치해야합니다). 너 끝났어.

아, 이름을 바꿔주세요. dropQueen; 그 이름은 당신이 잘못된 생각으로이 일을하고 있다는 분명한 징후입니다. 지금까지보다 적절한 것을 제안 할 수 있어야합니다.


편집 : 당신은이 작품 제작에 상당한 노력을 보여, 내가 당신과 함께 내 솔루션을 공유합니다. 그것은 원래의 코드를 사용하여 (귀하의 질문에 표시),하지만 함수는 dropQueen 완전히 (그리고 하나의 매개 변수를 덜) 재작 성. 그것이 얼마나 짧고 간단한 지 주목하십시오. 이것은 재귀에 일반적입니다. 보너스로

func dropQueen(atRow r: Int) { 
    if queens.count < limit { 
     for col in 0...limit-1 { 
      if isSafeForQueen(atRow: r, col: col) { 
       let q = Queen(row: r, col: col) 
       queens.append(q) 
       dropQueen(atRow: r+1) 
       if queens.count == limit { 
        return 
       } 
       queens.removeLast() 
      } 
     } 
    } 
} 

, 당신은 println(output())에 의해 return을 교체하는 경우, 다음 프로그램은 모두 92 개 솔루션을 인쇄합니다. 당신은 행동에서 볼 수 있습니다 http://swiftstub.com/923601919/

+0

고맙습니다. 당신의 대답을보기 전에 재귀 사용에 대한 많은 경험이 없습니다. 외관상으로는 나는 편익을 위해 재귀를 과용하고 컴퓨터에 모든 무거운 일을두고 성과를 명중했다. 그래서 코드의 핵심 부분을 변경하고 작동합니다. 나는 새로운 코멘트로 나의 코드를 넣을 것이다. 호기심에서 벗어나서 내가 옛날 방식으로 글을 쓰면 더 빨리 달릴 수 있을까? (예를 들어, C에서 사람들은 구조체 또는 클래스에 데이터를 저장하는 대신 여왕 (i, j)을 나타내는 array [i] = j를 사용합니까?) 많은 시간이 걸립니다. –

+0

당신이 듣게되어 좋았지 만 원래 코드보다 훨씬 복잡하다는 사실은 혼란 스럽습니다. 재귀는 일을 더 단순하게 만든다. 당신은 혼자 힘든 길을 만들고 있습니다! 제 코드를 살펴보십시오 (위의 편집 참조). 하나의 간단하고 컴팩트 한 재귀 함수에서 전체 알고리즘. 그리고 이전에 말한 것처럼 : 재귀 알고리즘에서 역 추적을하지 마십시오! 'array'접근법에 관해서는, 네가 반복적으로 객체를 생성하고 처리하는 오버 헤드 (구조체'Queen'의 인스턴스)가 없으므로 약간 더 효율적입니다. –

+1

마스터에게서 배웠습니다! 코딩을 시작할 때, 나는 본능 논리를 따랐을 뿐이다. 문제를 해결하는 방법은 step1, step2 일 수 있고, 코딩 스타일이 정의 된 것 같다. 일이 잘못 될 때마다 나는 그것을 고치려고합니다. 이것은 더 많은 코드를 추가한다는 것을 의미합니다. 대부분의 경우, 코드를 유연하고 재사용하기보다는 프로그램 실행을 우선 순위로 지정하십시오. '적은 수의 코드 줄이 더 낫다'또는 '단순함 ..'이라고 생각할 때마다 나는 더 동의하지 않고 어떻게 여기에서 얻을 수 있는지 모른다. 아마 약간의 코드는 더 많은 사고를 의미합니다. –

0

내 변경된 코드 :

모든

첫째, 내가 할 dropQueen() 특정 목적을 위해 takeQueen() : 다음

func dropQueen(atRow r: Int, col: Int) { 
    let q = Queen(row: r, col: col) 
    queens.append(q) 
} 

func takeLastQueen() -> (Int, Int) { 
    let q = queens.last 
    if q != nil { 
     queens.removeLast() 
    } 
    // return (-1,0) means it's time to stop the game 
    return q != nil ? (q!.row, q!.col) : (-1,0) 
} 

, 사용 역 추적 용으로 반복 동일 행의 각 열에 만 사용 재귀 루프 (물론, makeStep (전화 기능)을 변경하고 제 체크 "완료"를 추가 할 때 행 == 제한)

func makeStep(atRow r: Int, fromCol: Int) { 

    if r == limit { 
     // When find a resolution, keep the result 
     // But it won't stop 
     output() 
    } else if r < 0 { 
     // Time to stop game 
     // but why just 27 resolutions ? 
     return 
    } 

    var isQueenAtCurrentRow = false 

    for c in fromCol..<limit { 
     if isSafeForQueen(atRow: r, col: c) { 
      dropQueen(atRow: r, col: c) 
      isQueenAtCurrentRow = true 
      break 
     } 
    } 

    // Only use recursion for backtrack 
    if isQueenAtCurrentRow { 
     // if queen is set, go to next row 
     makeStep(atRow: r+1, fromCol: 0) 
    } else { 
     // if failed, go back to last row and try next spot 
     let (r,c) = backTrackForNext() 
     makeStep(atRow: r, fromCol: c) 
    } 
} 

backTrackForNext() 함수는 마지막 행으로 돌아가서 마지막 여왕의 다음 지점을 찾습니다. 난 놀이터에서 27 개 결과를 얻을 때까지 다음 자리는 체스 판 벗어난 경우가 결과를 찾으면 다음 다시

func backTrackForNext() -> (Int, Int) { 
    // find last row 
    var (r,c) = takeLastQueen() 
    c++ // find next spot 
    if c == limit { 
     // if hit end of column, go back again 
     backTrackForNext() 
    } 
    return (r,c) 
} 

이동, 그것은 멈추지 않을 것입니다. 나는 왜 27 살인지 알지 못했지만, 너희들은 나를 도와 주는데 대단하다.

관련 문제