给定一个带有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]]
但是不是可以做得更干净一点吗?
答案 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之间进行转换。