Sie sind auf Seite 1von 14

Babe-Bolyai University Faculty of Mathematics and Computer Science Cluj-Napoca

Path in a maze
Documentation

Coordinating professor: Dr. Lupa Dana Subject: Data Structures and Algorithms Student: Lucaci Andrei Group: 913

June 2012

Contents:
1. 2. 3. 4. 5. 6. 7. 8. 9.

Problem statement 3 Code.. 3 How I organized my program..11 Present the idea/method to solve the problem 12 Apply justification to proper example 13 Application Input/Output 13 Execution time..14 Suitable DS...14 Pseudocode for the main algorithm. 14

1. Problem statement A robot is asked to navigate a maze. It is placed at a certain position (the starting position: S) in the maze and is asked to try to reach another position (the goal position: G). Maze has rectangular shape. Positions are identified by (x,y) coordinates. Positions in the maze will either be open (*) or blocked with an obstacle (X). At any given moment, the robot can only move 1 step in one of 4 directions. Valid moves are: Go North: (x,y) Go East: (x,y) Go South: (x,y) Go West: (x,y) The robot can only move to positions without obstacles and must stay within the maze. The robot should search for a path from the starting position (S) to the goal position (G) until it finds one or until it exhausts all possibilities. S * * * * * * * X * X X * X X * * * * * * X * * X * * X * X * * * X * * * * * * * * * * * X X G *

Find optimum solution (shortest path). 5. Define ADT Priority Queue; representation: Red black tree. 7. Define ADT Stack: represented on a static vector. 2. Code Class RBtree{ constant integer black = 1; constant integer red = 0; struct RBnod { Generic data; RBnod *p,*left,*right; integer color; } RBnod* root; RBnod* NIL; } Description: a RedBlack Tree is a special type of binary tree, used in computer science to organize pieces of comparable data, such as text fragments or numbers. It is a self-balancing tree, meaning that it will obey the binary tree rules when it comes to insert or delete. A RBT has an average complexity per operation: (log(N)) where N is the number of Generic elements in the RBtree, namely T.

RBnod* find(T value) Description: This function is used to search for a nod with a known T value Input Data: value Precondition: value is a T Output Data: the return Postcondition: the return is pointer to a RBnod to the position where the T value is situated Complexity: BC,AC,WC: (log(N)) where N is the number of T elements RBnod* min(RBnod* current) Description: This function is used to find the minimum nod of the RBtree starting with a current position (the same as the ` T minFromRoot()` and ` T minVal(RBnod* current)` functions). Input Data: current Precondition: current is a pointer to a RBnod Output Data: the result Postcondition: the result is a pointer to a RBnod that represents the position that has the minimum T value considering only the nodes older than the RBnod current Complexity: BC,AC,WC: (log(N)) where N is the number of T elements RBnod* max(RBnod* current) Description: This function is used to find the maximum nod of the RBtree starting with a current position (the same as ` T maxVal(RBnod* current)` and ` T maxFromRoot()` functions). Input Data: current Precondition: current is a pointer to a RBnod Output Data: the result Postcondition: the result is a pointer to a RBnod that represents the position that has the maximum T value considering only the nodes older than the RBnod current Complexity: BC,AC,WC: (log(N)) where N is the number of T elements RBnod* next(RBnod* current) Description: This function returned the pointer to the RBnod that has the next T value higher, than the currents value Input Data: current Precondition: current is a pointer to a RBnod Output Data: the return Postcondition: the result is a pointer to a RBnod Complexity: BC,AC,WC: (log(N)) where N is the number of T elements RBnod* prev(RBnod* current) Description: This function returned the pointer to the RBnod that has the previous T value, lower than the currents value Input Data: current Precondition: current is a pointer to a RBnod Output Data: the return Postcondition: the result is a pointer to a RBnod Complexity: BC,AC,WC: (log(N)) where N is the number of T elements void insertB(RBnod* nod)
4

Description: This function inserts the T value into the RBtree, as like introducing a Nod in a normal binary tree Input Data: nod Precondition: nod is a pointer to a RBnod Output Data: Complexity: BC,AC,WC: (log(N)) where N is the number of T elements void remove(T value) Description: This function removes the T value from the RBtree, equilibrating it with the use of the function: removeRepair() in case the RBnod removed had a black color. Input Data: value Precondition: value is a T Output Data: Complexity: BC,AC,WC: (log(N)) where N is the number of T elements void rotate_left(RBnod* x) Description: This is a specific function for a Red-Black tree that rotates the RBnod-es from the left as in Figure 1 Input Data: x Precondition: x is a RBnod; Output Data: Complexity: BC,AC,WC: O(1)

