用于获取F#中的一组功率集的功能

时间:2015-09-28 18:49:56

标签: f#

我正在尝试用F#编写一个函数来获取一组的powersets。到目前为止,我写了:

let rec powerset = function
    |[] -> [[]]
    | [x] -> [[x]; []]
    |x::xs -> [x] :: (List.map (fun n -> [x; n]) xs) @ powerset xs;;

但这不会返回包含3个或更多元素的情况,只返回对,单个元素和空集。

1 个答案:

答案 0 :(得分:3)

你走在正确的轨道上,这是一个有效的解决方案:

let rec powerset = 
   function
   | [] -> [[]]
   | (x::xs) -> 
      let xss = powerset xs 
      List.map (fun xs' -> x::xs') xss @ xss

看到你只需要使用这个技巧:

  

对于每个元素x,您有一半的powerset元素将包含x,而一半不会

所以你递归地生成其余元素xss的powerset,然后将这两个部分连接起来(List.map (fun xs' -> x::xs') xssx添加到每个部分之前)

但是请注意,这不是尾递归的,并且会为更大的列表填充堆栈 - 您可以采用这个想法并尝试使用seq实现它,或者如果您愿意,可以创建一个尾递归版本

使用seq

这是一个在自然数的二进制表示(这些的子集)和集合的子集之间使用seq和双射的版本(将元素映射到数字并设置为1相应的元素在子集中,如果不是,则为0):

let powerset (xs : 'a seq) : 'a seq seq =
    let digits (n : bigint) : bool seq =
        Seq.unfold (fun n -> 
            if n <= 0I 
            then None 
            else Some (n &&& 1I = 1I, n >>> 1))
            n
    let subsetBy (i : bigint) : 'a seq =
        Seq.zip xs (digits i)
        |> Seq.choose (fun (x,b) -> if b then Some x else None)
    seq { 0I .. 2I**(Seq.length xs)-1I }
    |> Seq.map subsetBy

这适用于像powerset [1..100]这样的事情,但可能需要很长时间才能枚举它们;)(但它不应该占用太多内存......)