Related
I have a quadTree type defined by that :
type 'a quadtree =
| Empty
| Leaf of 'a
| Node of 'a quadtree * 'a quadtree * 'a quadtree * 'a quadtree;;
Rooms defined by
type room = {
n : bool;
e : bool;
s : bool;
w : bool;
ps : coord;
exit : bool
}
Coordinates defined by
type coord = {
x : int;
y : int;
}
So TLDR of all that, I have a Quadtree of rooms that have or don't have exits up, down, left and right.
The objective now is to create a function that will find a way (if it exists) from one room to another (from its coordinates), the problem is that I don't see how to do it in OCaml...
Anyway, thanks for your time, have a good day.
Edit :
To clarify, I am the one defining the types and can alter them if needed.
Also, I tried implementing Dijkstra's algorithm (from Wikipedia's pseudo code), but being quite unfamiliar with both graphs, and OCaml's arrays and lists. To be precise, my problem -I think- comes from the fact that I'm not able to modify variables in a function, so for instance in Wikipedia's pseudo code, in this line:
u ← Q.extract_min() // Remove and return best vertex
I see how to remove the best vertex, and I see how to return it, but not both at the same time.
Or, here:
for each neighbor v of u: // where v is still in Q.
alt ← dist[u] + length(u, v)
if alt < dist[v]: // A shorter path to v has been found
dist[v] ← alt
prev[v] ← u
How do I modify dist and prev outside of the 'for' loop? Can I use a for loop or is it simpler / better to use a recursive function?
Also I should make clear that the maze is "directional", meaning that being able to go from room A to room B does not mean you'll be able to go from room B to room A.
Edit 2 :
I should have clarified this in the beginning, sorry :
The quadtree follows this rule :
| Node of North West * North East * South West * South East
Edit 3 :
Okay change of plan, turns out I was doing things very stupidly. I don't need to find the way to a certain room, just to an exit. So I tried this :
let rec contains_exit = function
| [] -> false
| e::l' when (getCell e.x e.y maze).exit -> true
| e::l' when (getCell e.x e.y maze).exit = false -> contains_exit l'
;;
let rec find_exit start way =
if is_exit start then
way
else
(let a = find_exit (northp start) way#[start] in
if contains_exit a then
way
else
(
let b = find_exit (eastp start) way#[start] in
if contains_exit b then
way
else
(
let c = find_exit (southp start) way#[start] in
if contains_exit c then
way
else
(
let d = find_exit (westp start) way#[start] in
if contains_exit d then
way
else
way
)
)
)
)
;;
But it gives me a stack overflow. After a bit of research, it seems that the line "contains_exit a" is never true, so the way is never returned and it loops !
Any idea why that is ? Is the problem my contains_exit function ?
Edit 4 :
Ended up doing this function :
let rec find_exit start way =
sleep 50000000;
let r = (Random.int 180) in
set_color (rgb r r r);
fill_rect (start.x * sizeCell + doorWidth * 2) (start.y * sizeCell + doorWidth * 2) (sizeCell - 4 * doorWidth) (sizeCell - 4 * doorWidth);
if is_exit start then
way#[start]
else
(let a = if (getCell start.x start.y maze).n && ((mem (northp start) way) = false) then find_exit (northp start) way#[start] else [] in
if a != [] then
a
else
(
let b = if (getCell start.x start.y maze).e && ((mem (eastp start) way) = false) then find_exit (eastp start) way#[start] else [] in
if b != [] then
b
else
(
let c = if (getCell start.x start.y maze).w && ((mem (westp start) way) = false) then find_exit (westp start) way#[start] else [] in
if c != [] then
c
else
(
let d = if (getCell start.x start.y maze).s && ((mem (southp start) way) = false) then find_exit (southp start) way#[start] else [] in
if d != [] then
d
else
[]
)
)
)
)
;;
it sometimes works... But other times it blocks and it goes from one room to the one below then up again then down again... I don't understand why !?
If you want to try the whole program, here it is : link
Then you can go for some thing like this:
type 'a quadtree =
| Empty
| Leaf of 'a
| Node of 'a * 'a quadtree * 'a quadtree * 'a quadtree * 'a quadtree;;
type room = {
n : bool;
e : bool;
s : bool;
w : bool;
ps : coord;
exit : bool
};;
type coord = {
x : int;
y : int;
};;
let rec treeForRoom(tree, room) =
match tree with
| Empty -> Empty
| Leaf l -> if l.ps == room.ps then l else Empty
| Node (r, n, e, s, w) as node ->
if r == room
then node
else
match ((r.ps.x - room.ps.x), (r.ps.y - room.ps.y)) with
| (0, n) -> if n > 0 then treeForRoom(w) else treeForRoom(e)
| (n, 0) -> if n > 0 then treeForRoom(s) else treeForRoom(n)
(* Assuming the root of the tree is the room we start from *)
let rec searchPath(tree, r) =
match tree with
| Empty -> (false, 0, [])
| Leaf l -> if l == r then (true, 0) else (false, 0, [])
| Node (r, n, e, s, w) as node ->
let pn = searchPath(n, r)
and pe = searchPath(e, r)
and ps = searchPath(s, r)
and pw = searchPath(w, r)
in
find_best_path(p1, p2, p3, p4)
let find_best_path(p1, p2, p3, p4) =
match (p1, p2, p3, p4) with
| ((false,_,_), (false,_,_), (false,_,_), (false,_,_)) -> (false, -1, [])
| ((true, w, p), (false,_,_), (false,_,_), (false,_,_)) -> (true, w, p)
| ((false,_,_), (true, w, p)), (false,_,_), (false,_,_)) -> (true, w, p)
| ((false,_,_), (false,_,_), (true, w, p)), (false,_,_)) -> (true, w, p)
| ((false,_,_), (false,_,_), (false,_,_),(true, w, p)) -> (true, w, p)
| ((p1ok, p1w, p1p), (p2ok, p2w, p2p),(p3ok, p3w, p3p),(p4ok, p4w, p4p)) ->
if p1ok && p2ok && p3ok && p4ok
then
min_weight([(p1ok, p1w, p1p), (p2ok, p2w, p2p),(p3ok, p3w, p3p),(p4ok, p4w, p4p)])
else
....
let rec min_weight(l) =
match l with
| [] -> (false, -1, [])
| [t] -> t
| [(to, tw, tp) as t::q] -> let (mo, mw, mp) as minw = min_weight(q) in
if tw < mw
then
t
else
minw
I added the root to the type definition ('a* ...) so I can make a function to find the good tree to go through. I also assume that the tree respect the following rule: (root, north room, east room, south room, west room) for each node (you can make an add function to ensure this property).
Then you go through the tree exploring from the end and getting the minimal weight path for then end to the start point. (It is the same weight as it goes through the same paths under the same conditions (cause you explore the tree from the start but compute the path from then end)).
This code does not take into account the possibility to pass through doors but it is a just a check to add as the way of going through the tree is already correctly oriented.
I let you complete and correct the code.
Hope it will help you.
Right now I have a binary tree modeled in SMLNJ however I want to change this model to where there is a tree inside of tree inside of tree. (Ternary Tree)
If you begin with the root of a binary tree.
Binode with an (id1*(binode)) ß- a binary tree
Then if that “binode” is located as part of a tuple: 2Dbinode
That tuple has an identifier and that binode with is the root of the binode tree.
2Dbinode (id2* (binode) *2Dbinode)
Each of those in turn are part of a 3Dbinode which consists of:
3Dbinode(id3 * (2Dbinode) * 3Dbinode)
For ex. 3Dbinode(id3 * (2Dbinode) * 3Dbinode) the root 3Dbinode might contain the following data:
(25, (7, (11)))
And by adding the nodes (25, (7, (22)))
(25, (10, (4))), (30, (7, (22)))
3DBinary Tree Model
Here is SMLNJ code for the 2D binary tree I am modifying.
datatype btree = Empty | Node of int * btree * btree;
fun AddNode (i:int, Empty) = Node(i, Empty, Empty) |
AddNode(i:int, Node(j, left, right)) =
if i = j then Node(i, left, right)
else if i < j then Node(j, AddNode(i, left), right)
else Node(j, left, AddNode(i, right));
fun printInorder Empty = () |
printInorder (Node(i,left,right)) =
(printInorder left; print(Int.toString i ^ " "); printInorder right);
val x : btree = AddNode(50, Empty);
val x : btree = AddNode(75, x);
val x : btree = AddNode(25, x);
val x : btree = AddNode(72, x);
val x : btree = AddNode(20, x);
val x : btree = AddNode(100, x);
val x : btree = AddNode(3, x);
val x : btree = AddNode(36, x);
val x : btree = AddNode(17, x);
val x : btree = AddNode(87, x);
printInorder(x);
I need to populate the data structure with N randomized (3Dnodes):
How can I implement these functions?
Search for a specific node displaying the path to the node ex: (25, 10, 4) the display would be
(25)
(25, 7)
(25, 10)
(25, 10,4)
If the user searches for a node that does not exist:
(30, (7, (30))) then the path displayed would be
(25)
(30)
(30,7)
(30, 7, 30) NOT FOUND
If the user wishes to ADD a node at any level they should be prompted to enter the 3 digit code for that node; Again the path should be displayed.
EX: ADD (30, 11, 5) then the display would be
(25)
(30)
(30, 7)
(30, 11) created
(30, 11, 5) created
Print out the contents of the 3dbinode tree as a sequence of (A,B,C)
DELETE a node
Delete a node: EX: DEL (30, 7, _)
Then the result would be
- (30, 7, 22) deleted
- (30, 7, 0) created
Ternary tree?
Edit 2: After you reviewed your question, it still seems unclear how you wish to navigate this ternary tree when searching it. A binary search tree divides its elements left and right depending on which one is greater. You have described no similar criterion: When should your functions use the first, second and third branch?
Edit 3: I've provided the function pathExists that works on ternary trees, but AddNode is still missing as you have not provided any insight into the questions that I've highlighted with bold. If your tree really does serve the purpose of containing points in a 3-dimensional space, it does sound like you want a k-d tree, as I suggested once. I've also partially provided the function make3DTree under the assumption that a k-d tree is what you're looking for.
Until I saw the drawing you made of a ternary tree (or generally an n-ary tree), I could not understand your question. A ternary tree simply has (up to) three child nodes at each level instead of the binary tree's two.
The wording "tree inside of a tree inside of a tree" means something different than the branching factor (2, 3, n). Exactly how you turn the triples (25, 7, 11), (25, 7, 22), (25, 10, 4), and (30, 7, 22) into your ternary tree is still a little puzzling to me. It seems that the bottom nodes only have two empty leaves. I will interpret this as if there were a third empty arrow in the middle.
Your AddNode function constructs binary search trees where the smaller elements go to the left, and the larger elements to the right. But how, then, do you wish to use a third branch?
Compare the following generic / integer-specific binary tree datatype definitions,
datatype 'a btree = BTreeEmpty | BTreeNode of 'a * 'a btree * 'a btree
datatype int_btree = BTreeEmpty | BtreeNode of 'a * int_btree * int_btree
with the ones for ternary trees,
datatype 'a ttree = TTreeEmpty | TTreeNode of 'a * 'a ttree * 'a ttree * 'a ttree
datatype int_ttree = TTreeEmpty | TtreeNode of 'a * int_ttree * int_ttree * int_ttree
or even ones that support variable branching in each node,
datatype 'a tree = TreeEmpty | TreeNode of 'a * 'a tree list
datatype int_tree = TreeEmpty | TreeNode of int * int_tree list
Creating the ternary tree you depicted,
val treeModel =
let val t = TTreeNode
val e = TTreeEmpty
in
t (25,
e,
t (7,
e,
t (11, e, e, e),
t (10,
e,
t (4, e, e, e),
e
)
),
t (30,
e,
t (7,
e,
t (22, e, e, e),
e
),
e)
)
end
although it would probably be more convenient with a function like AddNode, only you need to specify a consistent way for elements to be added to this kind of tree. How does the binary search tree logic translate to ternary trees?
Determining if a path exists in a ternary tree
You can determine if a path to a node exists. Since a tree can have any depth, a path can have any length and should be represented with a list. The path [25, 7, 10, 4] exists in your ternary tree, for example.
fun pathExists [] _ = true (* the empty path is trivially found *)
| pathExists _ TTreeEmpty = false (* no non-empty path goes through an empty tree *)
| pathExists (x::xs) (TTreeNode (y, subtree1, subtree2, subtree3)) =
x = y andalso
(pathExists xs subtree1 orelse
pathExists xs subtree2 orelse
pathExists xs subtree3)
Testing this function with treeModel above:
- pathExists [25, 7, 10, 4] treeModel;
> val it = true : bool
- pathExists [25, 30, 7, 22] treeModel;
> val it = true : bool
- pathExists [25, 7, 11, 9] treeModel;
> val it = false : bool
- pathExists [25, 7, 9] treeModel;
> val it = false : bool
Inserting a point in a ternary tree [???]
A template for this function could be
fun AddNode (x, TTreeEmpty) = TTreeNode (x, TTreeEmpty, TTreeEmpty, TTreeEmpty)
| AddNode (x, TTreeNode (y, subtree1, subtree2, subtree3))) = ???
but if x <> y, which of the three subtrees should it try to add x to?
K-d tree?
Edit 1: After having answered, I realize that perhaps you are looking for a k-d tree? A k-d tree can store k-dimensional vectors, using only binary trees, in a way that allows efficient, locality-specific lookups.
The great trick here is to say that the 1st level of the tree divides the space into two halves on the X-axis, the 2nd level of the tree divides the left/right halves into halves on the Y-axis, the 3rd level of the tree divides the left/right halves into halves on the Z-axis, the 4th level on the X-axis again, the 5th level on the Y-axis again, and so on.
Here is an initial translation of that pseudocode for k = 3 into Standard ML:
(* 'byDimension dim (p1, p2)' determines if p1 is greater than p2 in dimension dim. *)
fun byDimension 0 ((x1,_,_), (x2,_,_)) = x1 > x2
| byDimension 1 ((_,y1,_), (_,y2,_)) = y1 > y2
| byDimension 2 ((_,_,z1), (_,_,z2)) = z1 > z2
| byDimension d _ _ = raise Fail ("Invalid dimension " ^ Int.toString d)
(* split points into two halves and isolate the middle element *)
fun splitAt dim points = ...
(* The number of dimensions, matching the arity of the point tuples below *)
val k = 3
fun make3DTree ([], _) = BTreeEmpty
| make3DTree (points, depth) =
let val axis = depth mod k
val len = List.length points
val points_sorted = ListMergeSort.sort (byDimension axis) points
val (points_left, median, points_right) = splitAt len points_sorted
in BTreeNode (median,
make3DTree (points_left, depth+1),
make3DTree (points_right, depth+1))
end
One optimization could be to reduce the constant re-sorting.
Adding a single 3D point to this tree is well-defined, but does not guarantee that the tree is particularly balanced:
(* get the (n mod 3)-th value of a 3-tuple. *)
fun getDimValue (n, (x,y,z)) =
let val m = n mod k
in if m = 0 then x else
if m = 1 then y else
if m = 2 then z else
raise Fail ("Invalid dimension " ^ Int.toString n)
end
(* the smallest tree that contains a k-dimensional point
* has depth k-1 (because of 0-indexing). *)
fun deepEnough depth = depth >= k-1
fun insertNode (point, BTreeEmpty, depth) =
let val v1 = getDimValue (depth, point)
val v2 = getDimValue (depth+1, point)
val (left, right) =
if deepEnough depth
then (BTreeEmpty, BTreeEmpty)
else if v1 > v2
then (insertNode (point, BTreeEmpty, depth+1), BTreeEmpty)
else (BTreeEmpty, insertNode (point, BTreeEmpty, depth+1))
in BTreeNode (v1, left, right)
end
| insertNode (point, BTreeNode (v1, left, right), depth) =
let val v2 = getDimValue (depth, point)
in if v1 > v2
then BTreeNode (v1, insertNode (point, left, depth+1), right)
else BTreeNode (v1, left, insertNode (point, right, depth+1))
end
Determining if a point exists in this tree can be achieved by performing a nearest-neighbor search, which is slightly less trivial to implement.
I want to make function maptree with standard ML.
If function f(x) = x + 1;
then
maptree(f, NODE(NODE(LEAF 1,LEAF 2),LEAF 3));
should make result
NODE(NODE(LEAF 2,LEAF 3),LEAF 4))
I write the code like below.
datatype 'a tree = LEAF of 'a | NODE of 'a tree * 'a tree;
fun f(x) = x + 1;
fun maptree(f, NODE(X, Y)) = NODE(maptree(f, X), maptree(f, Y))
| maptree(f, LEAF(X)) = LEAF(f X);
but when I execute this code like this
maptree(f, (NODE(NODE(LEAF 1,LEAF 2),LEAF 3)));
result is not I want to
(NODE(NODE(LEAF 2,LEAF 3),LEAF 4)))
but
NODE(NODE(LEAF #,LEAF #),LEAF 4)).
Why this happened(not a number but #)?
# is used by the REPL when the data structure it prints is deeper than a pre-set value. If you increase that value, you'll get the result you excepted. I assume you're using SML/NJ, which calls that setting print.depth:
sml -Cprint.depth=20
- maptree(f, (NODE(NODE(LEAF 1,LEAF 2),LEAF 3)));
val it = NODE (NODE (LEAF 2,LEAF 3),LEAF 4) : int tree
You can find more options like these by executing sml -H. Look them up under the "compiler print settings" section:
compiler print settings:
print.depth (max print depth)
print.length (max print length)
print.string-depth (max string print depth)
print.intinf-depth (max IntInf.int print depth)
print.loop (print loop)
print.signatures (max signature expansion depth)
print.opens (print `open')
print.linewidth (line-width hint for pretty printer)
Some comments:
I would probably go with the definition
datatype 'a tree = Leaf | Node of 'a tree * 'a * 'a tree
so that trees with zero or two elements can also be expressed.
I would probably curry the tree map function
fun treemap f Leaf = Leaf
| treemap f (Node (l, x, r)) = Node (treemap f l, x, treemap f r)
since you can then partially apply it, e.g. like:
(* 'abstree t' returns t where all numbers are made positive *)
val abstree = treemap Int.abs
(* 'makeFullTree n' returns a full binary tree of size n *)
fun makeFullTree 0 = Leaf
| makeFullTree n =
let val subtree = makeFullTree (n-1)
in Node (subtree, n, subtree)
end
(* 'treetree t' makes an int tree into a tree of full trees! *)
val treetree = treemap makeFullTree
You may at some point want to fold a tree, too.
According to AVL tree wiki
The balance factor is calculated as follows: balanceFactor = height(left-subtree) - height(right-subtree). For each node checked, if the balance factor remains −1, 0, or +1 then no rotations are necessary.
However, in OCaml, it seems it uses balance factor of 2
let bal l x d r =
let hl = match l with Empty -> 0 | Node(_,_,_,_,h) -> h in
let hr = match r with Empty -> 0 | Node(_,_,_,_,h) -> h in
if hl > hr + 2 then begin
match l with
Empty -> invalid_arg "Map.bal"
| Node(ll, lv, ld, lr, _) ->
if height ll >= height lr then
create ll lv ld (create lr x d r)
else begin
match lr with
Empty -> invalid_arg "Map.bal"
| Node(lrl, lrv, lrd, lrr, _)->
create (create ll lv ld lrl) lrv lrd (create lrr x d r)
end
end else if hr > hl + 2 then begin
match r with
Empty -> invalid_arg "Map.bal"
| Node(rl, rv, rd, rr, _) ->
if height rr >= height rl then
create (create l x d rl) rv rd rr
else begin
match rl with
Empty -> invalid_arg "Map.bal"
| Node(rll, rlv, rld, rlr, _) ->
create (create l x d rll) rlv rld (create rlr rv rd rr)
end
end else
Node(l, x, d, r, (if hl >= hr then hl + 1 else hr + 1))
Why?
In AVL trees you can see the maximal height difference as a tweakable parameter. They must have chosen 2 to tradeoff between the rebalancing cost on insertion/removal and the lookup cost.
Since you seem to be interested in these things I suggest you have a look at this this paper that has formal proof of correctness of OCaml's Set's module which uses the same AVL tree, by doing do so they actually did find an error in the rebalancing scheme... Also while not strictly equivalent implementation wise, I learned quite a lot from this this paper.
Where let's say:
datatype bin_tree = Empty |
Node of value * bin_tree * bin_tree
How would I go about filling a binary tree (not a binary search tree where left is smaller than root and right bigger). Just values from a list inserted at each node in a binary tree.
You use the value constructors you've declared.
If we assume for a moment that value is int instead, then we for instance have that the tree
1
/ \
2 4
/
3
is represented by:
Node (1,
Node (2,
Node (3, Empty, Empty),
Empty
),
Node (4, Empty, Empty)
)
Or, equivalently, on one line:
Node (1, Node (2, Node (3, Empty, Empty), Empty), Node (4, Empty, Empty))
It's not really possible to help you, without knowing more about how you wan't your tree constructed from a given list. However here is an example that creates a balanced tree. It takes the first element and uses it as the node value, and then it splits the rest of the list into two sub lists of equal size (if possible), by taking all "even" element in the "left" list and all "odd" elements in the "right" list:
datatype 'a bin_tree = Empty
| Node of 'a * 'a bin_tree * 'a bin_tree
fun list_split xs =
let
fun loop [] (left, right) = (rev left, rev right)
| loop (x::y::xs) (left, right) = loop xs (x :: left, y :: right)
| loop (x :: xs) (left, right) = loop xs (x :: left, right)
in
loop xs ([], [])
end
fun built_tree [] = Empty
| built_tree (x :: xs) =
let
val (left, right) = list_split xs
val left_tree = built_tree left
val right_tree = built_tree right
in
Node (x, left_tree, right_tree)
end
The result:
- built_tree [1,2,3,4,5,6,7,8,9];
val it =
Node
(1,Node (2,Node (4,Node (8,Empty,Empty),Empty),Node (6,Empty,Empty)),
Node (3,Node (5,Node (9,Empty,Empty),Empty),Node (7,Empty,Empty)))
: int bin_tree
Here is an answer to the same question done in Java. This will probably help a good bit :).