Is it possible to traverse a binary tree with a finite-state machine? - tree-traversal

Good day,
Just to be clear: I am not looking for recursive or iterative solutions, Wikipedia has sufficient pseudocode to implement pre-, in- and post-order traversal of any tree.
I am interested in building a finite-state machine to traverse a binary tree.
A Tree consists of Nodes. Nodes have a LeftChild, a RightChild, and a Parent property.
The FSM halts at a Node at any given time, and can have as many states as required, but NO DYNAMIC STACK of any sort (which distinguishes it from a Turing machine). On input "GiveNext" the machine should halt on the next node (say traversing the tree pre-order.)
I've tried for quite a while now, and suspect, that it is not possible, but I am not sure. The problem is the need to keep track of recent decisions, so that on revisiting a Node via Parent one can turn right when left has been processed.
Thoughts?
Thanks in advance!
Herb

There may be a lot of constraints on this kind of exercises that I may be missing. I hope this is at least a bit usefull.
I am assuming that it is acceptable that the FSM is 'standing' at any given time on a 'current node' and therefore it has available inputs returning correct 0/1 values with info about this current node.
Inputs:
AmIALeftChild (equals 1 if the current node is hanging left of their parent node, otherwise 0)
AmIARightChild (1 if I am hanging left of my parent node, otherwise 0)
HaveLeftChild? (1 if I have a left child, otherwise 0 )
HaveRightChild? (1 if I have a right child, otherwise 0 )
And is it ok to 'traverse it' by following the instructions in these 3 outputs?
Outputs:
GoToLeftChild
GoToRightChild
GoUp
(only 1 of the 3 outputs can be true at any given time)
If both constraints are allowed, then you could build a FSM like this one:
States
Startup
NormalTraverse
GoBackFromLeft
GoBackFromRight
Finished
This is the State machine:
Start -> just go to NormalTraverse state (assuming we are on the root, right)
NormalTraverse ->
If HaveLeftChild=1 Then
Set GoToLeftChild=1 (and others outputs to 0)
Go to NormalTraverse
ElseIf HaveRightChild Then
Set GoToRightChild=1
Go to NormalTraverse
ElseIf AmIALeftChild Then
Set GoUp=1
Go to GoBackFromLeft
ElseIf AmIARightChild Then
Set GoUp=1
Go to GoBackFromRight
Else
Go to Finished.
GoBackFromLeft ->
If HaveRightChild Then
Set GoToRightChild=1
Go to NormalTraverse.
ElseIf AmIALeftChild Then
Set GoUp=1
Go to GoBackFromLeft
ElseIf AmIARightChild Then
Set GoUp=1
Go to GoBackFromRight
Else
Go to Finished.
GoBackFromRight
If AmIALeftChild Then
Set GoUp=1
Go to GoBackFromLeft
ElseIf AmIARightChild Then
Set GoUp=1
Go to GoBackFromRight
Else
Go to Finished.
Excuse my english now and please note that the code is not procedural, and the "IF's" should be mutually exclusive after correctly 'expanding' them 'bit-wisely'. but I am writing it these way to save a little of time. If you don't understand what I mean, please ask.

This is what I have come up with, after many sheets of paper worth half a tree, mainly to draw many binary trees (especially degenerated ones) to verify that the FSM works correctly.
The only variable required to be accessible all the time is the start node. This can be just any node in the tree: the FSM traverses just this subtree.
It is assumed, that the start node was already processed (or needs no processing). But it would be easy to integrate, if this is not the case for your application: Before the GO LEFT step, just add a test for the start node: I AM START. If TRUE, Report, otherwise continue as shown.
The individual actions mean:
GO LEFT - Make the current node's left child the current node, if there is any. The answer is OK if successful, or No Left if there is no such node.
GO RIGHT - Make the current node's right child the current node, if there is any. The answer is OK if successful, or No Right if there is no such node.
GO UP - Make the current node's parent the current node. There is just one resulting state. For the root, the parent does not exist, but the FSM will never attempt to access the root's parent.
I AM LEFT - Test if the current node is its parent left child. The answer is True or False.
I AM RIGHT - Test if the current node is its parent right child. The answer is True or False.
I AM START - Test if the current node is the start node. The answer is True or False.