Fig(1) Left-rotation and Right-rotaion void rotate_right(RBnod* x) Description: This is a specific function for a Red-Black tree that rotates the RBnod-es from the right as in Figure 1 Input Data: x Precondition: x is a RBnod; Output Data: Complexity: BC,AC,WC: O(1) void insert(T value) Description: This function is a specific Red-Black tree function that inserts the T value in the Red-Black tree Input Data: value Precondition: value is of typeT Output Data: Complexity: BC,AC,WC: (log(N)) where N is the number of T elements void removeRepair(RBnod* x) Description: This function is a specific Red-Black tree function that takes all the cases that forms when removing a black nod from the RBtree Input Data: x
5

Precondition: x is a pointer to a RBtree Output Data: Complexity: BC,AC,WC: (log(N)) where N is the number of T elements Operations on the red-black tree: RBtree::find(k){ RBnod x=root; while x NIL and k info[x] do if k < info[x] then x x else x right[x] return x } RBtree::min(k){ while left[x] NIL do x left[x] return x } RBtree::next(k){ if right[x] NIL then return min(right[x]) y p[x] while y NIL and x = right[y] do x y y p[y] return y } RBtree::insertb(z){ y NIL x root while x NIL do yx if info[z] < info[x] then x left[x] else x right[x] p[z] y if y = NIL then root[T] z else if info[z] < info[y] then left[y] z else right[y] z } RBtree::remove(z){ if left[z] = NIL or right[z] = NIL
6

then y z else y next(z) if left[y] NIL then x left[y] else x right[y] if x NIL then p[x] p[y] if p[y] = NIL then root[T] x else if y = left[p[y]] then left[p[y]] x else right[p[y]] x if y z then info[z] info[y] return y } RBtree::rotate_left(x){ y right[x] right[x] left[y] p[left[y]] x p[y] p[x] if p[x] = nil[T] then root[T] y else if x = left[p[x]] then left[p[x]] y else right[p[x]] y left[y] x p[x] y } RBtree::removeRepair(x){ while x root[T] and color[x] = BLACK do if x = left[p[x]] then w right[p[x]] if color[w] = RED then color[w] BLACK color[p[x]] RED rotate_left (p[x]) w right[p[x]] if color[left[w]] = BLACK and color[right[w]] = BLACK then color[w] RED x p[x] else if color[right[w]] = BLACK then color[left[w]] BLACK color[w] RED rotate_right(T, w) w right[p[x]] color[w] color[p[x]] color[p[x]] BLACK
7

color[right[w]] BLACK rotate_left (T, p[x]) x root[T] else (same as then clause with "right" and "left" exchanged) color[x] BLACK } Class PriorityQueue: RBtree<Position> rbt; Position p; Position NIL; int size; Description: this class use the same idea of a Priority Queue, but the main difference is that it is implemented over a Red-Black Tree. void push(Position infos) Description: This function will insert into the ADT Priority queue an element of type Position. Input Data: value. Precondition: value is of type Position. Output Data: Complexity: BC,AC,WC: (log(N)) as in the RBT::insert() function. Position pop() Description: This function will remove from the ADT Priority queue an element of type Position. Input Data: Precondition: Output Data: Postcondition: returns an element of type Position with the maximum priority. Complexity: BC,AC,WC: (log(N)) as in the RBT::remove() function. bool isEmpty() Description: Checks if the Priority Queue is empty. Input Data: Precondition: valid PQ. Output Data: Postcondition: returns true if the PQ is empty or false otherwise. Complexity: O(1); Position min() Description: Returns the element with the lowest priority and removes it. Input Data: Precondition: valid PQ. Output Data: Postcondition: returns an element of type Position. Complexity: (logN) as in the RBT min function. Position max() Description: Returns the element with the highest priority and removes it.
8

Input Data: Precondition: valid PQ. Output Data: Postcondition: returns an element of type Position. Complexity: (logN) as in the RBT max function. Position top() Description: Returns the element with the highest priority. Input Data: Precondition: valid PQ. Output Data: Postcondition: returns an element of type Position. Complexity: (logN) as in the RBT max function. int PQsize() Description: Returns the size of the PQ. Input Data: Precondition: valid PQ. Output Data: Postcondition: returns an int which is the size of the PQ. Complexity: O(1). void Empty() Description: Flushes all the data from the PQ. Input Data: Precondition: valid PQ. Output Data: Postcondition: will pop all the elements from the PQ. Complexity: N*(logN), where N is T value. Operations on the Priority Queue: Rbt: RBtree. PriorityQueue::push(T elem) Rbt.insert(elem) Size++ PriorityQueue::pop() if (rbt.getRoot() rbt.getNIL()) Position result rbt.getDt(rbt.max(rbt.getRoot())) rbt.remove(result) size-return result PriorityQueue::isEmpty() return rbt.isEmpty() PriorityQueue::min() return rbt.min() PriorityQueue::max()
9

