December 1, 2019
By: Kevin

core.logic逻辑引擎的一个例子

  1. 例子1
  2. 例子2

逻辑编程是种编程范型,通过指定匹配的逻辑规则来解决问题,不需要设置步骤。过程是:

事实+规则=结果

在调度、规划等领域有广泛应用,当然也可以用来解答数字游戏,填字,数独。

在lisp语言中有很好的支持,core.logic 是clojure的逻辑编程库。

例子1

一道题

(ns number-game.core
  (:refer-clojure :exclude [==])
  (:require [clojure.core.logic :refer :all])
  (:require [clojure.core.logic.fd :as fd]))

(->> (run* [q]
       (fresh [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10]
         (== q [a1 a2 a3 a4 a5 a6 a7 a8 a9 a10])
         (fd/in  a1 a2 a3 a4 a5 a6 a7 a8 a9 a10  (fd/interval 1 4))
         ;; 1 这道题的答案是?A、A B、B C、C D、D
         ;; 2 第五题的答案是?A、C B、D C、A D、B
         (conde
          [(== a2 1) (== a5 3)]
          [(== a2 2) (== a5 4)]
          [(== a2 3) (== a5 1)]
          [(== a2 4) (== a5 2)])
         ;; 3 以下哪一项和其它三项不同  A、3 B、6 C、2 D、4
         (conde

          [(== a2 a6) (== a6 a4) (!= a3 a2) (== a3 1)]
          [(== a3 a2) (== a2 a4) (!= a6 a3) (== a3 2)]
          [(== a3 a6) (== a6 a4) (!= a2 a4) (== a3 3)]
          [(== a3 a6) (== a6 a2) (!= a4 a3) (== a3 4)])
         ;; 4 哪两个答案相同 A、1 5 B、2 7 C、1 9 D、6 10
         (conde
          [(== a1 a5) (== a4 1)]
          [(== a2 a7) (== a4 2)]
          [(== a1 a9) (== a4 3)]
          [(== a6 a10) (== a4 4)])
         ;; 5 答案与本题相同 A、8 B、4 C、9 D、7
         (conde
          [(== a5 a8) (== a5 1)]
          [(== a5 a4) (== a5 2)]
          [(== a5 a9) (== a5 3)]
          [(== a5 a7) (== a5 4)])

         ;;6 与第八题相同的 A、2 4 B、1 6 C、3 10 D、5 9
         (conde
          [(== a2 a4)  (== a4 a8)  (== a6 1)]
          [(== a1 a6)  (== a6 a8)  (== a6 2)]
          [(== a3 a10) (== a10 a8) (== a6 3)]
          [(== a5 a9)  (== a9 a8)  (== a6 4)])
         
         ;; 8 哪一题的答案和第一题的答案在字母表中不相邻 A、C B、D C、A D、B
         ;; a7 a5 a2 a10
         (project [a7 a5 a2 a10 a1]
                  (let [ds
                        (->> [a7 a5 a2 a10]
                             (map #(Math/abs (- a1 % )) ))]
                    (conde
                     [(!= 1 (get ds 0)) (== a8 1)]
                     [(!= 1 (get ds 1)) (== a8 2)]
                     [(!= 1 (get ds 2)) (== a8 3)]
                     [(!= 1 (get ds 3)) (== a8 4)])))
         ;; 9 已知“第一题与第六题答案相同”和“第X题和第五题答案相同”的真假性相反,那么X为: A、C B、D C、A D、B
         (conde
          [(== a1 a6) (!= a6 a5) (== a9 1)]
          [(!= a1 a6) (== a6 a5) (== a9 1)]

          [(== a1 a6) (!= a10 a5) (== a9 2)]
          [(!= a1 a6) (== a10 a5) (== a9 2)]

          [(== a1 a6) (!= a2 a5) (== a9 3)]
          [(!= a1 a6) (== a2 a5) (== a9 3)]

          [(== a1 a6) (!= a9 a5) (== a9 4)]
          [(!= a1 a6) (== a9 a5) (== a9 4)])

         ;; 10 ABCD出现的最多的和最少的的差值是? A、3 B、2 C、4 D、1
         (project [q]
                  (let [r (->> q
                               (group-by identity )
                               vals
                               (map count)
                               sort
                               (#(- (last %) (first %))))]
                    (conde
                     [(== a10 1) (== r 3) ]
                     [(== a10 2) (== r 2) ]
                     [(== a10 3) (== r 4) ]
                     [(== a10 4) (== r 1) ])))
         ;; 7 被选的最少的:A、C B、B C、A D、D
         (project [q]
                  (let [min-select
                        (->> q
                             (group-by identity)
                             (merge-with concat {1 [] 2 [] 3 [] 4 []})
                             (reduce-kv (fn [m k v]
                                          (assoc m :min (if (> (:min m) (count v))
                                                          (count v)
                                                          (:min m))
                                                 :index (if (> (:min m) (count v))
                                                          k
                                                          (:index m))))
                                        {:index 1
                                         :min 999})
                             :index)]
                    (conde [(== 1 min-select) (== a7 3)]
                           [(== 2 min-select) (== a7 2)]
                           [(== 3 min-select) (== a7 1)]
                           [(== 4 min-select) (== a7 4)])))

         ))
     distinct
     (map (fn [e]
            (map-indexed (fn [i e] [(inc i) (case e
                                             1 "A"
                                             2 "B"
                                             3 "C"
                                             4 "D")]) e)))
     (clojure.pprint/pprint))

最终拿到的结果:

([1 "B"]
 [2 "C"]
 [3 "A"]
 [4 "C"]
 [5 "A"]
 [6 "C"]
 [7 "D"]
 [8 "A"]
 [9 "B"]
 [10 "A"])

例子2

zhouhui

(ns zhouhui-puzzle.core
  (:refer-clojure :exclude [==])
  (:require [clojure.core.logic :refer :all]
            [clojure.core.logic.fd :as fd]))

(run 3 [q]

  (fresh [a b]

    (== q [a b])

    (fd/in a b (fd/interval 1 2000))

    (fd/eq
     (= 2020 (-
              (* a a)
              (* b b))))
    (fd/> a b)))

;; => ([106 96] [506 504])
(-> 2000
    (#(- (* % %)
         (* (dec %) (dec %)))))

;; => 3999
;; 2000 的时候相邻的两个数字的平方差值最小已经是3999, 所以说不可能再有2000以外的数字符合条件
;; 所以只能有两组数字满足以上要求.
Tags: clojure logic clojurescript