Related

Is there a pattern for concurrent searching?

I have this concurrent pattern that came up when trying to model my problem, and I don't know if there's a name for it. Having a design pattern reference or something like that could help me implement it more safely.
Concept:
The foreman (main thread) is asked to look for a series of objects in a big warehouse.
This warehouse has n floors. The foreman has a team of n workers (helper threads), each with a dedicated floor.
The foreman recieves an object, and asks every worker to find it.
If a worker finds it on their floor, they return to the foreman with appropriate information. (location, status...)
The foreman then calls back all other workers (since the item has been found there's no need for more searching), and move on to the next object.
If everyone comes back saying "No it's not on my floor" we can act accordingly. (signal a missing product to management...)
The main problem I have is that I need to make sure threads don't waste calculation time when the item has already been found, and to ensure proper coordination.
I also can't give every thread the entire list of things to find, since this information is recieved item by item. (eg. via network)
Are you looking for Observer pattern ?
Once a worker finds the item and returns to the Foreman. The Foreman should notify to all the workers that the item is found, so all the threads will stop search and return.

Are there any efficient (sub)tree locking mechanisms?

I have a tree T, nodes of which can be addressed by their paths (payloads of nodes contain some names that can be glued together into a path). What I would like to have is some mechanism (algorithm, auxiliary data structure etc.) that will allow, given the path P0, lock an entire sub-tree in such a fashion that:
attempts to lock on any path P1 (where P1 starts with P0, i.e. P1 belongs to the locked subtree) will result in lock for P1 being the one for P0 (well, it might be not the very same lock, but operations on P1 should wait until P0 lock is free);
but when P0 lock is released, any lock for P2, where P2 starts with P0, but not P2 starts with P1, i.e. P2 subtree and P1 subtree are different, different locks will be granted for those two paths, and so on.
I tackled this task for a couple of times, but I wound up with an over-complicated code that was messy and used some kind of tree for storing locks itself, which was even heavier that the tree I tried to lock.
In case my question is unclear, please let me know if you'd like to see some drawings/diagrams or anything that would help.
Disclaimer: This is very similar to a HW assignment I did in 2009, so I remember the basic idea, not the details. In any case, I suggest the following idea:
Each node in the tree has it's own lock. Every locking action starts from the root of the tree, and follows down towards the desired node, locking each node on the way down. If it encounters a locked node (that isn't the root), the locking action is terminated (unsuccessfully), or set to wait and try again (like busy wait). If the root itself is locked, it might be a temporary lock for another action taking place. If the root is not locked but an inner node is, it is really locked.
Unlocking might be different, not sure if following the path down from the root is necessary. (it might be, so this is worth checking).
EDIT:
While working up the tree after the required node is locked, also mark the nodes on the path to the tree as ones that have some in their subtree locked. This way when you are locking a node you can know if there is a node in that subtree that is already locked.
I don't believe OP is still waiting for new answers, so this one is rather for future visitors of this page.
The question is about mechanisms, but I would like to recommend a software solution.
Locktopus allows to lock subtrees. Additionally, it can lock multiple resources (atomically) for exclusive (write) or shared (read) access.
I might be missing something but i think your requirements are easily solved by first computing the inverse transitive closure of your tree which can be computed in linear time.
The basic idea is that knowing if a node is safe to operate on, can be reduce to knowing if any of its parent haven't been locked.
To do so, you keep a set of locked nodes. Locking a sub-tree is done by simply adding its root to the locked_nodes set, and unlocking a subtree is done by removing its root from the same set ( booth O(1) operations)
Given a node, to know if the node (or equivalently the sub-tree rooted on that node) can be operated on :
Intersection(parents(node),locked_nodes) = empty_set
The set "parent(node)" is extracted from the inverse transitive closure.

3 requirements for synchronization: why does this approach not work?