return rbt.max() PriorityQueue::empty() for (i=0; i<size) do pop() end_for Class Stack: T elem; int size; T vector[1000]; int vect[1000]; int nrElem; T NIL; Description: ATD stack implemented on a static vector. T push(T elem) Description: Pushes information onto the stack. Precondition: stack is not full. Postcondition: returns the element inserted. T pop() Description: Pops the element from the top of the stack and returns it. Precondition: stack is not empty. Postcondition: returns the element and deletes it from the stack. void initEmpty() Description: initializes the vector on which the stack is built with the NIL (T). Precondition: valid stack. Postcondition: bool isEmpty() Description: checks if the stack is empty. Precondition: valid stack. Postcondition: return true on success or false otherwise. (*)T getPrev() Description: returns the value of the element just below the top of the stack. Not a pure-stack function. Preconition: more than 2 elements in the stack. Postcondition: returns an element of type T. Class bfs ifstream f_in; ofstream f_out; char Mat[DIM][DIM]; int p[DIM][DIM],v[DIM][DIM], elem, DimX,DimY;; Position Start,Stop; PriorityQueue pq; Stack<Position> solution;
10

string filename, fileexit; Description: this is a class that sums the programs soling part. It contains the solving part, the reading part, and the saving/printing/writing part of the program. void solve() Description: this function will solve the maze problem by applying a breadth first search, and then, by a greedy algorithm it will return the shortest path or null, if there is no other solution. Precondition: Postcondition: void readFromFile() Description: this function will read from a file the into the matrix the elements. Precondition: a valid file. Postcondition: void init() Description: this is the initializing function. It will set the starting and the ending positions, as well the free and the closed spots of the maze. Precondition: Postcondition: void saveToFile() Description: this function will write to a file the solution of the program. Precondition: a valid file. Postcondition: void generalInfo() Description: prints the name of the two file: read and write. Precondition: valid files. Postcondition: void start() Description: sums up the functions from the bfs class into a starting point. Precondition: Postcondition: 3. How I organized my program. The whole code is divided into several files: bfs.h, main.cpp, Position.h, priorityqueue.h, RBtree.h, stack.h, testQue.h, ex.in, ex1.in, sol.txt, sol1.txt. bfs.h the part where I read/solve/write the program. main.cpp the part where I call the bfs class object to solve the problem. It is the actual starting point of the program. Position.h the part where I implement a class which helps me to work with the robot. It is a position class for the robot position and his movement in the maze. Its objects contains pairs of two ints, which are actually the coordinates of x and y. Priorityqueue.h the part where I implement an ADT Priority Queue over a Red-black tree. RBtree.h the part where I implement the actual Red-black tree. Stack.h the part where I implement an ADT Stack over a static vector.
11

testQueue.h the part where I test out my Priority Queue. ex.in, ex1.in files that contains mazes. sol.txt, sol1.txt files that contains the solutions of the mazes.
4. Present the idea/method used to solve the problem:

My problem statement is to solve the robot problem. From my point of view the best way to solve this problem was to apply a BFS on the maze, and, than to trace back on which path the robot can go. The BFS procedure looks like this: Breadth-First-Search( Maze m ) EnQueue( m.StartNode ) While Queue.NotEmpty c <- DeQueue If c is the goal Exit Else Foreach neighbor n of c If n "Unvisited" Mark n "Visited" EnQueue( n ) Mark c "Examined" End procedure So, the BFS algorithm will search through the whole maze for valid moves. If, for example, there is a free space, but surrounded by walls, it will not consider it as a valid position. After it searched the whole maze, it will trace back, applying a greedy algorithm and will give the shortest path to that point. If, by applying all the moves, it will not find a suitable way to get to the ending point of the matrix, it will then return with nothing, meaning that there is no solution in place for that maze. This BFS algorithm and the greedy algorithms are implemented in the bfs classs function solve(). Another important part of the program is reading the information into the maze, and, than saving it into files. For this matter I use 2 functions, readFromFile and saveToFile, which will do the job. In these files I work with the stream operators ifstream and ofstream. The readFromFile function will read a given file, and will place in the matrix the corresponding characters. The fixation of the starting and the ending part of the program is done in the init() function. Here the starting position is marked with * after it has been saved to a variable and the ending position is marked also with * after it has been saved to another variable. The saving part of the program is done, as I stated above, in the saveToFile function. In this function, the appropriate moves will be printed out, and the number of steps as well. If there is no valid path, it will write that there is no solution. 5. Apply justification to some appropriate example: I will take for example a maze of size 5x5, that will look like this: S * * * x * x * * * * x x * *
12

