找到总和为n的整数集合的所有子集

时间:2013-11-27 14:36:38

标签: clojure combinatorics

我试图找到一个函数,给定S一组整数,我是一个整数,返回总和为I的所有S的子集

在clojure-contrib或其他库中某处有这样的功能吗?

如果不是,有人可以给我一些提示来写一下clojure方式吗?

3 个答案:

答案 0 :(得分:3)

这不是subset sum problem,这是一个经典的NP完全问题吗?

在这种情况下,我只生成S的每个可能的不同子集,并查看哪些子集与I相加。

答案 1 :(得分:3)

我认为这是子集和问题,正如@MrBones所暗示的那样。以下是使用https://github.com/clojure/math.combinatorics(lein:[org.clojure/math.combinatorics "0.0.7"])的强力尝试:

(require '[clojure.math.combinatorics :as c])

(defn subset-sum [s n] 
  "Return all the subsets of s that sum to n."
  (->> (c/subsets s)
       (filter #(pos? (count %))) ; ignore empty set since (+) == 0
       (filter #(= n (apply + %)))))

(def s #{1 2 45 -3 0 14 25 3 7 15})

(subset-sum s 13)
; ((1 -3 15) (2 -3 14) (0 1 -3 15) (0 2 -3 14) (1 2 3 7) (0 1 2 3 7))
(subset-sum s 0)
; ((0) (-3 3) (0 -3 3) (1 2 -3) (0 1 2 -3))

这些“子集”只是列表。可以转换回套装,但我没有打扰。

答案 2 :(得分:1)

您可以生成如下集合的子集:

(defn subsets [s]
  (if (seq s)
    (let [f (first s), srs (subsets (disj s f))]
      (concat srs (map #(conj % f) srs)))
    (list #{})))

我们的想法是从集s中选择一个元素:第一个元素f将会这样做。然后我们递归地找到其他所有子集srssrs包含所有没有f的子集。通过向每个f添加f,我们可以获得n的所有子集。一起,这就是很多。最后,如果我们因为没有任何元素而无法选择元素,那么唯一的子集就是空元素。

剩下要做的就是从所有子集中过滤出总和为(fn [s] (= n (reduce + s))) 的子集。测试它的功能是

(defn subsets-summing-to [s n]
  (filter
    (fn [xs] (= n (reduce + xs)))
    (subsets s)))

不值得命名。

把这个放在一起,我们想要的功能是

concat

注释

  • 由于答案是一系列集合,我们可以通过将lazy-cat更改为map来使其更加懒散。 {{1}}无论如何都是懒惰的。
  • 我们似乎生成了很多集合,但请记住它们共享存储:保持另一个集合的空间成本因单个元素而异(几乎)不变。
  • Clojure算法中空集合总和为零。