I'm trying to learn about synchornization and understand there are 3 conditions that need to be met for things to work properly
1)mutual exclusion - no data is being corrupted
2)bounded waiting - a thread won't do nothing forever
3)progress being made - system as a whole is doing work e.g. not just passing around who's turn it is
I don't fully understand why the code bellow doesn't work. According to my notes it has mutual exclusion but doesn't satisfy making progress or bounded waiting. Why? Each thread can do something and as long as now thread crashes everythread will get a turn.
The following are shared variables
int turn; // initially turn = 0
turn == i: Pi can enter its critical section
The code is
do {
while (turn != i){}//wait
critical section
turn = j;//j signifies process Pj in contrast to Pi
remainder section
} while (true);
It's basically slide 10 of these notes.
I think the important bit is that according to slide 6 of your notes the 3 rules apply to the critical section of the algorithm and are exactly as follows:
Progress: If no one is in the critical section and someone wants in,
then those processes not in their remainder section must
be able to decide in a finite time who should go in.
Bounded Wait: All requesters must eventually be let into the critical
section.
How to break it:
Pi executes and its remainder section runs indefinitely (no restriction for this)
Pj runs in its entirety, setting turn:= i so it's now Pi's turn to run the critical section.
Pi is still running its remainder which runs indefinitely.
Pj is back to it's critical section but never gets to run it since Pi never gets back to the point where it can give the turn to Pj.
That breaks the progress rule. No one is in the critical section, Pj wants in but cannot decide in a finite time if it can go in.
That breaks the bounded wait rule. Pj will never be let back in into the critical section.
As Malvavisco correctly points out, if a process never releases a resource no other process will have access to it. This is an uninteresting case and typically it's considered trivial. (In practice, it turns out not to be -- which is why there's a lot of emphasis on being able to manage processes from outside, e.g. forcibly terminate a process with minimal ill effects.)
The slides are actually a little imprecise in their definitions. I find that this Wikipedia page on Peterson's algorithm (Algorithm #3 on slide 12) is more exact. Specifically:
Bounded waiting means that "there exists a bound or limit on the number of times that other processes are allowed to enter their critical sections after a process has made a request to enter its critical section and before that request is granted"
Some thought experimentation makes it pretty clear that Algorithm #1 (slide 10) fails this. There is no bound on the number of times the critical section could be entered by either process if the process switching timing was unfortunate. Suppose process 1 executes, enters the critical section, and from there on process 2 only is switched to while process 1 is in its critical section. Process 1 will never account for this. Peterson's algorithm will as process 1 will forfeit its ability to enter the critical section if process 2 is waiting (and vice versa).

Does every path in activity diagram have a finish node. Every "fork" branch need to goto a merge?

Does every path in an activity diagram need to have a finish node? A similar question is does every fork branch need to be merged?
I did an activity diagram (below), but it seems wrong. Some branches (from fork) has no finish node (nor end in a merge).
My idea was the clerk will send shipment packing slip to purchashing, accounting & customer. 2 of which just seem to create/init objects (eg. enter info). They are executed in parallel so I felt I should have a fork?
Does every path in an activity diagram need to have a finish node?
Yes. But there are two kinds of finish node: ActivityFinal and FlowFinal. You need to terminate each of the packaging and shipment flows with a FlowFinal node. See section 12.4 in the spec for details. The symbol is here, the page it's on is a good reference.
Does every fork branch need to be merged?
No. But it needs to terminate - hence existence of FlowFinal node.
hth.

Solve a maze using multicores?

