【问题标题】:All subsets of a set in clojureclojure 中集合的所有子集
【发布时间】:2014-01-03 22:39:52
【问题描述】:

我希望生成一个集合的所有子集,除了空集

即

(all-subsets #{1 2 3}) => #{#{1},#{2},#{3},#{1,2},#{2,3},#{3,1},#{1,2,3}}

如何在 clojure 中做到这一点?

【问题讨论】:

  • 通常幂集也包含空集。

标签: clojure set subset combinatorics


【解决方案1】:

在您的:dependencies 中project.clj:

[org.clojure/math.combinatorics "0.0.7"]

在 REPL:

(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}}

clojure.math.combinatorics/subsets 明智地返回一个 seq 的 seq,因此需要额外的转换来匹配您想要的输出。

【讨论】:

  • 我希望有一个简单的实现,而不是依赖库
  • @zcaudate:Clojure 是一种库语言。解决此类一般问题的手写实现绝不是一个好主意,而且根本不是好的实践,除非是出于学习目的。依赖像math.combinatorics 这样的官方库没有任何取舍,而且通常在很大程度上优于您可以手动完成的所有操作。如果可以改进,您也可以期待致力于解决该问题的人对其进行改进。
【解决方案2】:

这是一个简洁的尾递归版本,仅依赖于 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】:

    @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).
    

    【讨论】:

      【解决方案4】:

      这是@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 并不懒惰。 最好有一个像 Brent 那样的惰性求值版本,以保持表达式的优雅,同时使用惰性来实现效率。

      这个版本作为一个框架还有另一个优势,即当有太多子集需要计算时,它可以轻松地支持对候选子集进行修剪。可以在conj的位置添加剪枝逻辑。我用它来实现“频繁项集”的先验算法。

      【讨论】:

        【解决方案5】:

        参考: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})

        【讨论】:

        • 可能我对这个答案唯一真正的狡辩是使用 1 和小写 L 作为变量名。不得不眯着眼睛看它。
        【解决方案6】:

        此版本大致仿照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))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多