修剪多路树 - 什么是更好的解决方案?

时间:2013-08-14 20:37:41

标签: map f# tree mapreduce multiway-tree

我想知道是否有人可以针对以下问题为我的代码提供更简化的解决方案或改进。

假设我们有一个树枝,树枝深度“d”我们想修剪这棵树,这样我们就可以在深度保留“n”枝条 d 然后在深度 d-1 的另外n个分支;然后另一个 n 分支在 d-2 等......

在我的解决方案中,我需要使用dictionary来跟踪分支数量和ref变量深度以跟踪和降低深度级别< / p>

非常有兴趣了解更简单,更优雅的解决方案,或任何改善我所拥有的提示/技巧

数据结构

type MultiTree = | MNode of int * list<MultiTree>

测试树

let Mtree2 = MNode (0,
                    [MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])]);
                    MNode (1,[MNode (2,[MNode (3,[])])])])

修剪树功能

let trimTree noLosses depth t=
    let mlimit = ref depth
    let dict = Dictionary()

    let fn k =
        match dict.TryGetValue(k) with
        | true, l -> dict.[k] <- l + 1
        |_ -> dict.Add(k,1)
        if dict.[k] >= noLosses then mlimit := !mlimit - 1 else mlimit := !mlimit

    let rec loop d t  = 
        match t with
            | MNode(i,sub) when d > !mlimit ->  
                fn !mlimit      
                MNode(i, List.map (loop (d+1)) [])
            | MNode(i,sub) -> MNode(i, List.map (loop (d+1)) sub)
    loop 1  t


let Mtree2trimmed = Mtree2 |> trimTree 3 2

结果

使用Thomas P的“printTree”功能,可以很好地显示

let printt tree =
let rec loop depth (MNode(n, sub)) =
    printfn "%s %A" depth n
    for s in sub do loop (depth + "  ") s
loop "" tree

输出 -

printt Mtree2

 0
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3
   1
     2
       3

输出 -

printt Mtree2trimmed

 0
   1
     2
   1
     2
   1
     2
   1
   1
   1

所以,如果你已经做到这一点 - 任何建议?

干杯!

1 个答案:

答案 0 :(得分:1)

此问题称为。基本方法如下:

  1. 使用补充数据结构标记您的数据;
  2. 基于补充数据的处理;
  3. 删除那些不应该在最终结果中的项目;此外,消除不再需要的补充数据。
  4. 让我们用补充值标记每个分支:

    • depth0 = topmost branch)
    • sequential(针对每个子树,以0开头)

    为了实现这一点,我更改了树的定义,使其具有通用性:

    type MultiTree<'T> = | MNode of 'T * list<MultiTree<'T>>
    

    现在:

    let rec mapper depth sequential = function
        | MNode(value, sub) ->
            MNode( (depth, sequential, value),
                   (sub |> List.mapi (fun i value -> mapper (depth+1) i value))
                 )
    
    let tree1 = MNode (0,
                        [MNode (1,[MNode (2,[])]);
                        MNode (3,[]);])
    
    printfn "Original data"
    tree1 |> printt
    printfn "Mapped data"
    tree1 |> mapper 0 0 |> printt
    

    结果将是:

    Original data
     0
       1
         2
       3
    Mapped data
     (0, 0, 0)
       (1, 0, 1)
         (2, 0, 2)
       (1, 1, 3)
    

    现在,当数据被标记时,我们可以应用我们想要的任何过滤器。这里有三个例子,加上一个更大的树来展示所有可能性:

    // Take only first n branches at every level
    let rec filter1 n = function
        // first subtrees pass (up to "noLosses" count)
        | MNode( (depth, sequential, value), sub)
            when sequential < n
                                -> Some(MNode(value, List.choose (filter1 n) sub))
        // the rest are skipped
        | _                     -> None
    
    // Take "d" levels of branches unchanged, at higher levels take only second branch
    let rec filter2 d = function
        | MNode( (depth, sequential, value), sub)
            when depth < d      // lower depth - pass
    
            || sequential = 1   // at higher levels, take only the second branch (=1)
                                -> Some(MNode(value, List.choose (filter2 d) sub))
        // the rest are skipped
        | _                     -> None
    
    // Take only first n branches at every level;
    // "n" is a list to identify maximal element at each level
    let rec filter3 ns ts =
        match ns, ts with
        // Empty "noLosse" list -> no value
        | [], _                      -> None
        // if the sequential order of Tree branch is less than
        // the corresponding value in "ns" list, let the value pass the filter
        | nsHead :: nsTail, MNode((_, sequential, value), sub)
            when sequential < nsHead -> Some(MNode(value, List.choose (filter3 nsTail) sub))
        // the rest are skipped
        | _, _                       -> None
    
    printfn "Original data"
    tree2 |> printt
    printfn "Filter1 applied"
    tree2 |> mapper 0 0 |> filter1 2 |> Option.iter printt
    printfn "Filter2 applied"
    tree2 |> mapper 0 0 |> filter2 2 |> Option.iter printt
    printfn "Filter3 applied"
    tree2 |> mapper 0 0 |> filter3 [4; 4; 2] |> Option.iter printt
    

    注意,我们需要Option.iter,因为过滤器可能会返回None值。

    输出:

    Original data
     1
       11
         111
           1111
           1112
           1113
       12
         121
           1211
         122
           1221
           1222
           1223
         123
         124
       13
         131
           1211
       14
         141
           1411
       15
         151
           1511
       16
         161
           1611
    Filter1 applied
     1
       11
         111
           1111
           1112
       12
         121
           1211
         122
           1221
           1222
    Filter2 applied
     1
       11
       12
         122
           1222
       13
       14
       15
       16
    Filter3 applied
     1
       11
         111
       12
         121
         122
       13
         131
       14
         141
    

    重要的是,这种方法并未针对性能进行优化。它的主要思想是展示如何将任务拆分为一系列较小的任务,以便实际过滤实际上是一个简单的匹配案例。