This question is messy, i dont need a working solution, i need some psuedo code.
How would i solve this maze? This is a homework question. I have to get from point green to red. At every fork i need to 'spawn a thread' and go that direction. I need to figure out how to get to red but i am unsure how to avoid paths i already have taken (finishing with any path is ok, i am just not allowed to go in circles).
Heres an example of my problem, i start by moving down and i see a fork so one goes right and one goes down (or this thread can take it, it doesnt matter). Now lets ignore the rest of the forks and say the one going right hits the wall, goes down, hits the wall and goes left, then goes up. The other thread goes down, hits the wall then goes all the way right. The bottom path has been taken twice, by starting at different sides.
How do i mark this path has been taken? Do i need a lock? Is this the only way? Is there a lockless solution?
Implementation wise i was thinking i could have the maze something like this. I dont like the solution because there is a LOT of locking (assuming i lock before each read and write of the haveTraverse member). I dont need to use the MazeSegment class below, i just wrote it up as an example. I am allowed to construct the maze however i want. I was thinking maybe the solution requires no connecting paths and thats hassling me. Maybe i could split the map up instead of using the format below (which is easy to read and understand). But if i knew how to split it up i would know how to walk it thus the problem.
How do i walk this maze efficiently?
The only hint i receive was dont try to conserve memory by reusing it, make copies. However that was related to a problem with ordering a list and i dont think the hint was a hint for this.
class MazeSegment
{
enum Direction { up, down, left, right}
List<Pair<Direction, MazeSegment*>> ConnectingPaths;
int line_length;
bool haveTraverse;
}
MazeSegment root;
class MazeSegment
{
enum Direction { up, down, left, right}
List<Pair<Direction, MazeSegment*>> ConnectingPaths;
bool haveTraverse;
}
void WalkPath(MazeSegment segment)
{
if(segment.haveTraverse) return;
segment.haveTraverse = true;
foreach(var v in segment)
{
if(v.haveTraverse == false)
spawn_thread(v);
}
}
WalkPath(root);
Parallel Breadth-First Search
Search for parallel or multithread bread first traversal, which is basically what you're doing. Each time you come to a fork in your maze (where you can take one of several paths), you create a new worker thread to continue the search down each of the possible paths and report back which one gets you to the end.
It's similar to "simple" breadth first searches, except the subtrees can be searched in parallel.
If this were a pure tree data structure, you wouldn't need to lock, but since it's a graph traversal you will need to keep track of your visited nodes. So, the code which sets the "visited" flag of each node will need to be protected with a lock.
Here's a good example program. It uses audio feedback, so be sure your speakers are on.
http://www.break.com/games/maze15.html
Off hand, given your structure above, I could see solving this by adding an 'int Seen' to each MazeSegement instead of 'bool haveTraverse'. You could then use a interlocked increment on the 'Seen' variable as you're looping over the ConnectedPaths and only spawn a thread to take the path if the 'Seen' increment returns 1 (assuming Seen is initialized to 0).
So the code becomes something like
void WalkPath(MazeSegment segment)
{
foreach(var v in segment.ConnectedPaths)
{
if( Interlocked.Increment( &v.Path.Seen ) == 1)
spawn_thread(v.Path);
}
}
Other threads which might attempt to take the same path should get something >1. Because interlocked.increment would guarantee a thread-safe increment then we don't have to worry about 2 threads getting a result of '1' so only one thread should take a given path.
You can do this using the usual "read, calculate new value, compare-and-swap, repeat until CAS succeeds" method commonly found in lock-free programming.
All grid-squares in your maze start should hold a pointer representing the direction to move to reach the exit. Initially they all are "unknown".
Walk the maze starting at the exit. On each square reached, use compare and swap to replace "unknown" with the direction to the square this thread previously processed. If CAS fails, you've got a loop, prune that branch. If CAS succeeds, continue forward. When you assign a direction to the entrance, you now can follow the path to the exit.
Create a class (Worker) instances of which hold a path taken so far, and can only advance() through a straight corridor at given direction. At every intersection, drop the worker object which holds the path before intersection, and create two (or three) new objects, with a copy of that path and different turns taken.
Put these worker objects into a queue. Notice how every one of them is independent from another, so you may take several of them from the queue and advance() in parallel. You can simply create as many threads, or use a pool of threads according to the number of cores you have. Once any of the workers advance to the destination square, output the paths it holds, it is a solution.
Consider traversing the maze from exit to entry. In a real maze, blind alleys are intended to slow down motion form entry to exit, but rarely the other way around.
Consider adding a loop detection mechanism, e.g. by comparing intersections that make up your path with an intersection you encounter.
Consider using a hand-made linked list to represent the path. Note how inserting a new head to a linked list does not change the rest of it, so you can share the tail with other instances that don't modify it. This will reduce memory footprint and time needed to spawn a worker (only noticeable at rather large mazes).

Resources