0

このプログラムの正しい出力を生成するのに問題があります。私の出力はほぼ正しいですが、いくつかのステップが欠落しています。私のコードは次のとおりです。

(defun kt (x y m n)                       ;set the board
  (setq totalmoves (* m n))     ;find the total moves
  (setq steps 1)                         ;count steps
  (setq lst (list (list x y)))  ;list visited with initial points
  (findPath x y totalmoves steps m n lst)       ;start tour with initial points
)

(defun findPath (x y totalMoves steps m n lst)
  (cond
  ((null lst) NIL)
  ((= steps totalMoves) lst)                ;if steps equals than total moves, then solution is complete
  ;try to solve the rest recursively
  ;1- down and right
  ((canMove (+ x 2) (+ y 1) m n lst)
    (findPath (+ x 2) (+ y 1) totalMoves (+ steps 1) m n (appendList (+ x 2)        (+ y 1) lst))

)
;2- right and down
((canMove (+ x 1) (+ y 2) m n lst)
     (findPath (+ x 1) (+ y 2) totalMoves (+ steps 1) m n (appendList (+ x 1) (+ y 2) lst))

)
;3- right ups
((canMove (- x 1) (+ y 2) m n lst)
     (findPath (- x 1) (+ y 2) totalMoves (+ steps 1) m n (appendList(- x 1) (+ y 2) lst))

)
;4- up and right
((canMove(- x 2) (+ y 1) m n lst)
     (findPath(- x 2) (+ y 1) totalMoves (+ steps 1) m n (appendList(- x 2) (+ y 1) lst))
)
;5 - up and left
((canMove(- x 2) (- y 1) m n lst)
     (findPath(- x 2) (- y 1) totalMoves (+ steps 1) m n (appendList(- x 2) (- y 1) lst))
)
;6- left and up 
((canMove(- x 1) (- y 2) m n lst)
     (findPath(- x 1) (- y 2) totalMoves (+ steps 1) m n (appendList(- x 1) (- y 2) lst))
)
;7- left and down
((canMove(+ x 1) (- y 2) m n lst)
     (findPath(+ x 1) (- y 2) totalMoves (+ steps 1) m n (appendList(+ x 1) (- y 2) lst))
)
;8- down and left
((canMove(+ x 2) (- y 1) m n lst)
     (findPath(+ x 2) (- y 1) totalMoves (+ steps 1) m n (appendList(+ x 2) (- y 1) lst))
)
(t 
 (findPath (car(car(reverse lst))) (car(cdr(car(reverse lst)))) totalMoves steps m n (reverse(cdr (reverse lst))))
  )
)
)

(defun appendList (x y lst)
  (setq lst (reverse(append (list (list x y)) (reverse lst))))
)

(defun check-visited (x y lst)
  (cond
    ((null lst) 1)              ;if nth else to check in list
    ((equal (list x y) (car lst)) NIL)    ;check if curr is visited
    ((check-visited x y (cdr lst)))        ;recurse
  )
)

(defun canMove (x y m n lst)
  (if (and (<= 1 x)         ;for the correct state, all conditions below must be met          
       (<= x m)    ;height is more than or equal to x
       (<= 1 y)
       (<= y n)     ;width is more than or equal to y
       (equal (check-visited x y lst) 1)
  ) 
  1 NIL ;if all above conds are true, return true else return nil
)
)

テストコードは

kt 1 1 5 5

出力は((1 1) (3 2) (5 3) (4 5) (2 4) (1 2) (3 3) (5 4) (3 5) (1 4) (2 2) (4 3) (5 5) (3 4) (1 5) (2 3) (4 4) (2 5) (1 3) (2 1) (4 2))

ここには 21 のステップがリストされていますが、25 あるはずです。

4

1 に答える 1

2

findPath関数は特定の位置で可能なすべての移動を試行するわけではありませんが、 を使用して最初の可能な移動condのみを試行するため、アプローチは正しくありません (では、最初の非分岐が実行され、対応する値を返すことによってステートメントが終了します)。電話)。したがって、関数は、バックトラックなしで最長のツアーのみを生成します。これは正確に 21 手です。condnilfindPath

正しい解を得るためには、すべてfindPathの可能な移動を試して、 への再帰呼び出しによって適切な数の移動を生成する最初の移動を返す必要があります。orCommon Lisp では、これはandand演算子を使用して実行できます。

  1. ornオペランドで、存在しない場合は最初のオペランドの値を返します。nil存在しない場合は を返します。したがって、 のすべての再帰呼び出しnilを配置する場合、それらの1つが正しい最終値を返す場合、式は終了して戻りますその値;orfindPathor

  2. andnilオペランドのいずれかが である場合は を返しますnil。それ以外の場合、すべてが でない場合nilは、最後のオペランドの値を返します。したがって、最初に移動が可能かどうかを確認し、これが真の場合は、findPath再帰的に呼び出して移動を実行することで使用できます。呼び出しが返された場合、nilその移動は役に立ちません。それ以外の場合は、正しいツアーが見つかりました。

