December 1, 2019
By: Kevin
core.logic逻辑引擎的一个例子
逻辑编程是种编程范型,通过指定匹配的逻辑规则来解决问题,不需要设置步骤。过程是:
事实+规则=结果
在调度、规划等领域有广泛应用,当然也可以用来解答数字游戏,填字,数独。
在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

(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以外的数字符合条件
;; 所以只能有两组数字满足以上要求.