[英]Trim a Multiway Tree - what is a better solution?
I was wondering whether anyone could offer a more simplified solution or improvements to my code for the following problem. 我想知道是否有人可以为以下问题提供更简化的解决方案或对我的代码进行改进。
Say we have a tree with branches going to some depth "d" And we would like to trim this tree, such that we preserve "n" branches at depth d and then another n branches at depth d-1 ; 假设我们有一棵树,其分支的深度达到“ d”,并且我们想要修剪这棵树,这样我们就可以在深度d保留“ n”个分支,然后在深度d-1保留另一个n分支; then another n branches at d-2 etc... 然后在d-2处又有n个分支,等等。
In my solution I needed to resort to a dictionary
to keep track of the number of branches and ref
variable depth to keep track of and reducing the depth level 在我的解决方案中,我需要借助dictionary
来跟踪分支的数量,并ref
可变深度来跟踪并降低深度级别
Would be very interested to know a simpler, more elegant solution, or any hints / tips to improve what I have 知道一个更简单,更优雅的解决方案,或改善我所拥有的任何提示/技巧将非常有兴趣
Data Structure 数据结构
type MultiTree = | MNode of int * list<MultiTree>
Test Tree 测试树
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,[])])])])
Trim Tree Function 修剪树功能
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
Result 结果
Using Thomas P's "printTree" function this is nicely visualized 使用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
Output - 输出-
printt Mtree2
0
1
2
3
1
2
3
1
2
3
1
2
3
1
2
3
1
2
3
Output - 输出-
printt Mtree2trimmed
0
1
2
1
2
1
2
1
1
1
So if you've made it this far - any advice? 因此,如果您已经做到了这一点-有什么建议吗?
Cheers! 干杯!
This problem is called mapreduce . 这个问题称为mapreduce 。 The basic approach is the following: 基本方法如下:
Let's mark each branch with to supplementary values: 让我们用补充值标记每个分支:
depth
( 0
=topmost branch) depth
( 0
=最上面的分支) sequential
(for each subtree, starting with 0
) sequential
(对于每个子树,从0
开始) In order to accomplish that, I have changed the definition of your tree so that it was generic: 为了实现这一点,我更改了树的定义,使其具有通用性:
type MultiTree<'T> = | MNode of 'T * list<MultiTree<'T>>
And now: 现在:
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
The result would be: 结果将是:
Original data
0
1
2
3
Mapped data
(0, 0, 0)
(1, 0, 1)
(2, 0, 2)
(1, 1, 3)
Now, when the data is marked, we may apply whatever filter we want. 现在,在标记数据后,我们可以应用所需的任何过滤器。 Here are three examples, plus a bit larger tree to demonstrate all possibilities: 这是三个示例,外加一棵更大的树来演示所有可能性:
// 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
Note, we need Option.iter
because the filter may return None
value. 注意,我们需要Option.iter
因为过滤器可能返回None
值。
Outputs: 输出:
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
An important note, this approach is not optimized for performance. 需要注意的是,这种方法并未针对性能进行优化。 Its primary idea is to show how to split the task into a sequence of smaller tasks so that the actual filtering is literally a simple match case. 其主要思想是展示如何将任务分解为一系列较小的任务,以便实际过滤实际上是一个简单的匹配情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.