# stack_depth is initialised to 0
def find_in_tree(node, find_condition, stack_depth):
assert (stack_depth < max_stack_depth), 'Deeper than max depth'
stack_depth += 1
result = []
if find_condition(node):
result += [node]
for child_node in node.children:
result.extend(find_in_tree(child_node, find_condition, stack_depth))
return result
I need help understanding this piece of code. The question i want to answer is
The Python function above searches the contents of a balanced binary tree.
If an upper limit of 1,000,000 nodes is assumed what should the max_stack_depth constant be set to?
From what I understand, this is a trick question. If you think about it, stack_depth is incremented every time the find_in_tree() function is called in the recursion. And we are trying to find a particular node in the tree.So the worst case would be when we have to search through all the nodes in the tree before we find it. Hence, max_stack_depth should 1,000,000?
If you look at when stack_depth increments then it looks like we will increment every time we access a node. And in our case we are accessing every single node every time. Because there is no return condition when stops the algorithm when the correct node is found.
Can someone please try to explain me their thought process.
Instead of multiplying the number of nodes on each layer, you have to add them. For example, the number of nodes in the first four layers is 1+2+4+8=15, not 1*2*4*8=64.
# 1
# # + 2
# # # # + 4
# # # # # # # # + 8 = 15
In general, the number of nodes in the first n layers is 2**(n+1)-1. You can use logarithms to get the correct power and get the floor of that number. If you want fewer that that number, you would also have to subtract one from the power.
>>> math.floor(math.log(1000000, 2))
19
>>> sum(2**i for i in range(1, 20))
1048574
Concerning your edit: Yes, stack_depth is incremented with each node, but you are incrementing a local variable. The increment will carry to the child nodes (passed as a parameter) but not to the siblings, i.e. all the nodes at level n will be called with stack_depth == n-1 (assuming it started as 0 on the first level). Thus, max_stack_depth should be 19 (or 20 if it starts with 1) to visit the ~1,000,000 nodes in the first 19 levels of the tree.
Related
I am working on my senior thesis in economics, and one of the major problems is that an agent is trying to choose the strategy on a binary tree that maximizes utility (we use probability weighting, so ignore that the expected value of each bet is 0). The binary tree has 2^T - 1 nodes, where if an agent takes a bet, they have a 1/2 chance of getting 10 units and a 1/2 chance of losing 10 units. Then a strategy is essentially all of the bets they would be willing to take and the resulting probability/payoff end states.
Thus far, I've let strategies be a list of strategies (or probabilistic outcomes of the strategies). As such, the first strategy is
strategy[0] = [[-10,1/2],[10,1/2]],
where the first element of strategy[0] is the payout and then probability of an outcome. Here, the agent only takes the first bet and doesn't continue regardless of the outcome.
Then, the second strategy would split the first element of strategy[0] into two outcomes with half of the probability of the parent [-20,1/4],[0,1/4] and then adds the elements of strategy[0] that aren't being split, so
strategy[1] = [[-20,1/4],[0,1/4],[10,1/2]].
Here, the agent takes the first bet regardless and then only takes the second if they lose the first. Then, our goal is that to create an algorithm that generates every possible strategy for at most T bets.
Currently, I have a function that splits the i-th list element into two, signifying that the agent takes the bet at the given node, attached below
def split(strategy,i):
newstrat = []
node = strategy[i]
newnode1 = [node[0] - 10,node[1]/2]
newnode2 = [node[0] + 10,node[1]/2]
for j in range(len(strategy)):
if j != i:
newstrat += [prospects[j]]
else:
newstrat += [newnode1] + [newnode2]
return newstrat
So in as in our earlier example, we would have
strategy[1] = split(strategy[0],0),
strategy[2] = split(strategy[0],1),
strategy[3] = split(strategy[2],0], #the agent is willing to take bets at either node
and so on.
However, at the column where t = 3 < T, there are 4 nodes, and therefore 15 different combinations of choices whether or not to bet at one or more of the nodes. My question might be really elementary, but I am having trouble adding each strategy to the set of strategies in a way that allows me to continue building onto it for t = 4 and onward.
Let's say I have a set of variables ${x{1},...,x{n}}$ and a randomly given, but fixed number $s$. How to find the minimum number of variables required to sum up to that fixed number? We can presume, that the variables always sum up to the given number. So far I have achieved this:
def poss(x,s):
if s<=0:
if s==0:
return 1
if s<0:
return -1
else:
for i in x:
if poss(x,s-i)==1:
print("right")
if poss(x,s-i)==-1:
print("wrong")
else:
pass
I know at some point I need to possibly create an array that keeps track of how many addings each branch has made and delete those that don't work, then take minimum of that, but I'm not sure where or how.
an example of output I calculated by hand:
print(poss([2,3],10)) --> output is 4
It's important to note that when you use recursion inside for loops, all variables stored inside each recursion will be lost when exiting the recursion. To solve this we can capture the count of recursions (ie. the number of x elements used to reduce s) in a global variable, lets call memory.
Next we need to do three things:
The only instance in which memory should be changed is if s==0.
Memory count should only be changed when s==0 from the minimum number of subtractions. (we can sort x from largest to smallest so that we start cutting s with the bigger values first. Example: x=[1,1,4], s=8 should output 2 since (s-4-4 = 0), not 8 (s-1-1-1-1-1-1-1-1 = 0) where every subtraction is a new recursion)
We also need to make sure memory is only ever changed once (otherwise other combinations of elements in x will update it). To do this we can check the the value of memory prior to each following recursion and abort if memory has already been found.
The final code:
memory = 0
def poss2(x, s, count=0):
big_to_small = sorted(x, reverse=True)
global memory # Call on global variable
if s==0: # Capture count in global variable. Recursion inside of for loops will lose all previous data.
memory=count
elif s<0:
return count-1 # Go back, S too small
else:
for i in big_to_small:
if memory==0: # If memory is till zero, s==0 was not yet achieved. Keep recursing with new s and count
poss2(big_to_small, s-i, count+1)
return memory # Return the captured memory
poss2([1,1,1,6], 12)
# 2 --> (6,6)
poss2([2,3], 10)
# 4 --> (3,3,2,2)
poss2([2,7,5,1,6,32], 100)
# 5 --> (32, 32, 32, 2, 2)
Note also, this method is somewhat slow since it will iterate over every value in the for loop and do so in every recursion. A better approach to the same problem would be using something like
count = s // max(i)
s = s - count*max(i)
and then working your way down from the highest i in x.
Cheers!
I was going through the Maximum Binary Tree leetcode problem. The TL;DR is that you have an array, such as this one:
[3,2,1,6,0,5]
You're supposed to take the maximum element and make that the root of your tree. Then split the array into the part to the left of that element and the part to its right, and these are used to recursively create the left and right subtrees in the same way, respectively.
LeetCode claims that the optimal solution (shown in the "Solution" tab) uses a linear search for the maximum value of the sub-array in each recursive step. This is O(n^2) in the worst case. This is the solution I came up with, and it's simple enough.
However, I was looking through other submissions and found a linear time solution, but I've struggled to understand how it works! It looks something like this:
def constructMaximumBinaryTree(nums):
nodes=[]
for num in nums:
node = TreeNode(num)
while nodes and num>nodes[-1].val:
node.left = nodes.pop()
if nodes:
nodes[-1].right = node
nodes.append(node)
return nodes[0]
I've analysed this function and in aggregate, this appears to be linear time (O(n)), since each unique node is added to and popped from the nodes array at most once. I've tried running it with different example inputs, but I'm struggling to connect the dots and wrap my head around how this works. Can someone please explain it to me?
One way to understand the algorithm is to consider the loop invariants. In this case, the array of nodes always satisfies the condition that before and after each execution of the for-loop, either:
nodes is empty and a max binary tree does not exist (for example, if the input nums was empty)
the first item in nodes is the max binary tree based on the data processed so far from the input nums
The while-loop ensures that the current max binary tree is the first item in the nodes array, since otherwise, it would have been popped and added as a left subtree.
During each iteration of the for-loop, the check:
if nodes:
nodes[-1].right = node
adds the current node as a right subtree to the last item in the nodes array. And when this happens, the current node is less than the last node in the nodes array (since each input integer is defined to be unique). And since the current node is less than the last node in the array, the last node acts as a partition point whose value is greater than the current item, which is why the current node is added as a right subtree.
When there are multiple items in the nodes array, each item is a subtree of the item to its left.
Running Time
For the running time, let n be the length of the input nums. There are n executions of the for-loop. If the input data were sorted in descending order, but with the max input value at the end of the input (such as: 4, 3, 2, 1, 5), then the inner while-loop would be skipped during each iteration until the last for-loop iteration. During the last for-loop iteration, the while loop would run n - 1 times, for a total running time of n + (n - 1) => 2n - 1 => O(n).
This is a bottom up approach to check if the tree is an AVL tree or not. So how this code works is:
Suppose this is a tree :
8
3 10
2
1
The leaf node is checked that it is a leaf node(here 1). It then unfolds one recursion when the node with data 2 is the current value. The value of cl = 1, while it compares the right tree. The right branch of 2 is empty i.e does not have any children so the avl_compare will have (1, 0) which is allowed.
After this i want to add one value to cl so that when the node with data 3 is the current value, the value of cl = 2. avl_check is an assignment question. I have done this on my own but i need some help here to play with recursive functions.
def avl_check(self):
cl = cr = 0
if(self.left):
self.left.avl_check()
cl+=1
if(self.right):
self.right.avl_check()
cr += 1
if(not self.avl_compare(cl,cr)):
print("here")
Your immediate problem is that you don't seem to understand local and global variables. cl and cr are local variables; with the given control flow, the only values they can ever have are 0 and 1. Remember that each instance of the routine gets a new set of local variables: you set them to 0, perhaps increment to 1, and then you return. This does not affect the values of the variables in other instances of the function.
A deeper problem is that you haven't thought this through for larger trees. Assume that you do learn to use global variables and correct these increments. Take your current tree, insert nodes 4, 9, 10, and 11 (nicely balanced). Walk through your algorithm, tracing the values of cl and cr. By the time you get to node 10, cl is disturbingly more than the tree depth -- I think this is a fatal error in your logic.
Think through this again: a recursive routine should not have global variables, except perhaps for the data store of a dynamic programming implementation (which does not apply here). The function should check for the base case and return something trivial (such as 0 or 1). Otherwise, the function should reduce the problem one simple step and recur; when the recursion returns, the function does something simple with the result and returns the new result to its parent.
Your task is relatively simple:
Find the depths of the two subtrees.
If their difference > 1, return False
else return True
You should already know how to check the depth of a tree. Implement this first. After that, make your implementation more intelligent: checking the depth of a subtree should also check its balance at each step. That will be your final solution.
If given a tree with nodes with integers: 1 ~ 10, and branching factor of 3 for all nodes, how can I write a function that traverses through the tree counting from root to leaves for EVERY paths
So for this example, let's say it needs to return this:
{1: 1, 2: 5}
I've tried this helper function:
def tree_lengths(t):
temp = []
for i in t.children:
temp.append(1)
temp += [e + 1 for e in tree_lengths(i)]
return temp
There are too many errors with this code. For one, it leaves behind imprints of every visited node in the traversal in the returning list - so it's difficult to figure out which ones are the values that I need from that list. For another, if the tree is large, it does not leave behind imprints of the root and earlier nodes in the path prior to reaching the line "for i in t.children". It needs to first: duplicate all paths from root leaves; second: return a list exclusively for the final number of each path count.
Please help! This is so difficult.
I'm not sure exactly what you are trying to do, but you'll likely need to define a recursive function that takes a node (the head of a tree or subtree) and an integer (the number of children you've traversed so far), and maybe a list of each visited node so far. If the node has no children, you've reached a leaf and you can print out whatever info you need. Otherwise, for each child, call this recursive function again with new parameters (+1 to count, the child node as head node, etc).