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; then another n branches at d-2 etc...
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
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
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. The basic approach is the following:
Let's mark each branch with to supplementary values:
depth
(0
=topmost branch)sequential
(for each subtree, starting with0
)In order to accomplish that, I have changed the definition of your tree so that it was generic:
And now:
The result would be:
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:
Note, we need
Option.iter
because the filter may returnNone
value.Outputs:
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.