【问题标题】:How to generate the power set of a set in Scala如何在Scala中生成集合的幂集
【发布时间】:2012-07-19 20:06:40
【问题描述】:

我有一组某种类型的项目,想要生成它的幂集。

我在网上搜索并找不到任何处理此特定任务的 Scala 代码。

这就是我想出的。它允许您限制由长度参数产生的集合的基数。

def power[T](set: Set[T], length: Int) = {
   var res = Set[Set[T]]()
   res ++= set.map(Set(_))

   for (i <- 1 until length)
      res = res.map(x => set.map(x + _)).flatten

   res
   }

这不包括空集。为此,您必须将方法的最后一行简单地更改为 res + Set()

有什么建议可以以更实用的方式完成吗?

【问题讨论】:

    标签: scala set powerset


    【解决方案1】:

    所有其他答案似乎有点复杂,这是一个简单的函数:

        def powerSet (l:List[_]) : List[List[Any]] =
          l match {
           case Nil => List(List())
           case x::xs =>
             var a = powerSet(xs)
             a.map(n => n:::List(x)):::a
          }
    

    所以

        powerSet(List('a','b','c'))
    

    会产生以下结果

        res0: List[List[Any]] = List(List(c, b, a), List(b, a), List(c, a), List(a), List(c, b), List(b), List(c), List())
    

    【讨论】:

      【解决方案2】:

      这是一个使用辅助函数的简单递归解决方案:

      def concatElemToList[A](a: A, list: List[A]): List[Any] = (a,list) match {
          case (x, Nil)                 => List(List(x))
          case (x, ((h:List[_]) :: t))  => (x :: h) :: concatElemToList(x, t)
          case (x, (h::t))              => List(x, h) :: concatElemToList(x, t)
      }
      
      def powerSetRec[A] (a: List[A]): List[Any] = a match {
          case Nil    => List()
          case (h::t) => powerSetRec(t) ++ concatElemToList(h, powerSetRec (t))
      }
      

      所以调用

      powerSetRec(List("a", "b", "c"))
      

      会给出结果

      List(List(c), List(b, c), List(b), List(a, c), List(a, b, c), List(a, b), List(a))
      

      【讨论】:

      • 结果中缺少空集
      【解决方案3】:

      可以这么简单:

      def powerSet[A](xs: Seq[A]): Seq[Seq[A]] = 
        xs.foldLeft(Seq(Seq[A]())) {(sets, set) => sets ++ sets.map(_ :+ set)}
      

      递归实现:

      def powerSet[A](xs: Seq[A]): Seq[Seq[A]] = {
        def go(xsRemaining: Seq[A], sets: Seq[Seq[A]]): Seq[Seq[A]] = xsRemaining match {
          case Nil => sets
          case y :: ys => go(ys, sets ++ sets.map(_ :+ y))
        }
      
        go(xs, Seq[Seq[A]](Seq[A]()))
      }
      

      【讨论】:

      • 虽然这段代码可以回答问题,但最好包含一些上下文,解释如何它的工作原理和何时 i> 使用它。从长远来看,纯代码的答案没有用处。
      • 向 foldLeft 致敬。我正在专门寻找那个实现。正如之前的评论者所说,一个解释会很好。
      【解决方案4】:

      使用内置的combinations函数:

      val xs = Seq(1,2,3)
      (0 to xs.size) flatMap xs.combinations
      
      // Vector(List(), List(1), List(2), List(3), List(1, 2), List(1, 3), List(2, 3),
      // List(1, 2, 3))
      

      注意,我作弊并使用了Seq,因为未知原因,combinations 是在SeqLike 上定义的。因此,使用一组,您需要转换为/从Seq:

      val xs = Set(1,2,3)
      (0 to xs.size).flatMap(xs.toSeq.combinations).map(_.toSet).toSet
      
      //Set(Set(1, 2, 3), Set(2, 3), Set(), Set(3), Set(2), Set(1), Set(1, 3), 
      //Set(1, 2))
      

      【讨论】:

      • 但是有了set,​​它是否维护字典集?
      【解决方案5】:

      这是另一个(懒惰的)版本......因为我们正在收集计算幂集的方法,所以我想我会添加它:

      def powerset[A](s: Seq[A]) =
        Iterator.range(0, 1 << s.length).map(i =>
          Iterator.range(0, s.length).withFilter(j =>
            (i >> j) % 2 == 1
          ).map(s)
        )
      

      【讨论】:

        【解决方案6】:

        看起来在 7 月份没有人知道它,但有一个内置方法:subsets。

        scala> Set(1,2,3).subsets foreach println
        Set()
        Set(1)
        Set(2)
        Set(3)
        Set(1, 2)
        Set(1, 3)
        Set(2, 3)
        Set(1, 2, 3)
        

        【讨论】:

          【解决方案7】:

          这里有一种更有趣的写法:

          import scalaz._, Scalaz._
          
          def powerSet[A](xs: List[A]) = xs filterM (_ => true :: false :: Nil)
          

          按预期工作:

          scala> powerSet(List(1, 2, 3)) foreach println
          List(1, 2, 3)
          List(1, 2)
          List(1, 3)
          List(1)
          List(2, 3)
          List(2)
          List(3)
          List()
          

          请参阅 this discussion thread 以了解其工作原理。

          (正如 debilski 在 cmets 中指出的那样,ListW 也将powerset 拉到List 上,但这并不好玩。)

          【讨论】:

          • 我喜欢这样 - 我的问题是,filterM 还有什么用途?
          • @oxbow_lakes 例如,您可以执行三路谓词过滤器。 (x =&gt; if ...None/Some(false)/Some(true))。单个None 将清除整个输入。但我想我从未听说过的奇异 monad 会有更高级的用法。
          • 顺便内置:List(1, 2, 3).powerset。 :)
          • @oxbow_lakes: let doYouLike it = fmap (== "y") $ putStr ("do you like " ++ show it ++ " (y/n)? ") &gt;&gt; getLine in filterM doYouLike ["scala", "haskell"]
          • filterM 和朋友们对M=[a]State[X, a] 很有用。例如,您可以过滤掉重复:gist.github.com/3157243
          【解决方案8】:

          请注意,如果您有一个集合S 和另一个集合T,其中T = S ∪ {x}(即T 是S 并添加了一个元素),那么T - P(T) 的幂集 - 可以用P(S)和x表示如下:

          P(T) = P(S) ∪ { p ∪ {x} | p ∈ P(S) }
          

          也就是说,您可以递归地定义幂集(注意这是如何免费为您提供幂集的大小 - 即添加 1 个元素会使幂集的大小翻倍)。因此,您可以在 scala 中以递归方式执行此操作,如下所示:

          scala> def power[A](t: Set[A]): Set[Set[A]] = {
             |     @annotation.tailrec 
             |     def pwr(t: Set[A], ps: Set[Set[A]]): Set[Set[A]] =
             |       if (t.isEmpty) ps
             |       else pwr(t.tail, ps ++ (ps map (_ + t.head)))
             |
             |     pwr(t, Set(Set.empty[A])) //Powerset of ∅ is {∅}
             |   }
          power: [A](t: Set[A])Set[Set[A]]
          

          然后:

          scala> power(Set(1, 2, 3))
          res2: Set[Set[Int]] = Set(Set(1, 2, 3), Set(2, 3), Set(), Set(3), Set(2), Set(1), Set(1, 3), Set(1, 2))
          

          用List(即递归ADT)做同样的事情实际上看起来好多了:

          scala> def power[A](s: List[A]): List[List[A]] = {
             |     @annotation.tailrec 
             |     def pwr(s: List[A], acc: List[List[A]]): List[List[A]] = s match {
             |       case Nil     => acc 
             |       case a :: as => pwr(as, acc ::: (acc map (a :: _)))
             |     }
             |     pwr(s, Nil :: Nil)
             |   }
          power: [A](s: List[A])List[List[A]]
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-09-02
            • 1970-01-01
            • 1970-01-01
            • 2012-03-04
            • 1970-01-01
            • 2014-01-22
            相关资源
            最近更新 更多