【问题标题】:F# get set of subsets containing k elementsF#获取包含k个元素的子集
【发布时间】:2017-11-10 19:34:03
【问题描述】:

给定一个带有n 元素{1, 2, 3, ..., n} 的集合,我想声明一个函数,它返回包含具有k 元素数量的集合,例如:

allSubsets 3 2

将返回 [[1;2];[1;3];[2;3]],因为这些是由 1 .. n 创建的集合中具有 2 元素的集合

我已经创建了初始的 create-a-set-part,但我对如何找出其中包含 k 元素的所有子集有点困惑。

let allSubsets n k =
    Set.ofList [1..n] |> 

更新:

我设法使用 yield 获得了一个可行的解决方案:

let allSubsets n k =
let setN = Set.ofList [1..n] 
let rec subsets s = 
    set [ 
        if Set.count s = k then yield s
        for e in s do
            yield! subsets (Set.remove e s) ]
subsets setN

allSubsets 3 2
val it : Set<Set<int>> = set [set [1; 2]; set [1; 3]; set [2; 3]]

但不是可以做得更干净一点吗?

【问题讨论】:

    标签: f#


    【解决方案1】:

    你拥有的东西很干净,但效率也很低。尝试运行allSubsets 10 3,你就会明白我的意思了。

    这是我想出的:

    let input = Set.ofList [ 1 .. 15 ]
    
    let subsets (size:int) (input: Set<'a>) = 
        let rec inner elems = 
            match elems with
            | [] -> [[]]
            | h::t -> 
                List.fold (fun acc e -> 
                    if List.length e < size then 
                        (h::e)::e::acc 
                    else e::acc) [] (inner t)
        inner (Set.toList input)
        |> Seq.choose (fun subset ->
            if List.length subset = size then
                Some <| Set.ofList subset
            else None)
        |> Set.ofSeq
    
    subsets 3 input
    

    inner 递归函数是从here 修改后的幂集函数。我的第一个预感是生成幂集,然后对其进行过滤,这将非常优雅,但事实证明这也相当低效。

    如果这是生产质量代码,我会考虑生成给定长度的索引列表,并使用它们来索引输入数组。例如,这就是 FsCheck 生成子集的方式。

    【讨论】:

      【解决方案2】:

      你可以计算powerset然后过滤,以便只得到具有指定长度的那些“:

      let powerset n k =
          let lst = Set.toList n     
          seq [0..(lst.Length |> pown 2)-1] 
              |> Seq.map (fun i -> 
                  set ([0..lst.Length-1] |> Seq.choose (fun x -> 
                      if i &&& (pown 2 x) = 0 then None else Some lst.[x])))
          |> Seq.filter (Seq.length >> (=) k)
      

      然而,这对于 k 接近于 n 的大集合 (n) 来说效率不高。但是很容易优化,需要根据每个数字的二进制表示的位数提前过滤掉。

      【讨论】:

        【解决方案3】:

        这个函数实现了流行的n-choose-k函数:

        let n_choose_k (arr: 'a []) (k: int) : 'a list list =
            let len = Array.length arr
            let rec choose lo x =
                match x with
                | 0 -> [[]]
                | i -> [ for j in lo..(len-1) do
                           for ks in choose (j+1) (i-1) do
                               yield arr.[j]::ks ]
            choose 0 k
        
        > n_choose_k [|1..3|] 2;;
        val it : int list list = [[1; 2]; [1; 3]; [2; 3]]
        

        您可以使用 Set.toArray 和 Set.ofList 与 Set 相互转换。

        【讨论】:

          【解决方案4】:

          您可以考虑以下方法:

          1. 获取电源设置
              let rec powerset xs =
              match xs with
              | [] -> [ [] ]
              | h :: t -> List.fold (fun ys s -> (h :: s) :: s :: ys) [] (powerset t)
          
          1. 使用必要数量的元素过滤所有子集
              let filtered xs k = List.filter (fun (x: 'a list) -> x.Length = k) xs
          
          1. 终于得到请求的所有子集
              let allSubsets n k = Set.ofList (List.map (fun xs -> Set.ofList xs) (filtered (powerset [ 1 .. n ]) k))
          

          只是为了检查和玩你可以使用:

              printfn "%A" (allSubsets 3 2) // set [ set [1; 2]; set [1; 3]; set [2; 3] ]
          

          【讨论】:

            猜你喜欢
            • 2011-11-14
            • 2012-12-01
            • 2019-09-19
            • 2011-10-28
            • 2013-12-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-08-03
            相关资源
            最近更新 更多