* x

x *

* *

x x

G x

The BFS will be applying like this: S (0,0) starting point. Try to go to (0, 1). If (0,1) = * and is not outside the border, it will mark it as reached. Try to go to (0, 2). If (0,2) = * and is not outside the border, it will mark it as reached. Try to go to (0, 3). If (0,3) = * and is not outside the border, it will mark it as reached. Try to go to (0, 4). If (0,4) = * and is not outside the border, it will mark it as reached, but (0,4) is != * so it will no marked it as reached. Instead it will go to the previous and try to go to another place. Try to go to (1, 3). If (1,3) = * and is not outside the border, it will mark it as reached. Try to go to (1, 4). If (1,4) = * and is not outside the border, it will mark it as reached, but (1,4) is != * so it will no marked it as reached. And so on. On its way back, it will store the appropriate positions into a stack called solutions by apply a greedy algorithm, from which they will be extracted as the optimal solution. If we take for example the same maze, but with closed S: S x x * * x x * * * * * * x * * x * x G x * * x x it will try to go to all positions, but being surrounded by closed spots it will return with no solution. 6. Application Input/Output: The application takes its input from ex.in or ex1.in and will output it in sol.txt and sol1.txt. The console menu of the programs lets the user choose from two options: given the already set mazes, it can either run the program, and see the result from the sol.txt or sol1.txt, or it can choose to see the execution time of each maze. Of course, the mazes can be changed to a maximum size of 99 on width or height. The console menu looks like this: 1. Solve from a given set. 2. See the execution time for the function from the given set. Type the 'menu' keyword to view again the menu. Type the 'q' character to exit the program. st The 1 option, as I said, it will only solve the maze, but the 2nd will show the timing also. The menu offers some additional help, if typed the keyword menu, it will print out again the menu, and if its typed the character q it will terminate the program. Note that any other command will be ignored by the program, and it will not be taken into consideration. 7. Execution time: For a maze of size 8x8 with valid paths the execution time is around 0.002 seconds. For a maze of size 8x8 with no valid paths the execution time is around 0.001 seconds. For a maze of size 16x16 with valid paths the execution time is around 0.003-0.005 seconds. For a maze of size 16x16 with no valid paths the execution time is around 0.003-0.005 seconds.
13

The execution time can be improved by using an advanced machine. 8. Suitable DS: In my opinion the Priority Queue and the Stack as DS are very good for this kind of problems. But, I think it can be done with other DS as with vectors or regular queues. Working with the PQ as the reached configuration DS and with Stack as the processed configuration DS helped me to solve this problem. The reached configurations could have been done using a Stack DS, but when working with PQ as the processed configuration DS it would have yield serious problems, and, probably, I would have had to use iterators over the PQ. Working with other DS would have changed a bit how the program works. In stl vectors or dequeues for example, you can access the elements by position, which can bring either advantages or disadvantages into the program.
9. PseudoCode for the main algorithm: pq: PriorityQueue solution: Stack current: Position next: Position subalg. solve() pq.push(Start) while (not pq.isEmpty()) current <- pq.pop() if (current=Stop) then break endif for (i=1; i<5) do next <- Position(current.getX()+xk[i], current.getY()+yk[i]) if (next.rdcorner(Position(DimX, DimY)) && next.lucorner(Position(-1,-1))) then if (not v[next.getX()][next.getY()] && Mat[next.getX()] [next.getY()]=='*' then v[next.getX()][next.getY()]=1 p[next.getX()][next.getY()]=i pq.push(next) end_if end_if end_for end_while if (p[Stop.getX()][Stop.getY()]=0) then return end_if for (current=Stop; p[current.getX()][current.getY()]) do solution.push(current) prev <- p[current.getX()][current.getY()] current <- Position(current.getX()-xk[prev], current.getY()-yk[prev] end_for end_subalg

14

Das könnte Ihnen auch gefallen