Skip to content

Instantly share code, notes, and snippets.

@jngbng
Created September 26, 2021 08:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jngbng/69f341f818bb6b88cecb9c364f5bcb17 to your computer and use it in GitHub Desktop.
Save jngbng/69f341f818bb6b88cecb9c364f5bcb17 to your computer and use it in GitHub Desktop.
logic programming 2 (feat. 문제적 남자)
(ns core-logic.core
(:require [clojure.core.logic :as logic]
[clojure.core.logic.fd :as fd]))
;;; 출처: https://blog.taylorwood.io/2018/05/10/clojure-logic.html
(defn- productsumo
"Sigma (vars_i * denoms_i) = sum 을 만족하는가."
[vars denoms sum]
(logic/fresh [vhead vtail
dhead dtail
product tail-sum]
(logic/conde ;; or 로 읽으면 된다.
;; vars가 빈 리스트라면, sum도 0이어야 한다.
[(logic/emptyo vars) (logic/== sum 0)]
;; 아니라면 재귀호출
[;; vars의 첫번째를 vhead라 하고, 나머지를 vtail 이라 하자.
(logic/conso vhead vtail vars)
;; 마찬가지로 denoms의 첫번째를 dhead라 하고, 나머지를 dtail 이라 하자.
(logic/conso dhead dtail denoms)
;; product = vhead * dhead
(fd/* vhead dhead product)
;; tail-sum = sum - product
(fd/- sum product tail-sum)
;; tail들로 연산을 하면 tail-sum이 되어야 함.
(productsumo vtail dtail tail-sum)])))
(defn- strictly-increasing?
"coll의 값들이 증가하는 순서로 정렬되어 있나?
forall i j, i < j -> coll[i] < coll[j]"
[coll]
(logic/everyg (fn [[i j]]
(fd/< i j))
(partition 2 1 coll)))
(defn- in*
"lvar-coll의 범위를 모두 interval로 설정"
[lvar-coll interval]
(logic/everyg (fn [lv]
(fd/in lv interval))
lvar-coll))
(defn solution-n [num-groups]
(let [total-qty1 50
total-qty2 30
total-qty3 10
min-total-qty (min total-qty1
total-qty2
total-qty3)
;; 묶음별 수량
qtys (repeatedly num-groups logic/lvar)
;; 묶음별 가격
prices (repeatedly num-groups logic/lvar)
;; 첫째의 묶음별 판매량
sales1 (repeatedly num-groups logic/lvar)
;; 둘째의 묶음별 판매량
sales2 (repeatedly num-groups logic/lvar)
;; 셋째의 묶음별 판매량
sales3 (repeatedly num-groups logic/lvar)]
(logic/run* [q]
(logic/fresh [earn]
;; 원하는 결과값.
(logic/== q {:price (map (fn [qty price]
{:qty qty
:price price})
qtys
prices)
:sales [sales1
sales2
sales3]})
;; 묶음별 수량 범위: 1 <= qty <= min-total-qty
(in* qtys (fd/interval 1 min-total-qty))
;; strictly-increasing? qty1 < qty2 < ...
(strictly-increasing? qtys)
;; 가격 범위: 적당히 설정
(in* prices (fd/interval 1 30))
;; 가격은 서로 다르다.
(fd/distinct prices)
;; 수식을 고려하여 가능한 범위 설정
(in* sales1 (fd/interval 1 total-qty1))
(in* sales2 (fd/interval 1 total-qty2))
(in* sales3 (fd/interval 1 total-qty3))
;; 수량 조건
(productsumo sales1 qtys total-qty1)
(productsumo sales2 qtys total-qty2)
(productsumo sales3 qtys total-qty3)
;; 가격 조건
(productsumo sales1 prices earn)
(productsumo sales2 prices earn)
(productsumo sales3 prices earn)))))
(first (solution-n 3))
;;; returns
{:price ({:qty 1, :price 3} {:qty 2, :price 1} {:qty 3, :price 19}),
:sales [(1 23 1) (5 11 1) (2 1 2)]}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment