Genetic Operators on Binary Tree - java

I am having problems trying to wrap my head around applying genetic operators to binary trees.
Firstly I have methods that generate two types of trees for the initial population, namely Grow (tree of variable size) and Full (balanced same shape and size tree).
FULL GROW
(*) (*)
(+) (-) (5) (-)
(1)(2) (3)(4) (6) (7)
The class for each tree looks like this:
public class Tree<E>{
E element;
Tree<E> left, right;
double rawFit;
int hitRat;
public Tree(E element)
{
this.element=element;
}
public Tree (E element, Tree left, Tree right)
{
this.element = element;
this.left = left;
this.right = right;
}
//MORE
//CODE
}
Now this is where I am having troubles understanding how to implement genetic operators, namely Mutation and Crossover
Randomly selecting a tree from my initial population, how do I go about applying theses genetic operators?
For Mutation:
I need to randomly select a point in a parent tree.
Remove the entire subtree below that selected point.
Generate a new subtree of similar depth to the removed subtree.
Replace it back on the original parent tree and the selected point.
This is now the offspring.
Graphic Depiction:
PARENT
(*)
randomly chosen point --> (+) (-)
(1)(2) (3)(4)
OFFSPRING RANDOM SUBTREE
(*)
(NULL) (-) + (*)
(3) (4) (5) (6)
NEW OFFSPRING
(*)
(*) (-)
(5)(6) (3) (4)
I also need to do something similar for Crossover as well.
It seems easy in theory, but I have no idea how to code this (Java). Any help would be appreciated.
EDIT: The method I've used for generating a full tree looks like this:
private static final String[] OPERATORS = {"+", "-", "/", "*"};
private static final int MAX_OPERAND = 100;
public static Tree full(int depth) {
if (depth > 1) {
String operator = OPERATORS[random.nextInt(OPERATORS.length)];
return new Tree(operator, full(depth - 1), full(depth - 1));
} else {
return new Tree(random.nextInt(MAX_OPERAND) + 1);
}
}

I'll try and explain some of the steps in brief.
Randomly select a point in a parent tree
One way of doing this would be to choose a random number, say k, between 0 and the number of non-leaf elements in the tree. The random point would be the kth element while traversing the tree in order.
Replace the entire subtree below that selected point.
Simply set the subtree to the new generated tree. Something like this:
public class Tree<E> {
public void mutate() {
Tree tree = this.getRandomSubtree();
tree.replace(NEW_RANDOM_TREE);
}
public void replace(Tree<E> newTree) {
if(this.isLeftChild()) this.getParent().setLeft(newTree);
else this.getParent().setRight(newTree);
}
...
}
The getRandomSubtree() method returns a random point in the tree.
The getParent() method of a tree returns the immediate parent node.
Note that you'll also have to check some cases where the random sub-tree returned is the root itself.

Randomly select a point:
Select a Node at Random from Unbalanced Binary Tree
Remove subtree from selected node is not necessary, just get the depth of the selected subtree, and hold reference to it. http://www.geeksforgeeks.org/get-level-of-a-node-in-a-binary-tree/
Use your "full" method to generate a new random subtree with saved depth of old subtree and assign this subtree to your saved reference of old subtree, so the old subtree is killed by garbage collector.

Related

Recursive generator of random algebraic expressions tree issue

Problem description
I am trying to write a math test for my little son.
Such test must generate a list of random algebraic expressions according to certain rules and check the correctness of solution.
In particular, I want to generate expressions consisting strictly of a given number of operators that are selected from a certain list.
For example generate a list of expression consisting of 3 operators of addition and subtraction in random order like:
12 - (5 + 2) + 2
3 + 4 - 2 + 10
and so on
To represent and calculate the expression, I use the binary expression tree structure.
Each tree consists of either a Leaf or a Node that contains an Operator and two subtrees.
This is a simple recursive structure and I want to work with it only recursively.
No setters in the classes of the tree. I can only use constructors to create a tree.
Leaf class
public final class Leaf implements Expression {
private final int value;
public Leaf(int value) {
this.value = value;
}
// ...
}
Node Class
public final class Node implements Expression {
private final Operator operator;
private final Expression left;
private final Expression right;
public Node(#NotNull Operator operator,
#NotNull Expression left,
#NotNull Expression right) {
this.operator = operator;
this.left = left;
this.right = right;
}
// ...
}
And Operator is a simple Enum type. I simplify my classes for the purposes of this question.
My issue
I am trying to build an expression based on the following rules:
There should be at least one operator in the expression, so my tree always starts from the Node.
I choose a random operator from a given list and increase the number of operators used
While this number less than the given number of operators I construct the left and rights subtree for current Node.
The left subtree can be randomly either a Leaf or Node
The right subtree can also be either a Leaf or Node, but if the left subtree is a Leaf and there are still unused operators, then the right must be a Node.
I wrote such an expression builder:
public class SmartExpressionBuilder {
private final Random random = ThreadLocalRandom.current();
private final List<Operator> allowedOperators;
private final int numberOfOperators;
public SmartExpressionBuilder(List<Operator> allowedOperators, int numberOfOperators) {
this.allowedOperators = allowedOperators;
this.numberOfOperators = numberOfOperators;
}
private int operatorsUsed;
public Expression build() {
operatorsUsed = 0;
return helper();
}
private Expression helper() {
if (operatorsUsed == numberOfOperators) return randomLeaf();
Operator op = randomOperator();
Expression left = random.nextBoolean() ? helper() : randomLeaf();
Expression right = (left instanceof Leaf || random.nextBoolean()) ? helper() : randomLeaf();
return new Node(op, left, right);
}
private Operator randomOperator() {
operatorsUsed++;
return allowedOperators.get(random.nextInt(allowedOperators.size()));
}
private Leaf randomLeaf() {
return new Leaf(random.nextInt(1, 10));
}
public static void main(String[] args) {
final var builder = new SmartExpressionBuilder(List.of(Operator.ADD, Operator.SUB), 4);
IntStream.range(0, 10)
.mapToObj(ignored -> builder.build())
.forEach(exp -> {
System.out.printf("%s = %d%n", exp.infix(), exp.evaluate());
TreePrinter.print(exp);
});
}
}
This works in principle. In the sense that a tree really builds with a given number of operators.
But there's a problem.
I get nodes looks like this:
Node Node
/ \ or / \
Leaf Node Node Leaf
For example my actual expression and tree may looks like this:
4 + 4 - (1 + 3) - 2 = 2
+
4 -
- 2
4 +
1 3
but i never get tree like this:
Node +
/ \ or - +
Node Node 5 2 2 -
6 1
I understand what the essence of the problem is.
In my recursive function, I always go into the left tree first.
And every time my random generates an the Node is in the left subtree, and not the Leaf, recursion dive deeper and deeper int the left subtree until unused operators ends.
This means that if an Node appeared in the left subtree, then Node cannot appear in the right at the same depths of tree.
I broke my brain, but did not figure out how to solve this problem without abandoning the recursive construction of my tree.
I would be very grateful for any ideas how build nodes of this kind
Node
/ \
Node Node
It's going to be very difficult to get balanced trees this way - you have to tune it very carefully for the left tree to probably give you half the operators. I don't think it's worth it.
Instead, I would pick the target number of operators at the top level - that would be a minimum plus some random range to generate larger or smaller expressions - and then randomly assign some of them to each subtree. So you have a recursive call that takes a size parameter; if size==0, generate a leaf, otherwise make a node, and split size-1 into a leftSize and rightSize to pass to the recursive calls.
Here's some rough pseudocode (I don't write much Java these days, but hopefully it makes the algorithm clear)
private Expression build(int size){
if (size == 0) return buildLeaf()
else {
leftSize = randomInt(size-1)
rightSize = size - 1 - leftSize
leftTree = build(leftSize)
rightTree = build(rightSize)
return buildNode(leftTree, rightTree, getRandomOperator())
}
}
Does that make sense and work for you?
I rewritten my method, as Edward Peters suggested.
At each step of recursion, I randomly determine how many Node's will be in the left and right trees (the sum of these numbers at the first step should be equal to the required number of operators in the expression), and return the Leaf if the number of nodes turns out to zero.
It's work just fine.
public Expression build(int numberOfOperators) {
if (numberOfOperators == 0) return randomLeaf();
int leftNodes = random.nextInt(numberOfOperators);
int rightNodes = numberOfOperators - leftNodes - 1;
return new Node(randomOperator(), build(leftNodes), build(rightNodes));
}
One example of resulting expression tree:
5 + 5 - (4 + 7) = -1
- Node
+ + or Node Node
5 5 4 7 Leaf Leaf Leaf Leaf

Lowest value path in a binary search tree

Was asked this in an interview recently and got stumped.
Given a binary tree where the nodes contain integer values, find the path (going all the way down to the leaves) that sum up to the lowest value.
So starting at root and traversing all the way down in a depth first fashion until you get to a leaf and adding up the node values along the way. Repeat for every possible path to the leaves.
I was just overwhelmed by the amount of possibilities there could be. But i tried doing a dfs, adding up values along the way, until i got to a leaf. Stored the path and the sum in a hashmap. But then I couldn't figure out how to reset the current sum and go down a different path to a different leaf the second time around.
It could be solved along these lines: Imagine that we define the function lowestValuePath(path, currentValue, bst) which takes a path as a string representation ("lrlrr" would be equivalent to having gone left, right, left, right, right), a value which is the currently accumulated sum of node values along this path, and bst which is the tree to traverse.
Our starting case would be lowestValuePath("", 0, rootNode), and termination would occur on a leaf, returning the path traversed and the value accumulated along this path.
Java-ish pseudo code could be:
TraverseResult {
String path;
int value;
}
TraverseResult lowestValuePath(path, currentValue, bst) {
val newValue = currentValue + bst.getNodeValue();
if bst.isLeaf():
return new TraverseResult(path, newValue);
else
val rightPath = lowestValuePath(path + "r", newValue, bst.getRightNode());
val leftPath = lowestValuePath(path + "l", newValue, bst.getLeftNode());
return leftPath < rightPath ? leftPath : rightPath
}
Some special case handling for the empty tree might be necessary..
If it is a "Binary Search Tree", then for every node T:
left(T) <= T <= right(T)
So actually just turn left all the time and you will get to the lowest value.

Non recursive floor method for Binary search tree

