Last active
January 7, 2021 01:29
-
-
Save camsaul/2f21489fe3d2ad229481f3761ac90dd7 to your computer and use it in GitHub Desktop.
core.logic make change
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns make-change | |
(:refer-clojure :exclude [==]) | |
(:require [clojure.core.logic :refer :all] | |
[clojure.core.logic.fd :as fd])) | |
(def ^:dynamic *coin-set* | |
{:penny 1 | |
:nickel 5 | |
:dime 10 | |
:quarter 25}) | |
(def ^:dynamic *quantities* nil) | |
(defn possible-total-num-coins-interval [coin-set amount] | |
(let [[smallest-coin largest-coin] (apply (juxt min max) (vals coin-set))] | |
(mapv #(long (Math/ceil (/ (double amount) %))) | |
[largest-coin smallest-coin]))) | |
(defn sum° [l summ] | |
(matcha [l] | |
([[]] | |
(== summ 0)) | |
([[?x]] | |
(== summ ?x)) | |
([[?head . ?more]] | |
(fresh [sum-more] | |
(fd/+ ?head sum-more summ) | |
(sum° ?more sum-more))))) | |
(defn make-change* [amount] | |
(let [coin-set *coin-set* | |
coins (vec (for [[coin-name value] coin-set | |
:let [quantity (when *quantities* | |
(get *quantities* coin-name 0))] | |
:when (<= value amount)] | |
{:name coin-name | |
:value value | |
:n (lvar) | |
:total (lvar) | |
:max-n ((fnil min Integer/MAX_VALUE) quantity (long (/ amount value)))})) | |
num-coins (lvar)] | |
(run 1 [q] | |
(== q {:num-coins num-coins, :coins coins}) | |
(fd/in num-coins (apply fd/interval (possible-total-num-coins-interval coin-set amount))) | |
(everyg | |
(fn [{:keys [value n total max-n], :as coin}] | |
(all | |
(fd/in n (fd/interval 0 max-n)) | |
(fd/* value n total))) | |
coins) | |
(sum° (mapv :n coins) num-coins) | |
(sum° (mapv :total coins) amount)))) | |
(defn format-solution [{:keys [num-coins coins]}] | |
(into (array-map :num-coins num-coins) (for [coin (sort-by (comp - :value) coins) | |
:when (pos? (:n coin))] | |
[(:name coin) (:n coin)]))) | |
(defn make-change [amount] | |
{:pre [(not (neg? amount))]} | |
(let [amount (long (Math/floor (* amount 100)))] | |
(when-let [[solution] (not-empty (make-change* amount))] | |
(format-solution solution)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Examples: