这很有趣。我发现有两种续延:构建续延和处理续延。
无论如何,这与子集和问题非常相似,该问题是NP完全的。因此,可能没有比枚举所有可能性并选择符合标准的可能性更快的算法。
虽然如此,您实际上不需要从生成的组合中构建数据结构。如果每个结果调用一个函数更有效率。
/// Takes some input and a function to receive all the combinations
/// of the input.
/// input: List of any anything
/// iterator: Function to receive the result.
let iterCombinations input iterator =
/// Inner recursive function that does all the work.
/// remaining: The remainder of the input that needs to be processed
/// builder: A continuation that is responsible for building the
/// result list, and passing it to the result function.
/// cont: A normal continuation, just used to make the loop tail
/// recursive.
let rec loop remaining builder cont =
match remaining with
| [] ->
// No more items; Build the final value, and continue with
// queued up work.
builder []
cont()
| (x::xs) ->
// Recursively build the list with (and without) the current item.
loop xs builder <| fun () ->
loop xs (fun ys -> x::ys |> builder) cont
// Start the loop.
loop input iterator id
/// Searches for sub-lists which has a sum close to zero.
let nettedOutTrades tolerance items =
// mutable accumulator list
let result = ref []
iterCombinations items <| function
| [] -> () // ignore the empty list, which is always there
| comb ->
// Check the sum, and add the list to the result if
// it is ok.
let sum = comb |> List.sumBy snd
if abs sum <= tolerance then
result := (List.map fst comb, sum) :: !result
!result
例如:
> [("a",-1); ("b",2); ("c",5); ("d",-3)]
- |> nettedOutTrades 1
- |> printfn "%A"
[(["a"; "b"], 1); (["a"; "c"; "d"], 1); (["a"], -1); (["b"; "d"], -1)]
使用构建器延续而不是累加器的原因是,您可以按照传入的顺序获得结果,而无需将其反转。