I have been trying to get this to work but while it works for majority of the input sometimes it gives the wrong output. I have spent some time debugging the code and it seems the problem is when i get a Node that is smaller than the root but bigger than the left node under the root.
How can I traverse the right sub-tree and still return the right key if no node in the right sub-tree is the floor node for that key?
Recall that if you do anything recursively, it can be transformed* into iteration.
Let's consider taking the floor of a well-formed BST, which should simply be the smallest element which is less than or equal to your key in the tree. All we have to do is traverse the tree to get it.
Let's implement it recursively so we can tease out a few important corollaries between iteration and recursion.
// Assuming non-null root node with method declaration
private Node floor(Node root, Key key, Node lowestNode) {
if(key.compareTo(root.getKey()) <= 0) {
if(root.getLeft() != null) {
return floor(root.getLeft(), key, lowestNode);
} else {
return root.compareTo(lowestNode) < 0 ? root : lowestNode;
}
} else {
if(root.getRight() != null) {
lowestRightNode.add(root);
return floor(root.getRight(), key, lowestNode);
} else {
return lowestNode;
}
}
Let's walk through the conditions for success.
If we compare a node to be less than or equal to our key value:
If we have a left child, there's something smaller. Traverse down the left half of the tree.
Otherwise, we're at the floor - which means we're at the node whose value is less than or equal to our key. Return it.
Otherwise (our node has a value greater than our key):
If we have a right child, there's a chance that our work isn't done yet (something's smaller). We'd like to keep it around since we could step off of the tree, so let's store it, then traverse down the right half of the tree.
Otherwise, we've fallen off of the tree. Return the smallest element we've kept track of.
An example may look something like this:
9
/ \
3 14
/ \
1 2
With a key of 12:
Compare with 9. We're larger. Store 9 in our lowest node variable, recurse right.
Compare with 14. We're smaller, but we don't have a left child. We compare the value 14 to 9 and 9 is smaller, so we return the node with 9.
If we want to convert this into iteration, then think about your starting point, your conditional check, and your incrementation steps.
Starting point: A non-null node
Conditional check:
key.compareTo(root.getKey()) <= 0
root.getLeft() != null
continue
root.compareTo(lowestRightNode) < 0 ? root : lowestRightNode
terminal
else
root.getRight() != null
store temp value and continue
return lowestRightNode
terminal
Pay close attention to your continuation conditions, and what other work you'd have to do to keep track of the lowest node you've seen so far (only for the right-hand side, that is).
*: Some recursive operations are more painful to convert than others, of course.

Depth of a Node in BST including duplicates

i have implemented a function to find the depth of a node in a binary search tree but my implementation does not take care of duplicates. I have my code below and would like some suggestions on how to consider duplicates case in this function. WOuld really appreciate your help.
public int depth(Node n) {
int result=0;
if(n == null || n == getRoot())
return 0;
return (result = depth(getRoot(), n, result));
}
public int depth(Node temp, Node n, int result) {
int cmp = n.getData().compareTo(temp.getData());
if(cmp == 0) {
int x = result;
return x;
}
else if(cmp < 0) {
return depth(temp.getLeftChild(), n, ++result);
}
else {
return depth(temp.getRightChild(), n, ++result);
}
}
In the code you show, there is no way to prefer one node with same value over another. You need to have some criteria for differentiation.
You can retrieve the list of all duplicate nodes depths using the following approach, for example:
Find the depth of your node.
Find depth of the same node for the left subtree emerging from the found node - stop if not found.
Add depth of the previously found node (in 1) to the depth of the duplicate
Find depth of the same node for the right subtree emerging from the found node (in 1) - stop if not found.
Add depth of the previously found node (in 1) to the depth of the duplicate
Repeat for left and right subtrees.
Also see here: What's the case for duplications in BST?
Well, if there's duplicates, then the depth of a node with a given value doesn't make any sense on its own, because there may be multiple nodes with that value, hence multiple depths.
You have to decide what it means, which could be (not necessarily an exhaustive list):
the depth of the deepest node with that value.
the depth of the shallowest node with that value.
the depth of the first node found with that value.
the average depth of all nodes with that value.
the range (min/max) of depths of all nodes with that value.
a list of depths of all nodes with that value.
an error code indicating your query made little sense.
Any of those could make sense in specific circumstances.
Of course, if n is an actual pointer to a node, you shouldn't be comparing values of nodes at all, you should be comparing pointers. That way, you will only ever find one match and the depth of it makes sense.
Something like the following pseudo-code should do:
def getDepth (Node needle, Node haystack, int value):
// Gone beyond leaf, it's not in tree
if haystack == NULL: return -1
// Pointers equal, you've found it.
if needle == haystack: return value
// Data not equal search either left or right subtree.
if needle.data < haystack.data:
return getDepth (needle, haystack.left, value + 1)
if needle.data > haystack.data:
return getDepth (needle, haystack.right, value + 1)
// Data equal, need to search BOTH subtrees.
tryDepth = getDepth (needle, haystack.left, value + 1)
if trydepth == -1:
tryDepth = getDepth (needle, haystack.right, value + 1)
return trydepth
The reason why you have to search both subtrees when the values are equal is because the desired node may be in either subtree. Where the values are unequal, you know which subtree it's in. So, for the case where they're equal, you check one subtree and, if not found, you check the other.

Split a binary tree

I am trying to split a binary search tree at the root in Java. I have no idea how to go about this. The recursive call is what is mostly confusing me. Below is the pseudocode that I was given. When x is greater than T (the tree to be split), am I going to call split(R.key, R.right, L, R)? Am I on the right track? This is the only function in the project that is confusing me.
Thanks in advance.
void split( int x, bst T, bst L, bst R) /* assuming x is not in T */
{
if T is null, make L and R null
else if x < T.key
set R = T /* all keys at the root and in right subtree are greater than x */
recursively split T's left subtree into L and R’s left subtree
/* some keys in T's left subtree are greater than x, other may be less than x */
else /* x is greater than T.key */
set L = T
recursively split T's right subtree into L's right subtree and R
}
It is important that you must have a binary ordered tree, such that Left < Root < Right for every subtree. Also, there is no node in the left subtree such that node > Root, and no node in the right subtree such that node < Root. Balancing (that all subtrees are same depth +- 1) is not needed.
It is like a dicomotical search; if the value to split by is greater than your current root, you are sure that it is greater than all nodes in the left subtree (because of the previous restriction). So, in order to search which of the nodes of the tree are bigger, you only need to check the nodes to the right. Likewise, if the value to split by is less than the value of root, it is also less than the values of all the nodes of the right subtree, and you must check more finely in the left tree.
In order to see it clearly, I suggest you to draw this tree (no spacing here)
8
4 12
3 6 10 14
1 2 5 7 9 11 13 15
, set several sample split values, and mark which nodes would remain in the new tree.

Categories