F#获取包含k个元素的子集

时间:2017-11-10 19:34:03

标签: f#

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

allSubsets 3 2

将返回[[1;2];[1;3];[2;3]],因为这些是由2

创建的集合中包含1 .. n个元素的集合

我已经创建了初始的创建集部分,但我对如何查找其中包含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]]

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

3 个答案:

答案 0 :(得分:1)

你拥有的很干净,但效率也很低。尝试运行2017-11-11 14:21:27 LOG: 192.168.0.1 - Administrator:Test54 2017-11-11 14:21:28 LOG: 192.168.0.1 - Administrator:Test55 2017-11-11 14:21:29 LOG: 192.168.0.1 - Administrator:Test56 2017-11-11 14:21:30 LOG: 192.168.0.1 - Administrator:Test57 2017-11-11 14:21:31 LOG: 192.168.0.1 - Administrator:Test58 2017-11-11 14:21:32 LOG: 192.168.0.1 - Administrator:Test59 2017-11-11 14:21:33 LOG: 192.168.0.1 - Administrator:Test60 2017-11-11 14:21:34 LOG: 192.168.0.1 - Administrator:Test61 2017-11-11 14:21:35 LOG: 192.168.0.1 - Administrator:Test62 2017-11-11 14:21:35 SUCCESS : 192.168.0.1 - Administrator:Test62 ,你就会明白我的意思。

这就是我提出的:

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 递归函数是来自here的修改后的幂集函数。我的第一个预感是产生功率集然后过滤它,这将非常优雅,但事实证明它也是相当低效的。

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

答案 1 :(得分:1)

你可以计算powerset然后过滤,以便只获得具有指定长度的那些&#34;:

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),这是无效的。但它很容易优化,你必须根据每个数字的二进制表示的数字计数提前过滤掉。

答案 2 :(得分:0)

此函数实现了流行的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之间进行转换。