我希望生成除空集
之外的集合的所有子集即
(all-subsets #{1 2 3}) => #{#{1},#{2},#{3},#{1,2},#{2,3},#{3,1},#{1,2,3}}
如何在clojure中完成?
答案 0 :(得分:11)
:dependencies
project.clj
中的[org.clojure/math.combinatorics "0.0.7"]
:
(require '[clojure.math.combinatorics :as combinatorics])
(->> #{1 2 3}
(combinatorics/subsets)
(remove empty?)
(map set)
(set))
;= #{#{1} #{2} #{3} #{1 2} #{1 3} #{2 3} #{1 2 3}}
在REPL:
clojure.math.combinatorics/subsets
{{1}}明智地返回一系列seqs,因此额外的转换可以匹配您想要的输出。
答案 1 :(得分:3)
@zcaudate:为了完整性,这是一个递归实现:
(defn subsets
[s]
(if (empty? s)
#{#{}}
(let [ts (subsets (rest s))]
(->> ts
(map #(conj % (first s)))
(clojure.set/union ts)))))
;; (subsets #{1 2 3})
;; => #{#{} #{1} #{2} #{3} #{1 2} #{1 3} #{2 3} #{1 2 3}} (which is correct).
答案 2 :(得分:3)
这是一个简洁的尾递归版本,仅依赖于clojure.core。
(defn power [s]
(loop [[f & r] (seq s) p '(())]
(if f (recur r (concat p (map (partial cons f) p)))
p)))
如果您希望将结果放在一组集中,请使用以下内容。
(defn power-set [s] (set (map set (power s))))
答案 3 :(得分:2)
这是@Brent M. Spell解决方案的一个细微变化,以寻求对惯用语Clojure中的表现考虑的启示。
我只是想知道是否在循环中构造子集而不是通过(map set ...)
进行另一次迭代会节省一些开销,尤其是当集合非常大时?
(defn power [s]
(set (loop [[f & r] (seq s) p '(#{})]
(if f (recur r (concat p (map #(conj % f) p)))
p))))
(power [1 2 3])
;; => #{#{} #{3} #{2} #{1} #{1 3 2} #{1 3} #{1 2} #{3 2}}
在我看来loop
和recur
并不是懒惰的。
有一个懒惰的评估版本,如布伦特的,以保持表达优雅,同时使用懒惰同时实现效率将是很好的。
当有太多要计算的子集时,作为框架的这个版本具有另一个优点,即可以轻松支持子集候选者的修剪。可以在conj
的位置添加修剪逻辑。我用它来实现“Frequent Item Set”的先前算法。
答案 4 :(得分:1)
请参阅:Algorithm to return all combinations of k elements from n
(defn comb [k l]
(if (= 1 k) (map vector l)
(apply concat
(map-indexed
#(map (fn [x] (conj x %2))
(comb (dec k) (drop (inc %1) l)))
l))))
(defn all-subsets [s]
(apply concat
(for [x (range 1 (inc (count s)))]
(map #(into #{} %) (comb x s)))))
(所有子集#{1 2 3})
; (#{1}#{2}#{3}#{1 2}#{1 3}#{2 3}#{1 2 3})
答案 5 :(得分:1)
此版本在ES5 version on Rosetta Code之后松散建模。我知道这个问题似乎已经合理地解决了......不管怎么说,你去吧。
(fn [s]
(reduce
(fn [a b] (clojure.set/union a
(set (map (fn [y] (clojure.set/union #{b} y)) a))))
#{#{}} s))