新しい関数は次のとおりです。

(defun findPath (x y totalMoves steps m n lst)
  (if (= steps totalMoves)
      lst           ; if the steps are equal to total moves, then a solution is found
                    ; else try recursively all the possible moves from x y
                    ; 1- down and right
      (or (and (canMove (+ x 2) (+ y 1) m n lst)
               (findPath (+ x 2) (+ y 1) totalMoves (+ steps 1) m n (appendList (+ x 2) (+ y 1) lst)))
                    ; 2- right and down
          (and (canMove (+ x 1) (+ y 2) m n lst)
               (findPath (+ x 1) (+ y 2) totalMoves (+ steps 1) m n (appendList (+ x 1) (+ y 2) lst)))
                    ; 3- right ups
          (and (canMove (- x 1) (+ y 2) m n lst)
               (findPath (- x 1) (+ y 2) totalMoves (+ steps 1) m n (appendList (- x 1) (+ y 2) lst)))
                    ; 4- up and right
          (and (canMove (- x 2) (+ y 1) m n lst)
               (findPath (- x 2) (+ y 1) totalMoves (+ steps 1) m n (appendList (- x 2) (+ y 1) lst)))
                    ; 5 - up and left
          (and (canMove (- x 2) (- y 1) m n lst)
               (findPath (- x 2) (- y 1) totalMoves (+ steps 1) m n (appendList (- x 2) (- y 1) lst)))
                    ; 6- left and up 
          (and (canMove (- x 1) (- y 2) m n lst)
               (findPath (- x 1) (- y 2) totalMoves (+ steps 1) m n (appendList (- x 1) (- y 2) lst)))
                    ; 7- left and down
          (and (canMove (+ x 1) (- y 2) m n lst)
               (findPath (+ x 1) (- y 2) totalMoves (+ steps 1) m n (appendList (+ x 1) (- y 2) lst)))
                    ; 8- down and left
          (and (canMove (+ x 2) (- y 1) m n lst)
               (findPath (+ x 2) (- y 1) totalMoves (+ steps 1) m n (appendList (+ x 2) (- y 1) lst))))))

最後に、コードに関するいくつかの注意事項。

setq以前に宣言されていない変数の初期化には使用しないでください

letkt関数を次のように定義できるように、ローカル変数の宣言と初期化に使用できます。

(defun kt (x y m n)                            ; set the board
  (let ((totalmoves (* m n))                   ; find the total moves
        (steps 1)                              ; count steps
        (lst (list (list x y))))               ; list visited with initial points
    (findPath x y totalmoves steps m n lst)))  ; start tour with initial points

コードをシンプルに保つようにしてください

関数appendListは、1 つの要素のリストを別のリストの逆に追加し、結果を逆にすることによって定義されます。これは、最初のリストを 2 番目のリストに単純に追加することと同じです。つまり、次のようになります。

(defun appendList (x y lst)
  (append lst (list (list x y))))

一般化されたブール値を使用して条件を簡素化する

たとえば、関数check-visitedandcanMoveは、単一のより単純な関数に統合できます。

(defun canMove (x y m n lst)
  (and (<= 1 x m)      ;for the correct state, all conditions below must be met          
       (<= 1 y n)
       (not (member (list x y) lst :test 'equal))))

コードを因数分解するか、必要がない場合は同様のコードを繰り返さないでください

関数には多くの繰り返しがあり、 (はaの と同等です)findPathを使用して排除できます。loopthereisorloop

(defun findPath (x y totalMoves steps m n lst)
  (if (= steps totalMoves)
      lst 
      (loop for (x-inc y-inc) in '((+2 +1) (+1 +2) (-1 +2) (-2 +1) (-2 -1) (-1 -2) (+1 -2) (+2 -1))
        for x1 = (+ x x-inc)
        for y1 = (+ y y-inc)
        thereis (and (canMove x1 y1 m n lst)
                     (findPath x1 y1 totalMoves (1+ steps) m n (appendList x1 y1 lst))))))

使用中の言語に一般的な規則を使用する

Common Lisp では、camelCase は避けられ、名前と動詞がダッシュで区切られた古典的な表記法が好まれfind-pathます。findPathcan-movecanMove

于 2016-02-24T10:26:12.820 に答える