>
(ns verbal-arithmetic
  (:require
    [clojure.core.logic :refer [all run* everyg lvar == membero fresh conde succeed fail conso resto]]
    [clojure.core.logic.fd :as fd]))
(comment
  "Solving cryptarithmetic puzzle"
  " SEND
  + MORE
  ______
   MONEY")

(defn send-more-money-solutions []
  (run* [s e n d m o r y]
        (fd/in s e n d m o r y (fd/interval 0 9))
        (fd/!= s 0)
        (fd/!= m 0)
        (fd/distinct [s e n d m o r y])
        (fd/eq (= (apply + [(* 1000 s) (* 100 e) (* 10 n) d
                            (* 1000 m) (* 100 o) (* 10 r) e])
                  (apply + [(* 10000 m) (* 1000 o) (* 100 n) (* 10 e) y])))))

apply 때문에 위의 예제가 작동하지 않습니다   fd/eq 에서 제대로 작동하지 않습니다 . send-more-money-solutions 의 다음 버전   apply 를 사용하지 않기 때문에 작동합니다. . apply 를 사용해야합니다  길이가 다른 임의의 문자열로 작업하도록 솔루션을 일반화하십시오.

(defn send-more-money-solutions []
  (run* [s e n d m o r y]
        (fd/in s e n d m o r y (fd/interval 0 9))
        (fd/!= s 0)
        (fd/!= m 0)
        (fd/distinct [s e n d m o r y])
        (fd/eq (= (+ (* 1000 s) (* 100 e) (* 10 n) d
                     (* 1000 m) (* 100 o) (* 10 r) e)
                  (+ (* 10000 m) (* 1000 o) (* 100 n) (* 10 e) y)))))

어떻게해야합니까? (위의 경우 매크로를 작성할 수는 있지만 (아직 아직 확실하지는 않지만) 실제로 논리 변수 시퀀스 인 변수를 사용할 수 있어야한다는 아이디어가 있습니다.

(fd/eq (= (+ (apply + lvars1) (apply + lvars2))
          (apply + lvars3)))

오류 메시지는 다음과 같습니다

java.lang.IllegalArgumentException: Can't call nil, form: (nil + [(* 1000 s) (* 100 e) (* 10 n) d (* 1000 m) (* 100 o) (* 10 r) e] G__1124704)

fd/eq 에서 이상한 일이 벌어지고 있다고 생각합니다   eq 를 사용하지 않고 시도 해야하는 매크로  매크로.

미리 감사합니다!

  • 답변 # 1

    와이즈 비즈

    정확히이 문제에 대한일반솔루션은 임의의 동적 수의 논리 변수를 도입하여 관련/제한하는 것입니다.

    솔버

    먼저 일련의 논리 변수로 작업 할 재귀 목표를 정의하십시오. (행운 적으로 나는 이미 이전의 문제로 이것들을 가지고 있었다!)

    <올>

    논리 변수 시퀀스의 합을 다른 논리 변수와 연관 시키십시오 :

    I need to be able to use a variables that is a sequence of logic variables

    두 개의 논리 변수 시퀀스의 곱의 합을 다른 논리 변수와 연관시킵니다 :

    (defn sumo [vars sum]
      (fresh [vhead vtail run-sum]
        (conde
          [(== vars ()) (== sum 0)]
          [(conso vhead vtail vars)
           (fd/+ vhead run-sum sum)
           (sumo vtail run-sum)])))
    
    

    크기 곱셈기를 생성하는 작은 도우미 함수 :

    (defn productsumo [vars dens sum]
      (fresh [vhead vtail dhead dtail product run-sum]
        (conde
          [(emptyo vars) (== sum 0)]
          [(conso vhead vtail vars)
           (conso dhead dtail dens)
           (fd/* vhead dhead product)
           (fd/+ product run-sum sum)
           (productsumo vtail dtail run-sum)])))
    
    

    그런 다음에 모두 연결하십시오 :

    (defn magnitudes [n]
      (reverse (take n (iterate #(* 10 %) 1))))
    
    

    이 중 일부는 귀하의 예와 친숙해 보이지만 가장 큰 차이점은 단어 수 (및 해당 문자)를 동적으로 처리 할 수 ​​있다는 것입니다. 새로운 로직 변수는 (defn cryptarithmetic [& words] (let [distinct-chars (distinct (apply concat words)) char->lvar (zipmap distinct-chars (repeatedly (count distinct-chars) lvar)) lvars (vals char->lvar) first-letter-lvars (distinct (map #(char->lvar (first %)) words)) sum-lvars (repeatedly (count words) lvar) word-lvars (map #(map char->lvar %) words)] (run* [q] (everyg #(fd/in % (fd/interval 0 9)) lvars) ;; digits 0-9 (everyg #(fd/!= % 0) first-letter-lvars) ;; no leading zeroes (fd/distinct lvars) ;; only distinct digits (everyg (fn [[sum l]] ;; calculate sums for each word (productsumo l (magnitudes (count l)) sum)) (map vector sum-lvars word-lvars)) (fresh [s] (sumo (butlast sum-lvars) s) ;; sum all input word sums (fd/== s (last sum-lvars))) ;; input word sums must equal last word sum (== q char->lvar)))) 로 생성됩니다  모든 문자 세트와 각 단어의 합계에 대해 그런 다음 lvar 를 사용하여 논리 변수를 제한하거나 관련시킵니다.  위의 재귀 목표.

    샘플 문제

    이 함수는 주어진 단어에 대한 모든 솔루션을 반환하며 "더 많은 돈을 보내십시오"는 가능한 해결책이 하나만 있습니다 :

    everyg
    
    

    네 단어로 된 또 다른 예는 "cp is fun true"입니다 (Google Cryptarithmetic Puzzles 참조). 가능한 해결책은 72 가지입니다.

    (cryptarithmetic "send" "more" "money")
    => ({\s 9, \e 5, \n 6, \d 7, \m 1, \o 0, \r 8, \y 2})
    
    

    이것은 Wikipedia에서 찾을 수있는 가장 큰 것입니다.이 기능은 내 노트북에서 ~ 30 초에 유일한 솔루션을 찾습니다 :

    (cryptarithmetic "cp" "is" "fun" "true")
    =>
    ({\c 2, \e 4, \f 9, \i 7, \n 3, \p 5, \r 0, \s 6, \t 1, \u 8}
     {\c 2, \e 5, \f 9, \i 7, \n 3, \p 4, \r 0, \s 8, \t 1, \u 6}
     {\c 2, \e 6, \f 9, \i 7, \n 3, \p 5, \r 0, \s 8, \t 1, \u 4}
     ...
    
    

    다음은 결과를 예쁘게 인쇄하는 기능입니다 :

    (cryptarithmetic "SO" "MANY" "MORE" "MEN" "SEEM" "TO"
                     "SAY" "THAT" "THEY" "MAY" "SOON" "TRY"
                     "TO" "STAY" "AT" "HOME" "SO" "AS" "TO"
                     "SEE" "OR" "HEAR" "THE" "SAME" "ONE"
                     "MAN" "TRY" "TO" "MEET" "THE" "TEAM"
                     "ON" "THE" "MOON" "AS" "HE" "HAS"
                     "AT" "THE" "OTHER" "TEN" "TESTS")
    => ({\A 7, \E 0, \H 5, \M 2, \N 6, \O 1, \R 8, \S 3, \T 9, \Y 4})
    
    
    (defn pprint-answer [char->digit words] (let [nums (map #(apply str (map char->digit %)) words) width (apply max (map count nums)) width-format (str "%" width "s") pad #(format width-format %)] (println (clojure.string/join \newline (concat (map #(str "+ " (pad %)) (butlast nums)) [(apply str (repeat (+ 2 width) \-)) (str "= " (pad (last nums)))])) \newline))) (cryptarithmetic "wrong" "wrong" "right") (map #(pprint-answer % ["wrong" "wrong" "right"]) *1) ; + 12734 ; + 12734 ; ------- ; = 25468

  • 이전 expo/firebase - jpg 대신 옥텟 스트림으로 카메라 롤 업로드에서 선택한 이미지
  • 다음 python - 어떻게 이것을 더 빨리 만들 수 있습니까? 아니면 올바른 용어입니까?