Data Structures and Algorithms Made Easy With Java Learn Data Structure Using Java in 7 Days
Data Structures and Algorithms Made Easy With Java Learn Data Structure Using Java in 7 Days
About
......................................................................................
......................................................................................
....... 1 Chapter 1: Getting started with
algorithms
......................................................................................
.............. 2
Section 1.1: A sample algorithmic problem
...............................................................................................................
.. 2 Section 1.2: Getting Started with Simple Fizz Buzz Algorithm in
Swift ...................................................................... 2
Chapter 4: Trees
......................................................................................
................................................................... 14
Section 4.1: Typical anary tree representation
......................................................................................................... 14
Section 4.2: Introduction
...............................................................................................................
.............................. 14
Section 4.3: To check if two Binary trees are same or not
..................................................................................... 15
Chapter 9: Graph
......................................................................................
................................................................... 30
Section 9.1: Storing Graphs (Adjacency Matrix)
....................................................................................................... 30
Section 9.2: Introduction To Graph Theory
..............................................................................................................
33
Section 9.3: Storing Graphs (Adjacency List)
...........................................................................................................
37
Section 9.4: Topological Sort
...............................................................................................................
...................... 39
Section 9.5: Detecting a cycle in a directed graph using Depth First
Traversal .................................................. 40
Section 9.6: Thorup's algorithm
...............................................................................................................
.................. 41
Appendix A: Pseudocode
......................................................................................
................................................. 249
Credits
......................................................................................
......................................................................................
250 You may also like
......................................................................................
................................................................ 252
Problem: Sorting
Input: A sequence of n keys, a_1, a_2, ..., a_n.
Output: The reordering of the input sequence such that a'_1 <= a' _2
<= ... <= a'_{n-1} <= a' _n
// for example
let number = [ 1 ,2 ,3 ,4 ,5 ] // here 3 is fizz and 5 is buzz
To find all the fizz and buzz, we must iterate through the array and
check which numbers are fizz and which are buzz. To do this, create
a for loop to iterate through the array we have initialised:
After this, we can simply use the "if else" condition and module
operator in swift ie - % to locate the fizz and buzz
for num in number {
if num % 3 == 0 {
print( " \( num) fizz" )
} else {
print( num)
}
}
} else if num % 3 == 0 {
print( " \( num) fizz" )
} else if num % 5 == 0 {
print( " \( num) buzz" )
} else {
print( num)
}
}
if num % 15 == 0 {
print( " \( num) fizz buzz" )
} else if num % 3 == 0 { print( " \( num) fizz" )
} else if num % 5 == 0 { print( " \( num) buzz" )
} else {
print( num)
}
}
As Simple as that, you can use any language of your choice and get
started Enjoy Coding
Chapter 2: Algorithm
Complexity
Section 2.1: Big-Theta notation
Unlike Big-O notation, which represents only upper bound of the
running time for some algorithm, Big-Theta is a tight bound; both
upper and lower bound. Tight bound is more precise, but also more
difficult to compute.
The Big-Theta notation is symmetric: f( x) = Ө( g( x)) <=> g( x) = Ө( f(
x))
An intuitive way to grasp it is that f( x) = Ө( g( x)) means that the
graphs of f(x) and g(x) grow in the same rate, or that the graphs
'behave' similarly for big enough values of x.
The full mathematical expression of the Big-Theta notation is as
follows:
Ө(f(x)) = {g: N0 -> R and c1, c2, n0 > 0, where c1 < abs(g(n) / f(n)),
for every n > n0 and abs is the absolute value }
An example
(g(n))
f(n) = f(n) = o(g(n)) ω(g(n))
Analogy
between the asymptotic comparison of f, g and real numbers a, b
1
/
2
n
^
2
7n
=
Θ
(
n
^
2
)
5n ^ 2 =
=
7n ^ 2 o ( n ^ 3 ) ω ( n)
The asymptotic
notations can be represented on a Venn diagram as follows:
Links
Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford
Stein. Introduction to Algorithms.
References
Formal definition and theorem are taken from the book "Thomas H.
Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein.
Introduction to Algorithms".
Now I need to make a side note : you might have noticed that if f = O(
g) and g = O( h) , then f = O( h) . For instance in our case, we have f
= O( n^ 3 ) , and f = O( n^ 4 ) ... In algorithm complexity analysis, we
frequently say f = O( g) to mean that f = O( g) and g = O( f) , which
can be understood as "g is the smallest Big-O for f". In mathematics
we say that such functions are Big-Thetas of each other.
How is it used ?
int find_max( const int * array, size_t len) { int max = INT_MIN;
for ( size_t i = 0 ; i < len; i++) {
The input size is the size of the array, which I called len in the code.
Let's count the operations.
int max = INT_MIN; size_t i = 0 ;
These two assignments are done only once, so that's 2 operations.
The operations that are looped are:
Since there are 3 operations in the loop, and the loop is done n times,
we add 3n to our already existing 2 operations to get 3n + 2 . So our
function takes 3n + 2 operations to find the max (its complexity is 3n
+ 2 ). This is a polynomial where the fastest growing term is a factor
of n, so it is O(n).
You probably have noticed that "operation" is not very well defined.
For instance I said that if ( max < array[ i]) was one operation, but
depending on the architecture this statement can compile to for
instance three instructions : one memory read, one comparison and
one branch. I have also considered all operations as the same, even
though for instance the memory operations will be slower than the
others, and their performance will vary wildly due for instance to
cache effects. I also have completely ignored the return statement,
the fact that a frame will be created for the function, etc. In the end it
doesn't matter to complexity analysis, because whatever way I
choose to count operations, it will only change the coefficient of the n
factor and the constant, so the result will still be O(n). Complexity
shows how the algorithm scales with the size of the input, but it isn't
the only aspect of performance!
Step Problem
1 n/2
2 n/4
3 n/8
4 n/16
return mid;
else if ( arr[ mid]< item)
low= mid+ 1 ;
else high= mid- 1 ;
}
return –1 ; // Unsuccessful result }
elif L [ h] > 0 :
b=h
elif L[ h] < 0 :
a=h
In the worst case, we have to wait until a and b are equal. But how
many operations does that take? Not n, because each time we enter
the loop, we divide the distance between a and b by about two.
Rather, the complexity is O(log n).
Explanation
Note: When we write "log", we mean the binary logarithm, or log base
2 (which we will write "log_2"). As O(log_2 n) = O(log n) (you can do
the math) we will use "log" instead of "log_2".
Let's call x the number of operations: we know that 1 = n / (2^x).
So 2^x = n, then x = log n
Conclusion When faced with successive divisions (be it by two or
by any number), remember that the complexity is logarithmic.
Chapter 4: Trees
Section 4.1: Typical anary tree
representation
Typically we represent an anary tree (one with potentially unlimited
children per node) as a binary tree, (one with exactly two children per
node). The "next" child is regarded as a sibling. Note that if a tree is
binary, this representation creates extra nodes.
We then iterate over the siblings and recurse down the children. As
most trees are relatively shallow - lots of children but only a few levels
of hierarchy, this gives rise to efficient code. Note human genealogies
are an exception (lots of levels of ancestors, only a few children per
level).
struct node
{
struct node * next; struct node * child; std:: string data;
}
while ( node) {
if ( node-> child) {
for ( i= 0 ; i< depth* 3 ; i++)
printf( " " );
printf( "{ \n " ):
printtree_r( node-> child, depth + 1 ); for ( i= 0 ; i< depth* 3 ; i++)
printf( " " );
printf( "{ \n " ):
for ( i= 0 ; i< depth* 3 ; i++)
printf( " " );
printf( "%s \n " , node-> data.c_str ()); node = node-> next; }
}
}
void printtree( node * root)
{
printree_r( root, 0 );
}
b)
Output should be
false.
Pseudo code for the same:
Following
the code snippet each image shows the execution visualization which
makes it easier to visualize how this code works.
class Node:
def __init__( self, val):
self.l_child = None
self.r_child = None
self.data = val
def
insert( root, node):
if root is None:
root = node
else :
if root.data > node.data :
if root.l_child is None:
def
in_order_print( root):
if not root:
return
in_order_print ( root.l_child )
print root.data
in_order_print( root.r_child )
def
pre_order_print( root):
if not root:
return
Explanation of cases:
1. When the node to be deleted is a leaf node then simply delete the
node and pass nullptr to its parent node.
2. When a node to be deleted is having only one child then copy the
child value to the node value and delete the child (Converted to
case 1)
3. When a node to be delete is having two childs then the minimum
from its right sub tree can be copied to the node and then the
minimum value can be deleted from the node's right subtree
(Converted to Case 2)
Note: The minimum in the right sub tree can have a maximum of
one child and that too right child if it's having the left child that means
it's not the minimum value or it's not following BST property.
The structure of a node in a tree and the code for Deletion:
struct node {
int data;
node * left, * right;
};
else {
if ( root-> left == nullptr && root-> right == nullptr) // Case 1 {
free( root);
root = nullptr;
}
else if ( root-> left == nullptr) // Case 2 {
node* temp = root;
root= root-> right;
free( temp);
}
else if ( root-> right == nullptr) // Case 2 {
node* temp = root;
root = root-> left;
free( temp);
}
else // Case 3 {
node* temp = root-> right;
}
}
return root;
}
Time complexity of above code is O(h ), where h is the height of the
tree.
else {
return lowestCommonAncestor( root-> right, node1, node2); }
}
return root
def in_order_place( self, root):
if not root:
return None
else :
self.in_order_place ( root.l_child ) print root.val
self.in_order_place ( root.r_child )
else :
print root.val
self.pre_order_place ( root.l_child ) self.pre_order_place ( root.r_child
)
else :
self.post_order_place ( root.l_child ) self.post_order_place (
root.r_child ) print root.val
r = Node( 3 )
node = BinarySearchTree()
nodeList = [ 1 , 8 , 5 , 12 , 14 , 6 , 15 , 7 , 16 , 8 ]
for nd in nodeList:
node.insert ( r, Node( nd))
1. It is empty
2. It has no subtrees
3. For every node x in the tree all the keys (if any) in the left sub tree
must be less than key(x) and all the keys (if any) in the right sub tree
must be greater than key(x).
int data;
node * left;
node * right;
};
void levelOrder( struct node * root){
if ( root == NULL ) return ;
queue< node *> Q;
Q.push ( root);
Q.pop ();
}}
struct node* newNode( int data) {
return ( node); }
int main(){
Pre-order
traversal(root) is traversing the node then left sub-tree of the node
and then the right sub-tree of the node.
So the pre-order traversal of above tree will be:
1245367
In-order traversal(root) is traversing the left sub-tree of the node
then the node and then right sub-tree of the node.
So the in-order traversal of above tree will be:
4251637
Post-order traversal(root) is traversing the left sub-tree of the
node then the right sub-tree and then the node.
So the post-order traversal of above tree will be: 4 5 2 6 7 3 1
Chapter 9: Graph
A graph is a collection of points and lines connecting some (possibly
empty) subset of them. The points of a graph are called graph
vertices, "nodes" or simply "points." Similarly, the lines connecting the
vertices of a graph are called graph edges, "arcs" or "lines."
Here you can see a table beside the graph, this is our adjacency
matrix. Here Matrix[i][j] = 1 represents there is an edge between i
and j . If there's no edge, we simply put Matrix[i][j] = 0 .
These edges can be weighted, like it can represent the distance
between two cities. Then we'll put the value in Matrix[i][j] instead of
putting 1.
The graph described above is Bidirectional or Undirected , that
means, if we can go to node 1 from node 2 , we can also go to
node 2 from node 1 . If the graph was Directed , then there
would've been arrow sign on one side of the graph. Even then, we
could represent it using adjacency matrix.
We represent the nodes that don't share edge by infinity . One thing
to be noticed is that, if the graph is undirected, the matrix becomes
symmetric .
for j from 1 to N
Take input -> Matrix[ i][ j]
endfor
endfor
We can also populate the Matrix using this common way:
endfor
For directed graphs, we can remove Matrix[n2][n1] = cost line.
The drawbacks of using Adjacency Matrix:
System .out .println ( "Enter the edges: <to> <from>" ); while ( count
<= e)
{
System .out .println ( "The adjacency matrix for the given graph is: " );
System .out .print ( " " );
for ( int i = 1 ; i <= v; i++)
}
catch ( Exception E) {
Did you know, almost all the problems of planet Earth can be
converted into problems of Roads and Cities, and solved? Graph
Theory was invented many years ago, even before the invention of
computer. Leonhard Euler wrote a paper on the Seven Bridges of
Königsberg which is regarded as the first paper of Graph Theory.
Since then, people have come to realize that if we can convert any
problem to this City-Road problem, we can solve it easily by Graph
Theory.
Graph:
Let's say, we have 6 cities. We mark them as 1, 2, 3, 4, 5, 6. Now we
connect the cities that have roads between each other.
This is a simple graph where some cities are shown with the roads
that are connecting them. In Graph Theory, we call each of these
cities Node or Vertex and the roads are called Edge. Graph is
simply a connection of these nodes and edges.
Let's assume there is a party going on. The people in the party are
represented by nodes and there is an edge between two people if
they shake hands. Then this graph is undirected because any person
A shake hands with person B if and only if B also shakes hands with
A . In contrast, if the edges from a person A to another person B
corresponds to A 's admiring B , then this graph is directed, because
admiration is not necessarily reciprocated. The former type of graph
is called an undirected graph and the edges are called undirected
edges while the latter type of graph is called a directed graph and the
edges are called directed edges.
Degree:
The degree of a vertex is the number of edges that are connected to
it. If there's any edge that connects to the vertex at both ends (a loop)
is counted twice.
In directed graphs, the nodes have two types of degrees:
In-degree: The number of edges that point to the node.
Out-degree: The number of edges that point from the node to other
nodes.
For undirected graphs, they are simply called degree.
end for
Return edge
input -> x, y, w
edge[ x] .push ( y)
cost[ x] .push ( w)
end for
Return edge, cost
From this one, we can easily find out the total number of nodes
connected to any node, and what these nodes are. It takes less time
than Adjacency Matrix. But if we needed to find out if there's an edge
between u and v , it'd have been easier if we kept an adjacency
matrix.
Let our graph be called dag (since it is a directed acyclic graph), and
let it contain 5 vertices:
A < dag.add_vertex ( Task( 4 )); B < dag.add_vertex ( Task( 5 )); C <
dag.add_vertex ( Task( 3 )); D < dag.add_vertex ( Task( 2 )); E <
dag.add_vertex ( Task( 7 ));
where we connect the vertices with directed edges such that the
graph is acyclic,
// A ---> C ----+
// | | |
// v v v
// B ---> D --> E
dag.add_edge ( A, B, Cooldown( 2 )); dag.add_edge ( A, C,
Cooldown( 2 )); dag.add_edge ( B, D, Cooldown( 1 )); dag.add_edge
( C, D, Cooldown( 1 )); dag.add_edge ( C, E, Cooldown( 1 ));
dag.add_edge ( D, E, Cooldown( 3 ));
C++ implementation:
#include <iostream> #include <list>
using namespace std;
#define NUM_V 4
bool helper(list< int > *graph, int u, bool * visited, bool * recStack) {
visited[u]= true ;
recStack[u]= true ;
list< int > :: iterator i;
for (i = graph[u].begin (); i!= graph[u].end (); ++i) {
if (recStack[*i]) //if vertice v is found in recursion stack of this DFS
traversal return true ;
else if (*i== u) //if there's an edge from the vertex to itself
return true ;
else if (!visited[*i])
{ if (helper(graph, *i, visited, recStack))
return true ;
}
}
recStack[u]= false ;
return false ;
}
/*
/The wrapper function calls helper function on each vertices which
have not been visited. Helper function returns true if it detects a back
edge in the subgraph(tree) or false.
*/
bool isCyclic(list< int > *graph, int V)
{
bool visited[V]; //array to track vertices already visited
bool recStack[V]; //array to track vertices in recursion stack of the
traversal.
list < int > * graph = new list< int > [NUM_V]; graph[0].push_back (1);
graph[0].push_back (2);
graph[1].push_back (2);
graph[2].push_back (0);
graph[2].push_back (3);
graph[3].push_back (3);
bool res = isCyclic(graph, NUM_V); cout << res<< endl;
Result: As shown below, there are three back edges in the graph.
One between vertex 0 and 2; between vertice 0, 1, and 2; and vertex
3. Time complexity of search is O(V+E) where V is the number of
vertices and E is the number of edges.
Section 9.6: Thorup's algorithm
Thorup's algorithm for single source shortest path for undirected
graph has the time complexity O(m), lower than Dijkstra.
Basic ideas are the following. (Sorry, I didn't try implementing it yet,
so I might miss some minor details. And the original paper is
paywalled so I tried to reconstruct it from other sources referencing it.
Please remove this comment if you could verify.)
There are ways to find the spanning tree in O(m) (not described
here). You need to "grow" the spanning tree from the shortest edge to
the longest, and it would be a forest with several connected
components before fully grown.
Select an integer b (b>=2) and only consider the spanning forests
with length limit b^k. Merge the components which are exactly the
same but with different k, and call the minimum k the level of the
component. Then logically make components into a tree. u is the
parent of v iff u is the smallest component distinct from v that fully
contains v. The root is the whole graph and the leaves are single
vertices in the original graph (with the level of negative infinity). The
tree still has only O(n) nodes.
Maintain the distance of each component to the source (like in
Dijkstra's algorithm). The distance of a component with more than
one vertices is the minimum distance of its unexpanded children. Set
the distance of the source vertex to 0 and update the ancestors
accordingly.
Consider the distances in base b. When visiting a node in level k the
first time, put its children into buckets shared by all nodes of level k
(as in bucket sort, replacing the heap in Dijkstra's algorithm) by the
digit k and higher of its distance. Each time visiting a node, consider
only its first b buckets, visit and remove each of them, update the
distance of the current node, and relink the current node to its own
parent using the new distance and wait for the next visit for the
following buckets.
When a leaf is visited, the current distance is the final distance of the
vertex. Expand all edges from it in the original graph and update the
distances accordingly.
Visit the root node (whole graph) repeatedly until the destination is
reached.
It is based on the fact that, there isn't an edge with length less than l
between two connected components of the spanning forest with
length limitation l, so, starting at distance x, you could focus only on
one connected component until you reach the distance x + l. You'll
visit some vertices before vertices with shorter distance are all visited,
but that doesn't matter because it is known there won't be a shorter
path to here from those vertices. Other parts work like the bucket sort
/ MSD radix sort, and of course, it requires the O(m) spanning tree.
void dfs( int node, vector< vector< int>>* graph, vector< bool>*
visited) {
// check whether node has been visited before
if ((* visited)[ node])
return ;
// set as visited to avoid visiting the same node twice
(* visited)[ node] = true ;
// perform some action here
cout << node;
// traverse to the adjacent nodes in depth-first manner
for ( int i = 0 ; i < (* graph)[ node] .size (); ++ i)
dfs((* graph)[ node][ i] , graph, visited);
Let's say, the distance of each node from the source is kept in d[]
array. As in, d[3] represents that d[3] time is taken to reach node 3
from source . If we don't know the distance, we will store infinity in
d[3] . Also, let cost[u][v] represent the cost of u-v . That means it
takes cost[u][v] to go from u node to v node.
We need to
understand Edge Relaxation. Let's say, from your house, that is
source , it takes 10 minutes to go to place A . And it takes 25
minutes to go to place B . We have,
d[ A] = 10
d[ B] = 25
Now let's say it takes 7 minutes to go from place A to place B , that
means:
cost[ A][ B] = 7
Then we can go to place B from source by going to place A from
source and then from place A , going to place B , which will take 10
+ 7 = 17 minutes, instead of 25 minutes. So,
d[ A] + cost[ A][ B] < d[ B]
Then we update,
d[ B] = d[ A] + cost[ A][ B]
This is called relaxation. We will go from node u to node v and if d[u]
+ cost[u][v] < d[v] then we will update d[v] = d[u] + cost[u][v]
.
In BFS, we didn't need to visit any node twice. We only checked if a
node is visited or not. If it was not visited, we pushed the node in
queue, marked it as visited and incremented the distance by 1. In
Dijkstra, we can push a node in queue and instead of updating it with
visited, we relax or update the new edge. Let's look at one example:
Let's assume, Node 1 is the Source . Then,
d[ 1 ] = 0
d[ 2 ] = d[ 3 ] = d[ 4 ] = infinity ( or a large value)
We set, d[2], d[3] and d[4] to infinity because we don't know the
distance yet. And the distance of source is of course 0 . Now, we go
to other nodes from source and if we can update them, then we'll
push them in the queue. Say for example, we'll traverse edge 1-2 .
As d[1] + 2 < d[2] which will make d[2] = 2 . Similarly, we'll
traverse edge 1-3 which makes d[3] = 5 .
We can clearly see that 5 is not the shortest distance we can cross to
go to node 3 . So traversing a node only once, like BFS, doesn't
work here. If we go from node 2 to node 3 using edge 2-3 , we
can update d[3] = d[2] + 1 = 3 . So we can see that one node can
be updated many times. How many times you ask? The maximum
number of times a node can be updated is the number of in-degree of
a node.
Let's see the pseudo-code for visiting any node multiple times. We
will simply modify BFS:
u < Q.pop ()
for all edges from u to v in G.adjacentEdges ( v) do
if distance[ u] + cost[ u][ v] < distance[ v]
distance[ v] = distance[ u] + cost[ u][ v]
end if
end for
end while
Return distance
This can be used to find the shortest path of all node from the source.
The complexity of this code is not so good. Here's why,
Dijkstra proposed, instead of going for First come, first serve method,
if we update the nearest nodes first, then it'll take less updates. If we
processed node 2 before, then node 3 would have been updated
before, and after updating node 4 accordingly, we'd easily get the
shortest distance! The idea is to choose from the queue, the node,
that is closest to the source . So we will use Priority Queue here so
that when we pop the queue, it will bring us the closest node u from
source . How will it do that? It'll check the value of d[u] with it.
end if
end for
end while
Return distance
Complexity:
The complexity of BFS is O(log(V+E)) where V is the number of
nodes and E is the number of edges. For Dijkstra, the complexity is
similar, but sorting of Priority Queue takes O(logV) . So the total
complexity is: O(Vlog(V)+E)
Below is a Java example to solve Dijkstra's Shortest Path Algorithm
using Adjacency Matrix
class ShortestPath {
static final int V= 9 ;
int minDistance( int dist[] , Boolean sptSet[]) {
dist[ src] = 0 ;
for ( int count = 0 ; count < V- 1 ; count++) {
int u = minDistance( dist, sptSet);
sptSet[ u] = true ;
for ( int v = 0 ; v < V; v++)
{ 4 , 0 , 8 , 0 , 0 , 0 , 0 , 11 , 0 } , { 0 , 8 , 0 , 7 , 0 , 4 , 0 , 0 , 2 } , { 0 , 0
, 7 , 0 , 9 , 14 , 0 , 0 , 0 } , { 0 , 0 , 0 , 9 , 0 , 10 , 0 , 0 , 0 } , { 0 , 0 , 4 ,
14 , 10 , 0 , 2 , 0 , 0 } , { 0 , 0 , 0 , 0 , 0 , 2 , 0 , 1 , 6 } , { 8 , 11 , 0 , 0 ,
0 , 0 , 1 , 0 , 7 } , { 0 , 0 , 2 , 0 , 0 , 0 , 6 , 7 , 0 } };
1. The "g" value - This is how far away this node is from the green
square.
2. The "h" value - This is how far away this node is from the red
square.
3. The "f" value - This is the sum of the "g" value and the "h" value.
This is the final number which tells us which node to move to.
We've
calculated the g, h, and f values for all of the blue nodes. Now, which
do we pick?
Whichever one has the lowest f value.
However, in this case, we have 2 nodes with the same f value, 5.
How do we pick between them?
Simply, either choose one at random, or have a priority set. I usually
prefer to have a priority like so: "Right > Up > Down > Left"
One of the nodes with the f value of 5 takes us in the "Down"
direction, and the other takes us "Left". Since Down is at a higher
priority than Left, we choose the square which takes us "Down".
I now mark the nodes which we calculated the heuristics for, but did
not move to, as orange, and the node which we chose as cyan:
Alright, now
let's calculate the same heuristics for the nodes around the cyan
node:
Again, we
choose the node going down from the cyan node, as all the options
have the same f value:
Let's
calculate the heuristics for the only neighbour that the cyan node has:
Alright,
since we will follow the same pattern we have been following:
Once more,
let's calculate the heuristics for the node's neighbour:
Let's move
there:
Finally, we
can see that we have a winning square beside us, so we move there,
and we are done.
_13
425
786
Final state :
123
456
78_
Heuristic to be assumed :
Let us consider the Manhattan distance between the current and final
state as the heuristic for this problem statement.
h( n) = | x - p | + | y - q |
where x and y are cell co- ordinates in the current state p and q are
cell co- ordinates in the final state Total cost function :
So the total cost function f( n) is given by,
f( n) = g( n) + h( n) , where g( n) is the cost required to reach the
current state from given initial state
Solution to example problem :
First we find the heuristic value required to reach the final state from
initial state. The cost function, g(n) = 0, as we are in the initial state
h( n) = 8
Now, the possible states that can be reached from initial state are
found and it happens that we can either move _ to right or
downwards.
So states obtained after moving those moves are:
1_3413
425_25
786786
(1)(2)
Again the total cost function is computed for these states using the
method described above and it turns out to be 6 and 7 respectively.
We chose the state with minimum cost which is state (1). The next
possible moves can be Left, Right or Down. We won't move Left as
we were previously in that state. So, we can move Right or Down.
13_123
4254_5
786786
(3)(4)
(3) leads to cost function equal to 6 and (4) leads to 4. Also, we will
consider (2) obtained before which has cost function equal to 7.
Choosing minimum from them leads to (4). Next possible moves can
be Left or Right or Down. We get states:
123123123
_4545_485
7867867_6
(5)(6)(7)
We get costs equal to 5, 2 and 4 for (5), (6) and (7) respectively. Also,
we have previous states (3) and (2) with 6 and 7 respectively. We
chose minimum cost state which is (6). Next possible moves are Up,
and Down and clearly Down will lead us to final state leading to
heuristic function value equal to 0.
In order to
choose which square to move to next, we need to take into account 2
heuristics:
1. The "g" value - This is how far away this node is from the green
square.
2. The "h" value - This is how far away this node is from the red
square.
3. The "f" value - This is the sum of the "g" value and the "h" value.
This is the final number which tells us which node to move to.
We've
calculated the g, h, and f values for all of the blue nodes. Now, which
do we pick?
Whichever one has the lowest f value.
However, in this case, we have 2 nodes with the same f value, 5.
How do we pick between them?
Simply, either choose one at random, or have a priority set. I usually
prefer to have a priority like so: "Right > Up > Down > Left"
One of the nodes with the f value of 5 takes us in the "Down"
direction, and the other takes us "Left". Since Down is at a higher
priority than Left, we choose the square which takes us "Down".
I now mark the nodes which we calculated the heuristics for, but did
not move to, as orange, and the node which we chose as cyan:
Alright, now
let's calculate the same heuristics for the nodes around the cyan
node:
Again, we
choose the node going down from the cyan node, as all the options
have the same f value:
Let's
calculate the heuristics for the only neighbour that the cyan node has:
Alright,
since we will follow the same pattern we have been following:
Once more,
let's calculate the heuristics for the node's neighbour:
Let's move
there:
Finally, we
can see that we have a winning square beside us, so we move there,
and we are done.
else if ( j== 0 )
dp[ i][ j] = i;
else if ( str1.charAt ( i- 1 ) == str2.charAt ( j- 1 ))
dp[ i][ j] = dp[ i- 1 ][ j- 1 ];
else {
dp[ i][ j] = 1 + Math .min ( dp[ i- 1 ][ j] , Math .min ( dp[ i][ j- 1 ] , dp[ i- 1
][ j- 1 ]));
}
}
}
return dp[ str1.length ()][ str2.length ()];
}
}
Output 3
The problem is, given certain jobs with their start time and end time,
and a profit you make when you finish the job, what is the maximum
profit you can make given no two jobs can be executed in parallel?
This one looks like Activity Selection using Greedy Algorithm, but
there's an added twist. That is, instead of maximizing the number of
jobs finished, we focus on making the maximum profit. The number of
jobs performed doesn't matter here.
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | A | B | C | D | E | F | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (2,5) |
(6,7) | (7,9) | (1,3) | (5,8) | (4,6) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 6 | 4 | 2 | 5 | 11 | 5 | +--------
-----------------+---------+---------+---------+---------+---------+---------+
The jobs are denoted with a name, their start and finishing time and
profit. After a few iterations, we can find out if we perform Job-A and
Job-E , we can get the maximum profit of 17. Now how to find this
out using an algorithm?
The first thing we do is sort the jobs by their finishing time in non-
decreasing order. Why do we do this? It's because if we select a job
that takes less time to finish, then we leave the most amount of time
for choosing other jobs. We have:
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Acc_Prof | 5 | 6 | 5 | 4 | 11 | 2 | +-------------------------+---------+--------
-+---------+---------+---------+---------+
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 5 | 4 | 11 | 2 | +-------------------------+---------+---------+-
--------+---------+---------+---------+
We check if Job[i] and Job[j] overlap, that is, if the finish time of
Job[j] is greater than Job[i] 's start time, then these two jobs can't
be done together. However, if they don't overlap, we'll check if
Acc_Prof[j] + Profit[i] > Acc_Prof[i] . If this is the case, we will
update Acc_Prof[i] = Acc_Prof[j] + Profit[i] . That is:
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 5 | 4 | 11 | 2 | +-------------------------+---------+---------+-
--------+---------+---------+---------+
Now Job[j] and Job[i] don't overlap. The total amount of profit we
can make by picking these two jobs is: Acc_Prof[j] + Profit[i] = 5
+ 5 = 10 which is greater than Acc_Prof[i] . So we update
Acc_Prof[i] = 10 . We also increment j by 1. We get,
ji
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 10 | 4 | 11 | 2 | +-------------------------+---------+---------
+---------+---------+---------+---------+
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 10 | 4 | 11 | 2 | +-------------------------+---------+---------
+---------+---------+---------+---------+
Now, Job[j] and Job[i] don't overlap, we get the accumulated profit
5 + 4 = 9 , which is greater than Acc_Prof[i] . We update
Acc_Prof[i] = 9 and increment j by 1.
ji
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 10 | 9 | 11 | 2 | +-------------------------+---------+---------
+---------+---------+---------+---------+
Again Job[j] and Job[i] don't overlap. The accumulated profit is: 6
+ 4 = 10 , which is greater than Acc_Prof[i] . We again update
Acc_Prof[i] = 10 . We increment j by 1. We get:
ji
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 10 | 10 | 11 | 2 | +-------------------------+---------+---------
+---------+---------+---------+---------+
+-------------------------+---------+---------+---------+---------+---------+---------
+ | Name | D | A | F | B | E | C | +-------------------------+---------+---------+-
--------+---------+---------+---------+ |(Start Time, Finish Time)| (1,3) |
(2,5) | (4,6) | (6,7) | (5,8) | (7,9) | +-------------------------+---------+---------
+---------+---------+---------+---------+ | Profit | 5 | 6 | 5 | 4 | 11 | 2 | +--------
-----------------+---------+---------+---------+---------+---------+---------+ |
Acc_Prof | 5 | 6 | 10 | 14 | 17 | 8 | +-------------------------+---------+---------
+---------+---------+---------+---------+
for j -> 1 to i- 1
if Job[ j] .finish_time <= Job[ i] .start_time if Acc_Prof[ j] + Profit[ i] >
Acc_Prof[ i] Acc_Prof[ i] = Acc_Prof[ j] + Profit[ i] endif endif
endfor
endfor
maxProfit = 0
for i -> 1 to n
if maxProfit < Acc_Prof[ i] maxProfit = Acc_Prof[ i] return maxProfit
The complexity of populating the Acc_Prof array is O(n2). The
array traversal takes O(n) . So the total complexity of this algorithm is
O(n2).
Now, If we want to find out which jobs were performed to get the
maximum profit, we need to traverse the array in reverse order and if
the Acc_Prof matches the maxProfit , we will push the name of
the job in a stack and subtract Profit of that job from maxProfit .
We will do this until our maxProfit > 0 or we reach the beginning
point of the Acc_Prof array. The pseudo-code will look like:
endif
endfor
The complexity of this procedure is: O(n) .
One thing to remember, if there are multiple job schedules that can
give us maximum profit, we can only find one job schedule via this
procedure.
}
//Recursive function
public int lcs( String str1, String str2, int m, int n){
if ( m== 0 || n== 0 )
return 0 ;
if ( str1.charAt ( m- 1 ) == str2.charAt ( n- 1 )) return 1 + lcs( str1,
str2, m- 1 , n- 1 );
else
return Math .max ( lcs( str1, str2, m- 1 , n) , lcs( str1, str2, m, n- 1 ));
}
//Iterative function
public int lcs2( String str1, String str2){
int lcs[][] = new int [ str1.length ()+ 1 ][ str2.length ()+ 1 ]; for ( int
i= 0 ; i<= str1.length (); i++){
for ( int j= 0 ; j<= str2.length (); j++){
if ( i== 0 || j== 0 ){
lcs [ i][ j] = 0 ;
}
else if ( str1.charAt ( i- 1 ) == str2.charAt ( j- 1 )){
fib( 1 ) fib( 0 )
Overlapping Sub-problems
Here fib(0),fib(1) and fib(3) are the overlapping sub-problems.fib(0) is
getting repeated 3 times, fib(1) is getting repeated 5 times and fib(3)
is getting repeated 2 times.
Implementation
public int fib( int n){
int f[] = new int [ n+ 1 ]; f[ 0 ]= 0 ; f[ 1 ]= 1 ;
for ( int i= 2 ; i<= n; i++){
f [ i]= f[ i- 1 ]+ f[ i- 2 ]; }
return f[ n];
}
Time Complexity O(n)
arr [ i][ j] = 0 ;
}
}
return max;
}
Time Complexity O(m*n)
memo = []
memo.append ( 1 ) # f( 1 ) = 1
memo.append ( 1 ) # f( 2 ) = 1
def fibonacci( n):
if len( memo) > n:
return memo[ n]
If we break the problem down into it's core elements you will notice
that in order to compute fibonacci( n) we need fibonacci( n- 1 ) and
fibonacci( n- 2 ) . Also we can notice that our base case will appear at
the end of that recursive tree as seen above.
This main benefit here is that we now have eliminated the recursive
stack while keeping the O( n) runtime. Unfortunately, we still have an
O( n) space complexity but that can be changed as well.
Advanced Iterative Dynamic Programming O( n) Runtime
complexity, O( 1 ) Space complexity, No recursive stack
def fibonacci( n):
memo = [ 1 ,1 ] # f( 1 ) = 1 , f( 2 ) = 1
for i in range ( 2 , n):
memo[ i% 2] = memo[ 0 ] + memo[ 1 ]
return memo[ n% 2]
To store these last 2 results I use an array of size 2 and simply flip
which index I am assigning to by using i % 2 which will alternate like
so: 0 , 1 , 0 , 1 , 0 , 1 , ..., i % 2 .
Notes
This leads to O( alpha( n)) time for each operation, where alpha is the
inverse of the fast-growing Ackermann function, thus it is very slow
growing, and can be considered O( 1 ) for practical purposes.
This makes the entire Kruskal's algorithm O( m log m + m) = O( m log
m) , because of the initial sorting.
Note
if vRoot == uRoot:
return
if random() % 2 == 0 : vRoot.parent = uRoot
else :
uRoot.parent = vRoot
if the root of the tree that e.first belongs to is not the same
as the root of the tree that e.second belongs to:
connect one of the roots to the other, thus merging two trees
return MST, which now a single- tree forest
uRoot.parent = vRoot
makeSet ( n)
for each edge e in G:
if findSet( e.first ) != findSet( e.second ): unionSet( e.first , e.second )
+------------------------+-----+-----+-----+-----+-----+-----+ | Character | a | b
| c | d | e | f | +------------------------+-----+-----+-----+-----+-----+-----+
|Frequency (in thousands)| 45 | 13 | 12 | 16 | 9 | 5 | +-----------------------
-+-----+-----+-----+-----+-----+-----+
+------------------------+-----+-----+-----+-----+-----+-----+
| Character | a | b | c | d | e | f |
+------------------------+-----+-----+-----+-----+-----+-----+
| Fixed-length Codeword | 000 | 001 | 010 | 011 | 100 | 101 |
+------------------------+-----+-----+-----+-----+-----+-----+
|Variable-length Codeword| 0 | 101 | 100 | 111 | 1101| 1100|
+------------------------+-----+-----+-----+-----+-----+-----+
Compression Technique:
1. Create a leaf node for each symbol and add it to the priority queue.
2. While there is more than one node in the queue:
1. Remove the two nodes of highest priority from the queue.
2. Create a new internal node with these two nodes as children and
with frequency equal to the sum of the two nodes' frequency.
3. Add the new node to the queue.
3. The remaining node is the root node and the Huffman tree is
complete.
For our example:
n = node( C[ i])
Q. push ( n)
end for
while Q.size () is not equal to 1
Z = new node()
Z.left = x = Q.pop
Z.right = y = Q.pop
Z.frequency = x.frequency + y.frequency
Q.push ( Z)
end while
Return Q
Although linear-time given sorted input, in general cases of arbitrary
input, using this algorithm requires presorting. Thus, since sorting
takes O(nlogn) time in general cases, both methods have same
complexity.
Since n here is the number of symbols in the alphabet, which is
typically very small number (compared to the length of the message
to be encoded), time complexity is not very important in the choice of
this algorithm.
Decompression Technique:
current = root
// root represents the root of Huffman Tree // S refers to bit-stream to
be decompressed
while current.left != NULL and current.right != NULL if S[ i] is
equal to '0'
current := current.left
else
current := current.right
endif
i := i+ 1
endwhile
print current.symbol
endfor
Greedy Explanation:
Huffman coding looks at the occurrence of each character and stores
it as a binary string in an optimal way. The idea is to assign variable-
length codes to input input characters, length of the assigned codes
are based on the frequencies of corresponding characters. We create
a binary tree and operate on it in bottom-up manner so that the least
two frequent characters are as far as possible from the root. In this
way, the most frequent character gets the smallest code and the least
frequent character gets the largest code.
References:
Remember, you can't take two classes at the same time. That means
you can't take class 1 and 2 because they share a common time
10.30 A.M to 11.00 A.M. However, you can take class 1 and 3
because they don't share a common time. So your task is to take
maximum number of classes as possible without any overlap. How
can you do that?
Analysis
Lets think for the solution by greedy approach.First of all we randomly
chose some approach and check that will work or not.
sort the activity by start time that means which activity start
first we will take them first. then take first to last from sorted list and
check it will intersect from previous taken activity or not. If the current
activity is not intersect with the previously taken activity, we will
perform the activity otherwise we will not perform. this approach will
work for some cases like
Activity No. start time end time
1 11.00 A.M 1.30P.M
2 11.30 A.M 12.00P.M
3 1.30 P.M 2.00P.M
4 10.00 A.M 11.00AM
the sorting order will be 4-->1-->2-->3 .The activity 4--> 1--> 3 will be
performed and the activity 2 will be skipped. the maximum 3 activity
will be performed. It works for this type of cases. but it will fail for
some cases. Lets apply this approach for the case
->3 will be performed. So our approach will not work for the above
case. Let's try another approach
if we sort the activity by time duration the sort order will be 2--> 3 ---
>1 . and if we perform activity No. 2 first then no other activity can be
performed. But the answer will be perform activity 1 then perform 3 .
So we can perform maximum 2 activity.So this can not be a solution
of this problem. We should try a different approach.
The solution
Sort the Activity by ending time that means the activity
finishes first that come first. the algorithm is given below
sort the activity by its ending times , So sort order will be 1-->5-->2--
>4-->3.. the answer is 1-->3 these two activities will be performed.
ans that's the answer. here is the sudo code.
1. sort: activities
2. perform first activity from the sorted list of activities.
3. Set : Current_activity := first activity
4. set: end_time := end_time of Current activity
5. go to next activity if exist, if not exist terminate .
6. if start_time of current activity <= end_time : perform the activity
and go to 4
7. else: got to 5.
General case. How to give 99€ with coins of 10€, 7€ and 5€?
Here, giving coins of 10€ until we are left with 9€ leads obviously to
no solution. Worse than that a solution may not exist. This problem is
in fact np-hard, but acceptable solutions mixing greediness and
memoization exist. The idea is to explore all the possibilies and
pick the one with the minimal number of coins.
Some 1
| x -> if x < 0 then
(* we don't reach 0, we discard this solution*)
None
else
(*we search the smallest path different to None with the remaining
pieces*) optmin (optsucc (loop x)) acc
in
(*we call onepiece forall the pieces*)
List.fold_left onepiece None money_system
in loop amount
Note : We can remark that this procedure may compute several
times the change set for the same value. In practice, using
memoization to avoid these repetitions leads to faster (way faster)
results.
Chapter 18: Applications of
Greedy technique
Section 18.1: O☐ine Caching
The caching problem arises from the limitation of finite space. Lets
assume our cache C has k pages. Now we want to process a
sequence of m item requests which must have been placed in the
cache before they are processed.Of course if m<= k then we just put
all elements in the cache and it will work, but usually is m>> k.
There are numerous greedy strategies for this problem, lets look at
some:
1. First in, first out (FIFO) : The oldest page gets evicted
2. Last in, first out (LIFO) : The newest page gets evicted
3. Last recent out (LRU) : Evict page whose most recent access
was earliest
4. Least frequently requested(LFU) : Evict page that was least
frequently requested
5. Longest forward distance (LFD) : Evict page in the cache
that is not requested until farthest in the future.
Attention: For the following examples we evict the page with the
smallest index, if more than one page could be evicted.
Example (FIFO)
Let the cache size be k= 3 the initial cache a,b,c and the request
a,a,d,e,b,b,a,c,f,d,e,a,f,b,e,c:
Request a a d e b b a c f d e a f b e c
cache 1 a a d d d d a a a d d d f f f c
cache 2 b b b e e e e c c c e e e b b b
cache 3 c c c c b b b b f f f a a a e e
cache miss x x x x x x x x x x x x x
Thirteen cache misses by sixteen requests does not sound very
optimal, lets try the same example with another strategy:
Example (LFD)
Let the cache size be k= 3 the initial cache a,b,c and the request
a,a,d,e,b,b,a,c,f,d,e,a,f,b,e,c:
Request a a d e b b a c f d e a f b e c cache 1 a a d e e e e e
e e e e e e e c cache 2 b b b b b b a a a a a a f f f f cache 3 c c c c c c
ccfddddbbb
cache miss x x x x x x x x
Eight cache misses is a lot better.
Selftest : Do the example for LIFO, LFU, RFU and look what
happend.
The following example programm (written in C++) consists of two
parts: The skeleton is a application, which solves the problem
dependent on the chosen greedy strategy:
#include <iostream> #include <memory>
using namespace std;
const int cacheSize = 3; const int requestLength = 16;
const char request[] = {'a' ,'a' ,'d' ,'e' ,'b' ,'b' ,'a' ,'c' ,'f' ,'d' ,'e' ,'a' ,'f' ,'b'
,'e' ,'c' }; char cache[] = {'a' ,'b' ,'c' };
// for reset
char originalCache[] = {'a' ,'b' ,'c' };
class Strategy {
public :
Strategy(std:: string name) : strategyName(name) {} virtual
~Strategy() = default ;
}
cout << " \n Total cache misses: " << cntMisses << endl; }
for (int i= 0; i< 5; ++i) delete selectedStrategy[i]; }
The basic idea is simple: for every request I have two calls two my
strategy:
1. apply : The strategy has to tell the caller which page to use
2. update : After the caller uses the place, it tells the strategy
whether it was a miss or not. Then the strategy may update its
internal data. The strategy LFU for example has to update the hit
frequency for the cache pages, while the LFD strategy has to
recalculate the distances for the cache pages.
// all old pages get older, the new one get 0 for ( int i= 0 ; i<
cacheSize; ++ i)
{
private :
int age[ cacheSize];
};
FIFO just needs the information how long a page is in the cache (and
of course only relative to the other pages). So the only thing to do is
wait for a miss and then make the pages, which where not evicted
older. For our example above the program solution is:
Strategy: FIFO
Cache initial: ( a,b,c)
{
int newest = 0 ; for ( int i= 0 ; i< cacheSize; ++ i) {
// all old pages get older, the new one get 0 for ( int i= 0 ; i<
cacheSize; ++ i)
{
// here oldest mean not used the longest int apply( int requestIndex)
override {
int oldest = 0 ;
for ( int i= 0 ; i< cacheSize; ++ i) {
if ( cache[ i] == request[ requestIndex]) return i;
else if ( age[ i] > age[ oldest]) oldest = i;
}
return oldest; }
void update( int cachePos, int requestIndex, bool cacheMiss)
override {
// all old pages get older, the used one get 0 for ( int i= 0 ; i<
cacheSize; ++ i)
{
if ( i != cachePos) age[ i]++;
else
age[ i] = 0 ; }
}
private :
int age[ cacheSize];
};
{
nextUse[ cachePos] = calcNextUse( requestIndex, cache[
cachePos]);
}
private :
return requestLength + 1 ; }
// next usage of page int nextUse[ cacheSize]; };
The LFD strategy is different from everyone before. Its the only
strategy that uses the future requests for its decission who to evict.
The implementation uses the function calcNextUse to get the page
which next use is farthest away in the future. The program solution is
equal to the solution by hand from above: Strategy: LFD
Cache initial: ( a,b,c)
The LFD strategy is optimal, but there is a big problem. Its an optimal
offline solution. In praxis caching is usually an online problem, that
means the strategy is useless because we cannot now the next time
we need a particular item. The other four strategies are also online
strategies. For online problems we need a general different approach.
You have a ticket automat which gives exchange in coins with values
1, 2, 5, 10 and 20. The dispension of the exchange can be seen as a
series of coin drops until the right value is dispensed. We say a
dispension is optimal when its coin count is minimal for its
value.
// read some coin values, sort them descending, // purge copies and
guaratee the 1 coin is in it std:: vector < unsigned int >
readInCoinValues();
int main() {
std:: vector < unsigned int > coinValues; int ticketPrice;
int paidMoney;
coinCount.push_back (countCoins); }
// print out result
cout << "the difference " << paidMoney - ticketPrice << " is paid with:
" << endl;
for (unsigned int i= 0; i < coinValues.size (); ++i) {
if (coinCount[i] > 0)
cout << coinCount[i] << " coins with value " << coinValues[i] << endl;
}
return 0; }
int coinValue;
cout << "Coin value (<1 to stop): " ; cin >> coinValue;
if (coinValue > 0)
coinValues.push_back (coinValue);
else break ; }
// sort values
sort(coinValues.begin (), coinValues.end (), std:: greater < int > ());
// print array
cout << "Coin values: " ;
for (auto i : coinValues) cout << i << " " ;
cout << endl;
return coinValues; }
Be aware there is now input checking to keep the example simple.
One example output:
Or as C++ program:
#include <iostream>
#include <utility>
#include <tuple>
#include <vector>
#include <algorithm>
// step 3:
for (int i= 0; i< jobCnt; ++i) {
for (auto i : A)
cout << "(" << jobs[i].first << "," << jobs[i].second << ") " ;
cout << endl;
return 0; }
The output for this example is: Compatible: ( 1 ,3 ) ( 4 ,5 ) ( 6 ,8 ) ( 9
,10 )
The implementation of the algorithm is clearly in Θ(n^2). There is a
Θ(n log n) implementation and the interested reader may continue
reading below (Java Example).
Now we have a greedy algorithm for the interval scheduling problem,
but is it optimal?
Proposition: The greedy algorithm earliest finish time is
optimal.
Proof: (by contradiction)
Assume greedy is not optimal and i1,i2,...,ik denote the set of jobs
selected by greedy. Let j1,j2,...,jm denote the set of jobs in an
optimal solution with i1= j1,i2= j2,...,ir= jr for the largest possible
value of r.
return 1 ; }
static public int schedule( Job jobs[]) {
Arrays .sort ( jobs, new JobComparator());
int n = jobs.length ;
int table[] = new int [ n]; table[ 0 ] = jobs[ 0 ] .profit ;
123456
tj3 2 1 4 3 2
dj6 8 9 9 10 11
Job 3 2 2 5 5 5 4 4 4 4 1 1 1 6 6
Time 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Lj -8 -5 -4 1 7 4
The solution L= 7 is obviously not optimal. Lets look at some greedy
strategies:
12
tj 1 5
dj10 5
( 0 ,2 ) Lateness: 2
( 2 ,5 ) Lateness: 2
( 5 ,8 ) Lateness: 0
( 8 ,9 ) Lateness: 0
( 9 ,12 ) Lateness: 3
( 12 ,17 ) Lateness: 6
( 17 ,21 ) Lateness: 8
( 21 ,23 ) Lateness: 6
( 23 ,25 ) Lateness: 3
( 25 ,26 ) Lateness: 1
maximal lateness is 8
Lets assume the jobs are numbered so that d1<= d2<= ...<= dn. We
say a inversion of a schedule is a pair of jobs i and j so that i< j but j
is scheduled before i. Due to its definition the earliest deadline
first schedule has no inversions. Of course if a schedule has an
inversion it has one with a pair of inverted jobs scheduled
consecutively.
Mj = fi- dj ( definition)
<= fi- di ( since i and j are exchanged) <= Li
That means the lateness after swap is less or equal than before. This
concludes the proof.
Proposition: The earliest deadline first schedule S is optimal.
Proof: (by contradiction)
At first we'll select a source node. Let's say, node-1 is our source
. Now we'll add the edge from node-1 that has the minimum cost to
our subgraph. Here we mark the edges that are in the subgraph using
the color blue . Here 1-5 is our desired edge.
Now we consider all the edges from node-1 and node-5 and take
the minimum. Since 1-5 is already marked, we
take 1-2 .
This time, we consider node-1 , node-2 and node-5 and take the
minimum edge which is 5-4 .
The next step is important. From node-1 , node-2 , node-5 and
node-4 , the minimum edge is 2-4 . But if we select that one, it'll
create a cycle in our subgraph. This is because node-2 and node-4
are already in our subgraph. So taking edge 2-4 doesn't benefit us.
We'll select the edges in such way that it adds a new node in our
subgraph . So we
select edge 4-8 .
If we continue this way, we'll select edge 8-6 , 6-7 and 4-3 . Our
subgraph will look like:
This is our desired subgraph, that'll give us the minimum spanning
tree. If we remove the edges that we didn't
select, we'll get:
Complexity:
// cost of edge(u, v)
parent[ v] := u key[ v] := Edge( u, v)
end if
end for
end while
Here key[] stores the minimum cost of traversing node-v .
parent[] is used to store the parent node. It is useful for traversing
and printing the tree.
Below is a simple program in Java:
import java.util.* ;
System .out .print ( " " + LinkCost[ i][ j] + " " ); else
System .out .print ( " * " );
System .out .println ();
}
}
public int unReached( boolean [] r)
{
boolean done = true ;
for ( int i = 0 ; i < r.length ; i++ ) if ( r[ i] == false )
return i;
return 1 ;
}
public void Prim( )
{
int i, j, k, x, y;
boolean [] Reached = new boolean [ NNodes]; int [] predNode =
new int [ NNodes]; Reached[ 0 ] = true ;
for ( k = 1 ; k < NNodes; k++ )
{
Reached[ k] = false ;
}
predNode[ 0 ] = 0 ;
printReachSet( Reached ); for ( k = 1 ; k < NNodes; k++) {
x=y=0;
for ( i = 0 ; i < NNodes; i++ ) for ( j = 0 ; j < NNodes; j++ ) {
if ( Reached[ i] && ! Reached[ j] && LinkCost[ i][ j] < LinkCost[ x][ y] )
{
x = i;
y = j;
}
}
System .out .println ( "Min cost edge: (" +
+ x + "," +
+ y + ")" +
"cost = " + LinkCost[ x][ y]); predNode[ y] = x;
Reached[ y] = true ;
printReachSet( Reached );
System .out .println ();
}
int [] a= predNode;
for ( i = 0 ; i < NNodes; i++ )
System .out .println ( a[ i] + " --> " + i ); }
void printReachSet( boolean [] Reached )
if ( Reached[ i] )
System .out .print ( i + " " ); //System.out.println();
}
public static void main( String [] args)
{
int [][] conn = {{ 0 ,3 ,0 ,2 ,0 ,0 ,0 ,0 ,4 } , // 0 { 3 ,0 ,0 ,0 ,0 ,0 ,0 ,4 ,0 }
, // 1 { 0 ,0 ,0 ,6 ,0 ,1 ,0 ,2 ,0 } , // 2 { 2 ,0 ,6 ,0 ,1 ,0 ,0 ,0 ,0 } , // 3 { 0 ,0
,0 ,1 ,0 ,0 ,0 ,0 ,8 } , // 4 { 0 ,0 ,1 ,0 ,0 ,0 ,8 ,0 ,0 } , // 5 { 0 ,0 ,0 ,0 ,0 ,8
,0 ,0 ,0 } , // 6 { 0 ,4 ,2 ,0 ,0 ,0 ,0 ,0 ,0 } , // 7 { 4 ,0 ,0 ,0 ,8 ,0 ,0 ,0 ,0 } //
8 };
Graph G = new Graph( conn);
G.Prim ();
}
}
The idea of this algorithm is to go through all the edges of this graph
one-by-one in some random order. It can be any random order. But
you must ensure, if u-v (where u and v are two vertices in a graph) is
one of your orders, then there must be an edge from u to v . Usually
it is taken directly from the order of the input given. Again, any
random order will work.
After selecting the order, we will relax the edges according to the
relaxation formula. For a given edge u-v going from u to v the
relaxation formula is:
if distance[ u] + cost[ u][ v] < d[ v] d[ v] = d[ u] + cost[ u][ v]
That is, if the distance from source to any vertex u + the weight of
the edge u-v is less than the distance from source to another
vertex v , we update the distance from source to v . We need to
relax the edges at most (V-1) times where V is the number of edges
in the graph. Why (V-1) you ask? We'll explain it in another example.
Also we are going to keep track of the parent vertex of any vertex,
that is when we relax an edge, we will set:
parent[ v] = u
It means we've found another shorter path to reach v via u . We will
need this later to print the shortest path from source to the destined
vertex.
You can take any sequence you want. If we relax the edges once,
what do we get? We get the distance from source to all other
vertices of the path that uses at most 1 edge. Now let's relax the
edges and update the values of d[] . We get:
d [ i] := infinity
parent[ i] := NULL
end for
d[ source] := 0
for i from 1 to n- 1
flag := false
for all edges from ( u,v) in Graph
if d[ u] + cost[ u][ v] < d[ v]
d[ v] := d[ u] + cost[ u][ v]
parent[ v] := u
flag := true
end if
end for
if flag == false break
end for
Return d
To keep track of negative cycle, we can modify our code using the
procedure described here. Our completed pseudo-code will be:
d [ i] := infinity
parent[ i] := NULL
end for
d[ source] := 0
for i from 1 to n- 1
flag := false
for all edges from ( u,v) in Graph
if d[ u] + cost[ u][ v] < d[ v]
d[ v] := d[ u] + cost[ u][ v]
parent[ v] := u
flag := true
end if
end for
if flag == false
break
end for
for all edges from ( u,v) in Graph
if d[ u] + cost[ u][ v] < d[ v]
Return "Negative Cycle Detected"
end if
end for
Return d
Printing Path:
To print the shortest path to a vertex, we'll iterate back to its parent
until we find NULL and then print the vertices. The pseudo-code will
be:
return
PathPrinting( v)
print -> u
Complexity:
Since we need to relax the edges maximum (V-1) times, the time
complexity of this algorithm will be equal to O(V * E) where E
denotes the number of edges, if we use adjacency list to represent
the graph. However, if adjacency matrix is used to represent the
graph, time complexity will be O(V^3) . Reason is we can iterate
through all edges in O(E) time when adjacency list is used, but it
takes O(V^2) time when adjacency matrix is used.
Let's pick
vertex 1 as the source . After applying Bellman-Ford's single
source shortest path algorithm to the graph, we'll find out the
distances from the source to all the other vertices.
This is how the graph looks like after (V-1) = 3 iterations. It should be
the result since there are 4 edges, we need at most 3 iterations to
find out the shortest path. So either this is the answer, or there is a
negative weight cycle in the graph. To find that, after (V-1) iterations,
we do one more final iteration and if the distance continues to
decrease, it means that there is definitely a negative weight cycle in
the graph.
For this example: if we check 2-3 , d[2] + cost[2][3] will give us 1
which is less than d[3] . So we can conclude that there is a negative
cycle in our graph.
flag := false
for all edges from ( u,v) in Graph
if d[ u] + cost[ u][ v] < d[ v]
d[ v] := d[ u] + cost[ u][ v]
flag := true
end if
end for
if flag == false
break
end for
for all edges from ( u,v) in Graph
if d[ u] + cost[ u][ v] < d[ v]
Here, the source vertex is 1. We will find out the shortest distance
between the source and all the other vertices. We can clearly see
that, to reach vertex 4 , in the worst case, it'll take (V-1) edges.
Now depending on the order in which the edges are discovered, it
might take (V-1) times to discover vertex 4 . Didn't get it? Let's use
Bellman-Ford algorithm to find out the shortest path here:
We can see that our relaxation process only changed d[2] . Our
graph will look like:
Second iteration:
This time the relaxation process changed d[3] . Our graph will look
like:
Third
iteration:
Our third iteration finally found out the shortest path to 4 from 1 . Our
graph will look like:
So, it
took 3 iterations to find out the shortest path. After this one, no matter
how many times we relax the edges, the values in d[] will remain the
same. Now, if we considered another sequence:
We'd get:
Our very first iteration has found the shortest path from source to all
the other nodes. Another sequence 1->2 , 3->4 , 2->3 is possible,
which will give us shortest path after 2 iterations. We can come to the
decision that, no matter how we arrange the sequence, it won't take
more than 3 iterations to find out shortest path from the source in
this example.
We can conclude that, for the best case, it'll take 1 iteration to find out
the shortest path from source . For the worst case, it'll take (V-1)
iterations, which is why we repeat the process of relaxation (V-1)
times.
3. Calculate
Delx =| x2 – x1 |
Dely = | y2 – y1 |
If p < 0 then
X1 = x1 + 1
Pot(x1,y1)
P = p+ 2dely
Else
X1 = x1 + 1
Y1 = y1 + 1
Plot(x1,y1)
P = p + 2 dely – 2 * delx
End if
End for
6. END
Source Code:
int main() {
int gdriver= DETECT,gmode;
int x1,y1,x2,y2,delx,dely,p,i;
initgraph(& gdriver,& gmode,"c: \\ TC \\ BGI" );
printf ( "Enter the intial points: " ); scanf( "%d" ,& x1);
scanf( "%d" ,& y1);
printf( "Enter the end points: " ); scanf( "%d" ,& x2);
scanf( "%d" ,& y2);
putpixel( x1,y1,RED);
delx = fabs( x2- x1); dely= fabs( y2- y1); p=( 2 * dely) delx;
for ( i= 0 ; i< delx; i++){ if ( p< 0 )
{
x1 = x1+ 1 ;
putpixel( x1,y1,RED); p= p+( 2 * dely);
}
else
{
x1 = x1+ 1 ;
y1= y1+ 1 ;
putpixel( x1,y1,RED); p= p+( 2 * dely)-( 2 * delx);
}
}
getch();
closegraph(); return 0 ;
}
Else
X1 = x1 + 1
Y1 = y1 + 1
Plot(x1,y1)
P = p + 2 delx – 2 * dely
End if
End for
6. END
Source Code:
return 0 ;
}
Chapter 22: Floyd-Warshall
Algorithm
Section 22.1: All Pair Shortest Path
Algorithm
Floyd-Warshall 's algorithm is for finding shortest paths in a weighted
graph with positive or negative edge weights. A single execution of
the algorithm will find the lengths (summed weights) of the shortest
paths between all pair of vertices. With a little variation, it can print
the shortest path and can detect negative cycles in a graph.
FloydWarshall is a Dynamic-Programming algorithm.
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
||1|2|3|4|||1|2|3|4|
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
| 1 | 0 | 3 | 6 | 15 | | 1 | N | 1 | 1 | 1 |
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
| 2 | inf | 0 | -2 | inf | | 2 | N | N | 2 | N |
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
| 3 | inf | inf | 0 | 2 | | 3 | N | N | N | 3 |
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
| 4 | 1 | inf | inf | 0 | | 4 | 4 | N | N | N |
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
distance path
Since there is no loop, the diagonals are set N . And the distance
from the vertex itself is 0 .
end if
+-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+ | | 1 | 2 | 3 | 4 |
| | 1 | 2 | 3 | 4 | +-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+
| 1 | 0 | 3 | 1 | 3 | | 1 | N | 1 | 2 | 3 | +-----+-----+-----+-----+-----+ +-----+--
---+-----+-----+-----+ | 2 | 1 | 0 | -2 | 0 | | 2 | 4 | N | 2 | 3 | +-----+-----+-----
+-----+-----+ +-----+-----+-----+-----+-----+ | 3 | 3 | 6 | 0 | 2 | | 3 | 4 | 1 | N |
3 | +-----+-----+-----+-----+-----+ +-----+-----+-----+-----+-----+ | 4 | 1 | 4 | 2
| 0 | | 4 | 4 | 1 | 2 | N | +-----+-----+-----+-----+-----+ +-----+-----+-----+-----
+-----+ distance path
This is our shortest distance matrix. For example, the shortest
distance from 1 to 4 is 3 and the shortest distance between 4 to 3 is
2 . Our pseudo-code will be:
Procedure Floyd- Warshall( Graph):
for k from 1 to V // V denotes the number of vertex for i from 1 to V
for j from 1 to V
if distance[ i][ j] > distance[ i][ k] + distance[ k][ j] distance[ i][ j] :=
distance[ i][ k] + distance[ k][ j] path[ i][ j] := path[ k][ j]
end if
end for
end for
end for
Printing the path:
To print the path, we'll check the Path matrix. To print the path from
u to v , we'll start from path[u][v] . We'll set keep changing v =
path[u][v] until we find path[u][v] = u and push every values of
path[u][v] in a stack. After finding u , we'll print u and start popping
items from the stack and print them. This works because the path
matrix stores the value of the vertex which shares the shortest path to
v from any other node. The pseudo-code will be:
s = Stack ()
S.push ( destination)
while Path[ source][ destination] is not equal to source
Auxiliary Space: O( n)
Time Complexity: O( n^ 2 )
Chapter 24: Multithreaded
Algorithms
Examples for some multithreaded algorithms.
parallel for j = 1 to n
C[ i][ j] = 0
pour k = 1 to n
y [ i] = 0
parallel for i = 1 to n
for j = 1 to n
y[ i] = y[ i] + A[ i][ j]* x[ j]
return y
B [ s] = A[ p]
else
T = new Array ( n) //create a new array T of size n
q = floor(( p+ r)/ 2 ))
q_prime = q- p+ 1
spawn p- merge- sort( A,p,q,T,1 )
p- merge- sort( A,q+ 1 ,r,T,q_prime+ 1 )
sync
p- merge( T,1 ,q_prime,q_prime+ 1 ,n,B,s)
Searching
1. We keep matching characters txt[i] and pat[j] and keep
incrementing i and j while pat[j] and txt[i] keep matching.
Implementaion in Java
public class KMP {
public static void main( String [] args) {
// TODO Auto-generated method stub
String str = "abcabdabc" ;
String pattern = "abc" ;
KMP obj = new KMP();
System .out .println ( obj.patternExistKMP ( str.toCharArray () ,
pattern.toCharArray ()));
}
public int [] computeLPS( char [] str){ int lps[] = new int [
str.length ];
lps [ 0 ] = 0 ;
int j = 0 ;
for ( int i = 1 ; i< str.length ; i++){
} else {
if ( j!= 0 ){
j = lps[ j- 1 ];
} else {
lps[ i] = j+ 1 ; i++;
}
}
}
return lps; }
} else {
if ( j!= 0 ){
j = lps[ j- 1 ];
} else {
i++;
}
}
}
1. Insert
2. Remove
3. Replace
For Example
For iteration 2
For dp[2][1] we have to check that to convert az to a we need to
remove z , hence dp[2][1] will be 1 .Similary for dp[2][2] we need
to replace z with b , hence dp[2][2] will be 1 .So after 2nd iteration
our dp[] array will look like.
Implementation in Java
Time Complexity O( n^ 2 )
Proof: At the beginning of each phase (except for the first one) FWF
has a cache miss and cleared the cache. that means we have k
empty pages. In every phase are maximal k different pages
requested, so there will be now eviction during the phase. So FWF is
a marking algorithm.
Lets assume LRU is not a marking algorithm. Then there is an
instance σ where LRU a marked page x in phase i evicted. Let σt the
request in phase i where x is evicted. Since x is marked there has to
be a earlier request σt* for x in the same phase, so t* < t. After t* x is
the caches newest page, so to got evicted at t the sequence
σt*+1,...,σt has to request at least k from x different pages. That
implies the phase i has requested at least k+1 different pages which
is a contradictory to the phase definition. So LRU has to be a
marking algorithm.
Proof: Let σ be an instance for the paging problem and l the number
of phases for σ. Is l = 1 then is every marking algorithm optimal and
the optimal offline algorithm cannot be better.
We assume l ≥ 2. the cost of every marking algorithm for instance σ is
bounded from above with l ⋅ k because in every phase a marking
algorithm cannot evict more than k pages without evicting one
marked page. Now we try to show that the optimal offline algorithm
evicts at least k+l-2 pages for σ, k in the first phase and at least one
for every following phase except for the last one. For proof lets define
l-2 disjunct subsequences of σ. Subsequence i ∈ {1,...,l-2} starts at
the second position of phase i+1 and end with the first position of
phase i+2. Let x be the first page of phase i+1. At the beginning of
subsequence i there is page x and at most k-1 different pages in the
optimal offline algorithms cache. In subsequence i are k page request
different from x, so the optimal offline algorithm has to evict at least
one page for every subsequence. Since at phase 1 beginning the
cache is still empty, the optimal offline algorithm causes k evictions
during the first phase. That shows that
First page 1 is requested l times than page 2 and so one. At the end
there are (l-1) alternating requests for page k and k+1.
LFU and LIFO fill their cache with pages 1-k. When page k+1 is
requested page k is evicted and vice versa. That means every
request of subsequence (k,k+1)l-1 evicts one page. In addition their
are k-1 cache misses for the first time use of pages 1-(k-1). So LFU
and LIFO evict exact k-1+2(l-1) pages.
Now we must show that for every constant τ∈ℜ and every constan r ≤
1 there exists an l so that
which is equal to
Paging
The paging problem arises from the limitation of finite space. Let's
assume our cache C has k pages. Now we want to process a
sequence of m page requests which must have been placed in the
cache before they are processed. Of course if m<= k then we just put
all elements in the cache and it will work, but usually is m>> k.
We say a request is a cache hit , when the page is already in
cache, otherwise, its called a cache miss . In that case, we must
bring the requested page into the cache and evict another, assuming
the cache is full. The Goal is an eviction schedule that minimizes
the number of evictions .
There are numerous strategies for this problem, let's look at some:
1. First in, first out (FIFO) : The oldest page gets evicted
2. Last in, first out (LIFO) : The newest page gets evicted
3. Least recently used (LRU) : Evict page whose most recent
access was earliest
4. Least frequently used (LFU) : Evict page that was least
frequently requested
5. Longest forward distance (LFD) : Evict page in the cache
that is not requested until farthest in the future.
6. Flush when full (FWF) : clear the cache complete as soon as a
cache miss happened
// after first empty page all others have to be empty else if ( cache[ i]
== emptyPage)
return i;
}
// no free pages
return 0 ; }
void update( int cachePos, int requestIndex, bool cacheMiss)
override {
// no pages free -> miss -> clear cache if ( cacheMiss && cachePos
== 0 )
{
Marking Algorithms
Instead of analysing every algorithm separately, let's look at a special
online algorithm family for the paging problem called marking
algorithms .
Let σ=(σ1,...,σp) an instance for our problem and k our cache size,
than σ can be divided into phases:
Proof: Let σ be an instance for the paging problem and l the number
of phases for σ. Is l = 1 then is every marking algorithm optimal and
the optimal offline algorithm cannot be better.
We assume l ≥ 2. the cost of every marking algorithm, for instance, σ
is bounded from above with l ⋅ k because in every phase a marking
algorithm cannot evict more than k pages without evicting one
marked page. Now we try to show that the optimal offline algorithm
evicts at least k+l-2 pages for σ, k in the first phase and at least one
for every following phase except for the last one. For proof lets define
l-2 disjunct subsequences of σ. Subsequence i ∈ {1,...,l-2} starts at
the second position of phase i+1 and end with the first position of
phase i+2. Let x be the first page of phase i+1. At the beginning of
subsequence i there is page x and at most k-1 different pages in the
optimal offline algorithms cache. In subsequence i are k page request
different from x, so the optimal offline algorithm has to evict at least
one page for every subsequence. Since at phase 1 beginning the
cache is still empty, the optimal offline algorithm causes k evictions
during the first phase. That shows that
wA(σ) ≤ l⋅k ≤ (k+l-2)k ≤ OPT(σ) ⋅ k
Corollary 1.5: LRU and FWF are strictly k-competitive .
Excercise: Show that FIFO is no marking algorithm, but strictly
k-competitive .
Is there no constant r for which an online algorithm A is r-competitive,
we call A not competitive
Proposition 1.6: LFU and LIFO are not competitive .
Proof: Let l ≥ 2 a constant, k ≥ 2 the cache size. The different cache
pages are nubered 1,...,k+1. We look at the following sequence:
The first page 1 is requested l times than page 2 and so one. At the
end, there are (l-1) alternating requests for page k and k+1.
LFU and LIFO fill their cache with pages 1-k. When page k+1 is
requested page k is evicted and vice versa. That means every
request of subsequence (k,k+1)l-1 evicts one page. In addition, their
are k-1 cache misses for the first time use of pages 1-(k-1). So LFU
and LIFO evict exact k-1+2(l-1) pages.
Now we must show that for every constant τ∈ℜ and every constant r
≤ 1 there exists an l so that
which is equal to
Description
A sorting algorithm is stable if it preserves the relative order of equal
elements after sorting.
A sorting algorithm is in-place if it sorts using only O( 1 ) auxiliary
memory (not counting the array that needs to be sorted).
A sorting algorithm has a best case time complexity of O( T( n)) if its
running time is at least T( n) for all possible inputs.
A sorting algorithm has an average case time complexity of O( T( n))
if its running time, averaged over all possible inputs , is T( n) .
Merge sort
Insertion sort
Radix sort
Tim sort
Bubble Sort
{ 6 ,5 ,3 ,1 ,8 ,7 ,2 ,4 }
{** 5 ,6 ** ,3 ,1 ,8 ,7 ,2 ,4 } - 5 < 6 -> swap { 5 ,** 3 ,6 ** ,1 ,8 ,7 ,2 ,4 }
- 3 < 6 -> swap { 5 ,3 ,** 1 ,6 ** ,8 ,7 ,2 ,4 } - 1 < 6 -> swap { 5 ,3 ,1 ,**
6 ,8 ** ,7 ,2 ,4 } - 8 > 6 -> no swap { 5 ,3 ,1 ,6 ,** 7 ,8 ** ,2 ,4 } - 7 < 8 -
> swap { 5 ,3 ,1 ,6 ,7 ,** 2 ,8 ** ,4 } - 2 < 8 -> swap { 5 ,3 ,1 ,6 ,7 ,2 ,**
4 ,8 **} - 4 < 8 -> swap
Graphic:
}
}
}
}
Bubble Sort with pointer
void pointer_bubble_sort( long * list, long n) {
long c, d, t;
t = * ( list + d );
* ( list + d ) = * ( list + d + 1 ); * ( list + d + 1 ) = t;
}
}
}
}
swapNumbers ( i, k, array);
}
}
printNumbers( array);
}
}
private static void swapNumbers( int i, int j, int [] array) {
int temp;
temp = array[ i]; array[ i] = array[ j]; array[ j] = temp;
}
private static void printNumbers( int [] input) {
for ( int i = 0 ; i < input.length ; i++) {
}}
swapped = false ;
for ( var i= 0 ; i < a.length - 1 ; i++) { if ( a[ i] > a[ i+ 1 ]) {
}
}
} while ( swapped);
}
Auxiliary Space : O( n)
Algorithmic Paradigm : Divide and Conquer
Sorting In Place : Not in a typical implementation Stable : Yes
}
m := ( len( a)) / 2
f := mergeSort( a[: m])
s := mergeSort( a[ m:])
return merge( f, s)
}
} else if f[ i] < s[ j] {
a[ z] = f[ i]
i++
} else {
a[ z] = s[ j]
j++
}
}
return a }
func main () {
a := [] int { 75 , 12 , 34 , 45 , 0 , 123 , 32 , 56 , 32 , 99 , 123 , 11 , 86 ,
33 } fmt.Println ( a)
fmt.Println ( mergeSort( a))
arr1[ i]= 9999 ; // To mark the end of each temporary array arr2[ j]=
9999 ;
i=0;
j= 0 ;
for ( k= l; k<= h; k++) { //process of combining two sorted arrays
return 0 ; }
return 0 ; }
C# Merge Sort
public class MergeSort {
static void Merge( int [] input, int l, int m, int r) {
int i, j;
var n1 = m - l + 1 ; var n2 = r - m;
var left = new int [ n1]; var right = new int [ n2];
for ( i = 0 ; i < n1; i++)
{
left[ i] = input[ l + i];
}
{
right[ j] = input[ m + j + 1 ];
}
i=0;
j=0;
var k = l;
@Override
public void sort( T[] elements) {
T[] arr = ( T[]) new Comparable [ elements.length ];
sort( elements, arr, 0 , elements.length 1 ); }
// We check both our sides and then merge them
private void sort( T[] elements, T[] arr, int low, int high) { if ( low
>= high) return ;
int mid = low + ( high - low) / 2 ;
sort( elements, arr, low, mid);
sort( elements, arr, mid + 1 , high);
merge( elements, arr, low, high, mid);
}
private void merge( T[] a, T[] b, int low, int high, int mid) { int i =
low;
int j = mid + 1 ;
// We select the smallest element of the two. And then we put it into b
for ( int k = low; k <= high; k++) {
if ( i <= mid && j <= high) { if ( a[ i] .compareTo ( a[ j]) >= 0 ) { b[ k] =
a[ j++];
} else {
b[ k] = a[ i++];
}
} else if ( j > high && i <= mid) {
b[ k] = a[ i++];
} else if ( i > mid && j <= high) {
b[ k] = a[ j++];
}
}
else :
out.append ( Y[ p2])
p2 += 1
return sorted( A)
mid = len( A) / 2
return merge( mergeSort( A[: mid]) , mergeSort( A[ mid:]))
if __name__ == "__main__" :
# Generate 20 random numbers and sort them A = [ randint( 1 , 100 )
for i in xrange( 20 )] print mergeSort( A)
public MergeSortBU() {
}
private static void merge( Comparable [] arrayToSort,
Comparable [] aux, int lo,int mid, int hi) {
int i = lo;
int j = mid + 1 ;
for ( int k = lo; k <= hi; k++) {
if ( i > mid)
arrayToSort[ k] = aux[ j++];
else if ( j > hi)
arrayToSort[ k] = aux[ i++];
else if ( isLess( aux[ i] , aux[ j])) {
arrayToSort[ k] = aux[ i++];
} else {
arrayToSort[ k] = aux[ j++];
}
}}
}
}
}
public static boolean isLess( Comparable a, Comparable b) {
return a.compareTo ( b) <= 0 ;
}
private static void print( Comparable [] array)
{ http: //stackoverflow.com/documentation/algorithm/5732/merge-
sort#
}
}
| otherwise = x: insert n xs
{
bucket[ i] = new List< int>();
}
foreach ( int i in input)
{
bucket[ i - minValue] .Add ( i);
}
This scheme chooses a pivot which is typically the last element in the
array. The algorithm maintains the index to put the pivot in variable i
and each time it finds an element less than or equal to pivot, this
index is incremented and that element would be placed before the
pivot.
if A[ j] ≤ pivot then
swap A[ i] with A[ j]
i := i + 1
It uses two indices that start at the ends of the array being partitioned,
then move toward each other, until they detect an inversion: a pair of
elements, one greater or equal than the pivot, one lesser or equal,
that are in the wrong order relative to each other. The inverted
elements are then swapped. When the indices meet, the algorithm
stops and returns the final index. Hoare's scheme is more efficient
than Lomuto's partition scheme because it does three times fewer
swaps on average, and it creates efficient partitions even when all
values are equal.
Partition :
do :
i := i + 1 while A[ i] < pivot do
do : j := j - 1
while A[ j] > pivot do
public static void quickSort( int [] ar, int low, int high) {
if ( low< high) {
}
public static int partition( int [] ar, int l, int r)
{
int pivot = ar[ r]; int i = l;
for ( int j= l; j< r; j++) {
if ( ar[ j] <= pivot) {
int t = ar[ j]; ar[ j] = ar[ i]; ar[ i] = t;
i++;
}
}
int t = ar[ i]; ar[ i] = ar[ r]; ar[ r] = t;
return i; }
1. Construct a working array C that has size equal to the range of the
input array A .
2. Iterate through A , assigning C [x] based on the number of times x
appeared in A .
3. Transform C into an array where C [x] refers to the number of
values ≤ x by iterating through the array, assigning to each C [x] the
sum of its prior value and all values in C that come before it.
4. Iterate backwards through A , placing each value in to a new
sorted array B at the index recorded in C . This is done for a given A
[x] by assigning B [C [A [x]]] to A [x], and decrementing C [A [x]] in
case there were duplicate values in the original unsorted array.
Example of Counting Sort
Auxiliary Space: O( n+ k)
Time Complexity: Worst-case: O( n+ k) , Best-case: O( n) ,
Average-case O( n+ k)
Pseudocode:
for x in input:
count[ key( x)] += 1
total = 0
for i in range( k):
oldCount = count[ i]
count[ i] = total
total += oldCount
for x in input:
output[ count[ key( x)]] = x count[ key( x)] += 1
return output
if ( largest != i) {
var temp = input[ i];
input[ i] = input[ largest]; input[ largest] = temp; Heapify( input, n,
largest);
}
}
continue
while item == array[ pos]:
pos += 1
array[ pos] , item = item, array[ pos]
writes += 1
while pos != cycleStart:
pos = cycleStart
for i from cycleStart + 1 to length( array) 1 if array[ i] < item:
pos += 1
while item == array[ pos]:
pos += 1
array[ pos] , item = item, array[ pos]
writes += 1
return outout
if n> 2 then
1 . apply odd- even merge( n/ 2 ) recursively to the even
subsequence a0, a2, ..., an- 2 and to the
odd subsequence a1, a3, , ..., an- 1
2 . comparison [ i : i+ 1 ] for all i element { 1 , 3 , 5 , 7 , ..., n- 3 }
else
comparison [ 0 : 1 ]
Example of Odd-Even
Sort:
Implementation:
I used C# language to implement Odd-Even Sort Algorithm.
end
else
e2
end
end
end
The algorithm divides the input list into two parts: the sublist of items
already sorted, which is built up from left to right at the front (left) of
the list, and the sublist of items remaining to be sorted that occupy
the rest of the list. Initially, the sorted sublist is empty and the
unsorted sublist is the entire input list. The algorithm proceeds by
finding the smallest (or largest, depending on sorting order) element
in the unsorted sublist, exchanging (swapping) it with the leftmost
unsorted element (putting it in sorted order), and moving the sublist
boundaries one element to the right.
Your boss wants the equilibrium price ASAP, but tells you that the
equilibrium price can be a positive integer that is at most 10 ^ 17 and
there is guaranteed to be exactly 1 positive integer solution in the
range. So get going with your job before you lose it!
low = mid < Solution is in upper half of search space else < supply==
demand condition
return mid < Found solution
if ( x == a[ mid]) {
return ( mid);
} else
if ( x < a[ mid]) {
binsearch( a, x, low, mid - 1 );
} else {
binsearch( a, x, mid + 1 , high);
}
}
Section 39.2: Rabin Karp
The Rabin –Karp algorithm or Karp–Rabin algorithm is a string
searching algorithm that uses hashing to find any one of a set of
pattern strings in a text.Its average and best case running time is
O(n+m) in space O(p), but its worst-case time is O(nm) where n is the
length of the text and m is the length of the pattern.
// otherwise return -1
int search(int arr[], int n, int x) {
int i;
for (i= 0; i< n; i++) {
if (arr[i] == x) return i;
}
return 1; }
/* Driver program to test above functions*/
int main()
{ int arr[] = { 1 , 10 , 30 , 15 };
int x = 30 ;
int n = sizeof( arr)/ sizeof( arr[ 0 ]);
printf( "%d is present at index %d" , x, search( arr, n, x));
getchar(); return 0 ; }
Worst Case Analysis (Usually Done)
For some algorithms, all the cases are asymptotically same, i.e.,
there are no worst and best cases. For example, Merge Sort. Merge
Sort does Θ(nLogn) operations in all cases. Most of the other sorting
algorithms have worst and best cases. For example, in the typical
implementation of Quick Sort (where pivot is chosen as a corner
element), the worst occurs when the input array is already sorted and
the best occur when the pivot elements always divide array in two
halves. For insertion sort, the worst case occurs when the array is
reverse sorted and the best case occurs when the array is sorted in
the same order as output.
low = 0 ;
high = N - 1 ;
while ( low < high)
{
else
high = mid;
}
if ( array[ low] == x)
Interestingly, the above version of binary search allows you to find the
smallest occurrence of x in the array. If the array contains duplicates
of x, the algorithm can be modified slightly in order for it to return the
largest occurrence of x by simply adding to the if conditional:
low = mid + 1 ;
else
high = mid; }
+-------+---+---+---+---+---+---+---+---+ | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
| +-------+---+---+---+---+---+---+---+---+ | Text | a | b | c | b | c | g | l | x |
+-------+---+---+---+---+---+---+---+---+
This pattern does exist in the text . So our substring search should
return 3 , the index of the position from which this pattern starts. So
how does our brute force substring search procedure work?
What we usually do is: we start from the 0th index of the text and the
0th index of our *pattern and we compare Text[0] with Pattern[0]
. Since they are not a match, we go to the next index of our text and
we compare Text[1] with Pattern[0] . Since this is a match, we
increment the index of our pattern and the index of the Text also. We
compare Text[2] with Pattern[1] . They are also a match.
Following the same procedure stated before, we now compare
Text[3] with Pattern[2] . As they do not match, we start from the
next position where we started finding the match. That is index 2 of
the Text . We compare Text[2] with Pattern[0] . They don't match.
Then incrementing index of the Text , we compare Text[3] with
Pattern[0] . They match. Again Text[4] and Pattern[1] match,
Text[5] and Pattern[2] match and Text[6] and Pattern[3]
match. Since we've reached the end of our Pattern , we now return
the index from which our match started, that is 3 . If our pattern was:
bcgll, that means if the pattern didn't exist in our text , our search
should return exception or -1 or any other predefined value. We can
clearly see that, in the worst case, this algorithm would take O( mn)
time where m is the length of the Text and n is the length of the
Pattern . How do we reduce this time complexity? This is where KMP
Substring Search Algorithm comes into the picture.
Let's extend our example Text and Pattern for better understanding:
+-------+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--
+--+ | Index |0 |1 |2 |3 |4 |5 |6 |7 |8 |9
|10|11|12|13|14|15|16|17|18|19|20|21|22| +-------+--+--+--+--+--+--+--+-
-+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | Text |a |b |c |x |a |b |c
|d |a |b |x |a |b |c |d |a |b |c |d |a |b |c |y | +-------+--+--+--+--+--+--+--+--
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+---------+---+---+---+---+---+---+---+---+ | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | +---------+---+---+---+---+---+---+---+---+ | Pattern | a | b | c | d | a | b
| c | y | +---------+---+---+---+---+---+---+---+---+
At first, our Text and Pattern matches till index 2 . Text[3] and
Pattern[3] doesn't match. So our aim is to not go backwards in this
Text , that is, in case of a mismatch, we don't want our matching to
begin again from the position that we started matching with. To
achieve that, we'll look for a suffix in our Pattern right before our
mismatch occurred (substring abc ), which is also a prefix of the
substring of our Pattern . For our example, since all the characters
are unique, there is no suffix, that is the prefix of our matched
substring. So what that means is, our next comparison will start from
index 0 . Hold on for a bit, you'll understand why we did this. Next, we
compare Text[3] with Pattern[0] and it doesn't match. After that,
for Text from index 4 to index 9 and for Pattern from index 0 to index
5 , we find a match. We find a mismatch in Text[10] and
Pattern[6] . So we take the substring from Pattern right before the
point where mismatch occurs (substring abcdabc ), we check for a
suffix, that is also a prefix of this substring. We can see here ab is
both the suffix and prefix of this substring. What that means is, since
we've matched until Text[10] , the characters right before the
mismatch is ab . What we can infer from it is that since ab is also a
prefix of the substring we took, we don't have to check ab again and
the next check can start from Text[10] and Pattern[2] . We didn't
have to look back to the whole Text , we can start directly from where
our mismatch occurred. Now we check Text[10] and Pattern[2] ,
since it's a mismatch, and the substring before mismatch (abc )
doesn't contain a suffix which is also a prefix, we check Text[10]
and Pattern[0] , they don't match. After that for Text from index 11
to index 17 and for Pattern from index 0 to index 6 . We find a
mismatch in Text[18] and Pattern[7] . So again we check the
substring before mismatch (substring abcdabc ) and find abc is
both the suffix and the prefix. So since we matched till Pattern[7] ,
abc must be before Text[18] . That means, we don't need to
compare until Text[17] and our comparison will start from
Text[18] and Pattern[3] . Thus we will find a match and we'll
return 15 which is our starting index of the match. This is how our
KMP Substring Search works using suffix and prefix information.
Now, how do we efficiently compute if suffix is same as prefix and at
what point to start the check if there is a mismatch of character
between Text and Pattern . Let's take a look at an example:
+---------+---+---+---+---+---+---+---+---+ | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | +---------+---+---+---+---+---+---+---+---+ | Pattern | a | b | c | d | a | b
| c | a | +---------+---+---+---+---+---+---+---+---+
ji
+---------+---+---+---+---+---+---+---+---+ | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | +---------+---+---+---+---+---+---+---+---+ | Pattern | a | b | c | d | a | b
| c | a | +---------+---+---+---+---+---+---+---+---+
ji
+---------+---+---+---+---+---+---+---+---+ | Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | +---------+---+---+---+---+---+---+---+---+ | Pattern | a | b | c | d | a | b
| c | a | +---------+---+---+---+---+---+---+---+---+ | S | 0 | 0 | 0 | 0 | 1 | | | |
+---------+---+---+---+---+---+---+---+---+
Since Pattern[1] and Pattern[5] is a match, we put S[i] = S[5] =
j + 1 = 1 + 1 = 2 . If we continue, we'll find a mismatch for j = 3 and i
= 7 . Since j is not equal to 0 , we put j = S[j-1] . And we'll compare
the characters at i and j are same or not, since they are same, we'll
put S[i] = j + 1. Our completed array will look like:
+---------+---+---+---+---+---+---+---+---+ | S | 0 | 0 | 0 | 0 | 1 | 2 | 3 | 1 |
+---------+---+---+---+---+---+---+---+---+
The time complexity of this algorithm apart from the Suffix Array
Calculation is O( m) . Since GenerateSuffixArray takes O( n) , the
total time complexity of KMP Algorithm is: O( m+ n) .
Let's say we have a text: yeminsajid and we want to find out if the
pattern nsa exists in the text. To calculate the hash and rolling hash,
we'll need to use a prime number. This can be any prime number.
Let's take prime = 11 for this example. We'll determine hash value
using this formula:
a -> 1 g -> 7 m -> 13 s -> 19 y -> 25 b -> 2 h -> 8 n -> 14 t -> 20 z ->
26 c -> 3 i -> 9 o -> 15 u -> 21
d -> 4 j -> 10 p -> 16 v -> 22
e -> 5 k -> 11 q -> 17 w -> 23
f -> 6 l -> 12 r -> 18 x -> 24
Now we find the rolling-hash of our text. If the rolling hash matches
with the hash value of our pattern, we'll check if the strings match or
not. Since our pattern has 3 letters, we'll take 1st 3 letters yem from
our text and calculate hash value. We get:
25 X 11 ⁰ + 5 X 11 ¹ + 13 X 11 ² = 1653
This value doesn't match with our pattern's hash value. So the string
doesn't exists here. Now we need to consider the next step. To
calculate the hash value of our next string emi . We can calculate
this using our formula. But that would be rather trivial and cost us
more. Instead, we use another technique.
We subtract the value of the First Letter of Previous String
from our current hash value. In this case, y . We get, 1653 25 = 1628
.
We divide the difference with our prime , which is 11 for this
example. We get, 1628 / 11 = 148 . We add new letter X (prime)
⁻¹, where m is the length of the pattern, with the quotient, which is i =
9 . We get, 148 + 9 X 11 ² = 1237 .
The new hash value is not equal to our patterns hash value. Moving
on, for n we get:
Hash Recalculation:
String Match:
Procedure String- Match( Text, Pattern, m): for i from m to Pattern-
length + m - 1 if Text[ i] is not equal to Pattern[ i]
Return false
end if
end for
Return true
Rabin-Karp:
Procedure Rabin - Karp( Text, Pattern, Prime):
m := Pattern.Length
HashValue := Calculate- Hash( Pattern, Prime, m)
CurrValue := Calculate- Hash( Pattern, Prime, m)
for i from 1 to Text.length m
Return i
end if
CurrValue := Recalculate- Hash( String , i+ 1 , Prime, CurrValue)
end for
Return 1
If the algorithm doesn't find any match, it simply returns -1 .
j += 1
delimeter += 1
return prefix_table
def strstr ( haystack, needle):
# m: denoting the position within S where the prospective match for
W begins # i: denoting the index of the currently considered character
in W. haystack_len = len( haystack)
needle_len = len( needle)
if ( needle_len > haystack_len) or ( not haystack_len) or ( not
needle_len):
return 1
prefix_table = get_prefix_table( needle)
m=i=0
while (( i< needle_len) and ( m< haystack_len)):
if __name__ == '__main__' :
needle = 'abcaby'
haystack = 'abxabcabcaby' print strstr( haystack, needle)
C Language Implementation:
// create lps[] that will hold the longest prefix suffix // values for pattern
int * lps = ( int *) malloc( sizeof( int )* M); int j = 0 ; // index for pat[]
if ( j == M) {
printf( "Found pattern at index %d \n " , i- j); j = lps[ j- 1 ];
}
j = lps[ j- 1 ];
else
i = i+ 1 ;
}
}
free( lps); // to avoid memory leak
}
}
else // if (len == 0) {
lps[ i] = 0 ; i++;
}
}
}
}
}
Output:
Found pattern at index 10
Reference: http://www.geeksforgeeks.org/searching-for-patterns-
set-2-kmp-algorithm/
Chapter 41: Breadth-First
Search
Section 41.1: Finding the Shortest
Path from Source to other Nodes
Breadth-first-search (BFS) is an algorithm for traversing or searching
tree or graph data structures. It starts at the tree root (or some
arbitrary node of a graph, sometimes referred to as a 'search key')
and explores the neighbor nodes first, before moving to the next level
neighbors. BFS was invented in the late 1950s by Edward Forrest
Moore , who used it to find the shortest path out of a maze and
discovered independently by C. Y. Lee as a wire routing algorithm in
1961.
We didn't yet reach our target node, that is node 10 . So let's visit
the next nodes. we can directly go to from node 6 , node 7 and
node 8 .
So our task will be, to go from source to level 1 nodes. Then from
level 1 to level 2 nodes and so on until we reach our destination.
We can use queue to store the nodes that we are going to process.
That is, for each node we're going to work with, we'll push all other
nodes that can be directly traversed and not yet traversed in the
queue.
front
+-----+ +-----+ +-----+
|2||3||4|
+-----+ +-----+ +-----+
Now we pop node 4 and work with it. We can go to node 7 from
node 4 . level[7] = level[4] + 1 = 2 . We mark node 7 as visited
and push it in the queue.
This process will continue till we reach our destination or the queue
becomes empty. The level array will provide us with the distance of
the shortest path from source . We can initialize level array with
infinity value, which will mark that the nodes are not yet visited. Our
pseudo-code will be:
Procedure BFS ( Graph, source):
Q = queue();
level[] = infinity
level[ source] := 0
Q.push ( source)
while Q is not empty
u -> Q.pop ()
for all edges from u to v in Adjacency list if level[ v] == infinity
level[ v] := level[ u] + 1
Q.push ( v)
end if
end for
end while
Return level
By iterating through the level array, we can find out the distance of
each node from source. For example: the distance of node 10 from
source will be stored in level[10] .
Sometimes we might need to print not only the shortest distance, but
also the path via which we can go to our destined node from the
source . For this we need to keep a parent array.
parent[source] will be NULL. For each update in level array, we'll
simply add parent[ v] := u in our pseudo code inside the for loop. After
finishing BFS, to find the path, we'll traverse back the parent array
until we reach source which will be denoted by NULL value. The
pseudo-code will be:
Complexity:
We've visited every node once and every edges once. So the
complexity will be O(V + E) where V is the number of nodes and E
is the number of edges.
There will be one additional thing called direction array. This will
simply store the all possible combinations of directions we can go to.
Let's say, for horizontal and vertical moves, our direction arrays will
be:
+----+-----+-----+-----+-----+ | dx | 1 | -1 | 0 | 0 | +----+-----+-----+-----+----
-+ | dy | 0 | 0 | 1 | -1 | +----+-----+-----+-----+-----+
If any of the cell is blocked, for every possible moves, we'll check if
the cell is blocked or not. We'll also check if we have gone out of
bounds, that is we've crossed the array boundaries. The number of
rows and columns will be given.
top := Q.pop
for i from 1 to m
temp.x := top.x + dx[ i]
temp.y := top.y + dy[ i]
if temp is inside the row and column and top doesn't equal to
blocksign visited[temp.x][temp.y] := true
level[temp.x][temp.y] := level[top.x][top.y] + 1
Q.push(temp)
end if end for end while
Return level
As we have discussed earlier, BFS only works for unweighted graphs.
For weighted graphs, we'll need Dijkstra's algorithm. For negative
edge cycles, we need Bellman-Ford's algorithm. Again this algorithm
is single source shortest path algorithm. If we need to find out
distance from each nodes to all other nodes, we'll need
FloydWarshall's algorithm.
#include<stdio.h>
#include<stdlib.h>
#define MAXVERTICES 100
void enqueue(int );
int deque();
int isConnected(char **graph,int noOfVertices); void BFS(char
**graph,int vertex,int noOfVertices); int count = 0;
//Queue node depicts a single Queue element //It is NOT a graph
node.
struct node
{
int v;
struct node *next; };
typedef struct node Node; typedef struct node *Nodeptr;
graph = malloc (n * sizeof (char *)); visited = malloc (n*sizeof (char ));
printf ("enter number of edges and then enter them in pairs:" ); scanf
("%d" ,&e);
if (isConnected(graph,n))
printf ("The graph is connected" );
else printf ("The graph is NOT connected \n " ); }
int deque() {
if (Qfront == NULL ) {
printf ("Q is empty , returning -1 \n " ); return 1;
}
else
{
int v = Qfront-> v; Nodeptr temp= Qfront; if (Qfront == Qrear) {
Qfront = Qfront-> next; Qrear = NULL ;
}
else
Qfront = Qfront-> next;
AND
printf( "%d " ,vertex+ 1 );
add this as first line of while loop in BFS
and we define the following function:
}}
We'll traverse
the graph following these rules:
The edge that we take to go from gray node to white node are called
tree edge . If we only keep the tree edge 's and remove others,
we'll get DFS tree .
In undirected graph, if we can visit a already visited node, that must
be a backedge . But for directed graphs, we must check the colors.
If and only if we can go from one gray node to another gray node,
that is called a backedge .
In DFS, we can also keep timestamps for each node, which can be
used in many ways (e.g.: Topological Sort).
1. When a node v is changed from white to gray the time is recorded
in d[v] .
2. When a node v is changed from gray to black the time is recorded
in f[v] .
Here d[] means discovery time and f[] means finishing time . Our
pesudo-code will look like:
end if
end for
color[ u] := black
time := time + 1
f[ u] := time
Complexity:
Each nodes and edges are visited once. So the complexity of DFS is
O(V+E) , where V denotes the number of nodes and E denotes the
number of edges.
Applications of Depth First Search:
SByte
(( int ) m_value ^ ( int ) m_value << 8 );
Char
( int ) m_value ^ (( int ) m_value << 16 );
Int16
(( int )(( ushort) m_value) ^ ((( int ) m_value) << 16 ));
Int64 , Double
break ;
hash2 = (( hash2 << 5 ) + hash2) ^ c;
s += 2 ;
}
return hash1 + ( hash2 * 1566083941 ); ValueType
The first non-static field is look for and get it's hashcode. If the type
has no non-static fields, the hashcode of the type returns. The
hashcode of a static member can't be taken because if that member
is of the same type as the original type, the calculating ends up in an
infinite loop.
Nullable<T>
return hasValue ? value.GetHashCode () : 0 ;
Array
int ret = 0 ;
for ( int i = ( Length >= 8 ? Length - 8 : 0 ); i < Length; i++) {
In general case size of hash function less then size of input data: | y|
< | x| . Hash functions are not reversible or in other words it may be
collision: ∃ x1, x2 ∈ X, x1 ≠ x2: h( x1) = h( x2) . X may be finite or
infinite set and Y is finite set.
Hash functions are used in a lot of parts of computer science, for
example in software engineering, cryptography, databases, networks,
machine learning and so on. There are many different types of hash
functions, with differing domain specific properties.
Hash methods
There are several approaches for determinig hash function. Without
loss of generality, lets x ∈ X = { z ∈ ℤ: z ≥ 0 } are positive integer
numbers. Often m is prime (not too close to an exact power of 2).
Hash table
Hash functions used in hash tables for computing index into an array
of slots. Hash table is data structure for implementing dictionaries
(key-value structure). Good implemented hash tables have O(1) time
for the next operations: insert, search and delete data by key. More
than one keys may hash to the same slot. There are two ways for
resolving collision:
1. Chaining: linked list is used for storing elements with the same
hash value in slot
2. Open addressing: zero or one element is stored in each slot
The next methods are used to compute the probe sequences
required for open addressing
Method Formula
Linear probing h( x, i) = ( h'(x) + i) mod m
Quadratic probingh( x, i) = ( h'(x) + c1*i + c2*i^2) mod m Double
hashing h( x, i) = ( h1( x) + i* h2( x)) mod m
103 3 2
738 38 31
292 92 90
61 61 61
87 87 87
995 95 86
549 49 44
991 91 82
75757 50
920 20 11
626 26 20
55757 52
831 31 23
619 19 13
Links
Psuedocode
minimum = INF
for all permutations P
current = 0
for i from 0 to N- 2
current = current + cost[ P[ i]][ P[ i+ 1 ]] < Add the cost of going from 1
vertex to the next
current = current + cost[ P[ N- 1 ]][ P[ 0 ]] < Add the cost of going from
last vertex to the first
if current < minimum < Update minimum if necessary
minimum = current
output minimum
Time Complexity
There are N! permutations to go through and the cost of each path is
calculated in O( N) , thus this algorithm takes O( N * N!) time to
output the exact answer.
12 = 1 1 0 0
^^
vertices: 3 2 1 0
cost = min( cost,TSP( bitmask | ( 1 << i) , i) + cost[ pos][ i]); //Visit the
vertex }
}
memo[ bitmask][ pos] = cost; //Save the result
return cost;
}
//Call TSP(1,0)
//Adjust the value of N if needed //Set everything here to -1
Thus, this line is to update the value of cost to the minimum possible
value of travelling to every other vertex that has not been visited yet.
Time Complexity
The function TSP( bitmask,pos) has 2 ^ N values for bitmask and N
values for pos. Each function takes O( N) time to run (the for loop).
Thus this implementation takes O( N^ 2 * 2 ^ N) time to output the
exact answer.
1. Values(array v)
2. Weights(array w)
3. Number of distinct items(n)
4. Capacity(W)
for j from 0 to W do :
m[ 0 , j] := 0
for i from 1 to n do :
for j from 0 to W do :
if w[ i] > j then:
m[ i, j] := m[ i- 1 , j]
else :
m[ i, j] := max( m[ i- 1 , j] , m[ i- 1 , j- w[ i]] + v[ i])
for w in range( W+ 1 ):
if i== 0 or w== 0 :
K [ i][ w] = 0
elif wt[ i- 1 ] <= w:
K[ i][ w] = max( val[ i- 1 ] + K[ i- 1 ][ w- wt[ i- 1 ]] , K[ i- 1 ][ w]) else :
K[ i][ w] = K[ i- 1 ][ w]
return K[ n][ W]
val = [ 60 , 100 , 120 ]
wt = [ 10 , 20 , 30 ]
W = 50
n = len( val)
print( knapSack( W, wt, val, n))
private static int Knapsack( int w, int [] weight, int [] value, int
n) {
int i;
int [ ,] k = new int [ n + 1 , w + 1 ]; for ( i = 0 ; i <= n; i++)
{
int b;
for ( b = 0 ; b <= w; b++) {
if ( i== 0 || b== 0 ) {
k[ i, b] = 0 ;
}
else if ( weight[ i - 1 ] <= b) {
k[ i, b] = Math .Max ( value[ i - 1 ] + k[ i - 1 , b - weight[ i - 1 ]] , k[ i - 1 ,
b]); }
else
{
k[ i, b] = k[ i - 1 , b]; }
}
}
return k[ n, w];
}
public static int Main( int nItems, int [] weights, int [] values) {
int n = values.Length ;
return Knapsack( nItems, weights, values, n);
}
Chapter 46: Equation Solving
Section 46.1: Linear Equation
There are two classes of methods for solving Linear Equations:
1. Direct Methods : Common characteristics of direct methods are
that they transform the original equation into equivalent equations
that can be solved more easily, means we get solve directly from an
equation.
Implementation in C
int i, j;
while (! rootFound){
for ( i= 0 ; i< n; i++){ //calculation Nx[ i]= b[ i];
return ; }
int i, j;
for ( i= 0 ; i< n; i++){ //initialization Nx[ i]= x[ i];
while (! rootFound){
for ( i= 0 ; i< n; i++){ //calculation Nx[ i]= b[ i];
rootFound= 1 ; //verification
for ( i= 0 ; i< n; i++){
if (!( ( Nx[ i] x[ i])/ x[ i] > 0.000001 && ( Nx[ i] x[ i])/ x[ i] < 0.000001 )){
rootFound= 0 ;
break ;
}
}
return ; }
//Print array with comma separation void print( int n, double x[ n]){
int i;
for ( i= 0 ; i< n; i++){
printf( "%lf, " , x[ i]);
}
printf( " \n\n " );
return ; }
int main(){
//equation initialization
int n= 3 ; //number of variables
//assign values
a[ 0 ][ 0 ]= 8 ; a[ 0 ][ 1 ]= 2 ; a[ 0 ][ 2 ]= 2 ; b[ 0 ]= 8 ; //8x ₁ +2x ₂ -2x ₃
+8=0 a[ 1 ][ 0 ]= 1 ; a[ 1 ][ 1 ]= 8 ; a[ 1 ][ 2 ]= 3 ; b[ 1 ]= 4 ; //x ₁ -8x ₂
+3x ₃ -4=0 a[ 2 ][ 0 ]= 2 ; a[ 2 ][ 1 ]= 1 ; a[ 2 ][ 2 ]= 9 ; b[ 2 ]= 12 ; //2x ₁
+x ₂ +9x ₃ +12=0
int i;
for ( i= 0 ; i< n; i++){ //initialization x[ i]= 0 ;
}
JacobisMethod( n, x, b, a);
print( n, x);
x [ i]= 0 ;
}
GaussSeidalMethod( n, x, b, a); print( n, x);
return 0 ; }
Bracketing Method : We take two initial points where the root lies
in between them. ExampleBisection Method, False Position Method.
Open End Method : We take one or two initial values where the
root may be any-where. ExampleNewton-Raphson Method,
Successive Approximation Method, Secant Method.
Implementation in C:
/// Here define different functions to work with #define f(x) ( ((x)*(x)*
(x)) - (x) - 2 ) #define f2(x) ( (3*(x)*(x)) - 1 )
#define g(x) ( cbrt( (x) + 2 ) )
/**
* Takes two initial values and shortens the distance by both side. **/
double BisectionMethod(){
double root= 0;
double a= 1, b= 2; double c= 0;
int loopCounter= 0; if (f(a)*f(b) < 0){ while (1){
loopCounter++; c= (a+b)/2;
}
}
printf ("It took %d loops. \n " , loopCounter);
return root; }
/**
* Takes two initial values and shortens the distance by single side. **/
double FalsePosition(){
double root= 0;
double a= 1, b= 2; double c= 0;
int loopCounter= 0; if (f(a)*f(b) < 0){ while (1){
loopCounter++;
c= (a*f(b) b*f(a)) / (f(b) f(a));
/*/printf("%lf\t %lf \n", c, f(c));/**/ ///test if (f(c)< 0.00001 && f(c)>
-0.00001 ){
root= c;
break ;
}
return root; }
/**
* Uses one initial value and gradually takes that value near to the real
one. **/
double NewtonRaphson(){
double root= 0;
double x1= 1; double x2= 0;
int loopCounter= 0; while (1){
loopCounter++; x2 = x1 - (f(x1)/f2(x1));
/*/printf("%lf \t %lf \n", x2, f(x2));/**/ ///test
x1 = x2;
}
printf ("It took %d loops. \n " , loopCounter);
return root; }
/**
* Uses one initial value and gradually takes that value near to the real
one. **/
double FixedPoint(){
double root= 0;
double x= 1;
int loopCounter= 0; while (1){
loopCounter++;
}
/*/printf("%lf \t %lf \n", g(x), x-(g(x)));/**/ ///test
x = g(x);
}
printf ("It took %d loops. \n " , loopCounter);
return root; }
/**
* uses two initial values & both value approaches to the root. **/
double Secant(){
double root= 0;
}
x2 = ((x0*f(x1))-(x1*f(x0))) / (f(x1)-f(x0));
x0 = x1;
x1= x2;
}
printf ("It took %d loops. \n " , loopCounter);
return root;
int main(){
double root;
root = BisectionMethod();
printf ("Using Bisection Method the root is: %lf \n\n " , root);
root = FalsePosition();
printf ("Using False Position Method the root is: %lf \n\n " , root);
root = NewtonRaphson();
printf ("Using Newton-Raphson Method the root is: %lf \n\n " , root);
root = FixedPoint();
printf ("Using Fixed Point Method the root is: %lf \n\n " , root);
root = Secant();
printf ("Using Secant Method the root is: %lf \n\n " , root);
return 0; }
Brute-Force Method:
0 1 2 3 4 5 6 +-----+-----+-----+-----+-----+-----+-----+-----+ | chʳ | | a | b |
c | d | a | f | +-----+-----+-----+-----+-----+-----+-----+-----+ 0 | | | | | | | | | +-
----+-----+-----+-----+-----+-----+-----+-----+ 1 | a | | | | | | | | +-----+-----+----
-+-----+-----+-----+-----+-----+ 2 | c | | | | | | | | +-----+-----+-----+-----+-----
+-----+-----+-----+ 3 | b | | | | | | | | +-----+-----+-----+-----+-----+-----+-----+-
----+ 4 | c | | | | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ 5 | f | | |
| | | | | +-----+-----+-----+-----+-----+-----+-----+-----+
Here each row and column represent the length of the longest
common subsequence between two strings if we take the characters
of that row and column and add to the prefix before it. For example:
Table[2][3] represents the length of the longest common
subsequence between "ac" and "abc" .
0 1 2 3 4 5 6 +-----+-----+-----+-----+-----+-----+-----+-----+ | chʳ | | a | b |
c | d | a | f | +-----+-----+-----+-----+-----+-----+-----+-----+ 0 | | 0 | 0 | 0 | 0
| 0 | 0 | 0 | +-----+-----+-----+-----+-----+-----+-----+-----+ 1 | a | 0 | | | | | | |
+-----+-----+-----+-----+-----+-----+-----+-----+ 2 | c | 0 | | | | | | | +-----+-----
+-----+-----+-----+-----+-----+-----+ 3 | b | 0 | | | | | | | +-----+-----+-----+-----
+-----+-----+-----+-----+ 4 | c | 0 | | | | | | | +-----+-----+-----+-----+-----+-----
+-----+-----+ 5 | f | 0 | | | | | | | +-----+-----+-----+-----+-----+-----+-----+-----
+
0 1 2 3 4 5 6 +-----+-----+-----+-----+-----+-----+-----+-----+ | chʳ | | a | b |
c | d | a | f | +-----+-----+-----+-----+-----+-----+-----+-----+ 0 | | 0 | 0 | 0 | 0
| 0 | 0 | 0 | +-----+-----+-----+-----+-----+-----+-----+-----+ 1 | a | 0 | 1 | 1 |
1 | 1 | 1 | 1 | +-----+-----+-----+-----+-----+-----+-----+-----+ 2 | c | 0 | | | | | |
| +-----+-----+-----+-----+-----+-----+-----+-----+ 3 | b | 0 | | | | | | | +-----+---
--+-----+-----+-----+-----+-----+-----+ 4 | c | 0 | | | | | | | +-----+-----+-----+---
--+-----+-----+-----+-----+ 5 | f | 0 | | | | | | | +-----+-----+-----+-----+-----+----
-+-----+-----+
0 1 2 3 4 5 6 +-----+-----+-----+-----+-----+-----+-----+-----+ | chʳ | | a | b |
c | d | a | f | +-----+-----+-----+-----+-----+-----+-----+-----+ 0 | | 0 | 0 | 0 | 0
| 0 | 0 | 0 | +-----+-----+-----+-----+-----+-----+-----+-----+ 1 | a | 0 | 1 | 1 |
1 | 1 | 1 | 1 | +-----+-----+-----+-----+-----+-----+-----+-----+ 2 | c | 0 | 1 | | |
| | | +-----+-----+-----+-----+-----+-----+-----+-----+ 3 | b | 0 | | | | | | | +-----
+-----+-----+-----+-----+-----+-----+-----+ 4 | c | 0 | | | | | | | +-----+-----+-----
+-----+-----+-----+-----+-----+ 5 | f | 0 | | | | | | | +-----+-----+-----+-----+-----
+-----+-----+-----+
0 1 2 3 4 5 6 +-----+-----+-----+-----+-----+-----+-----+-----+ | chʳ | | a | b |
c | d | a | f | +-----+-----+-----+-----+-----+-----+-----+-----+ 0 | | 0 | 0 | 0 | 0
| 0 | 0 | 0 | +-----+-----+-----+-----+-----+-----+-----+-----+ 1 | a | 0 | 1 | 1 |
1 | 1 | 1 | 1 | +-----+-----+-----+-----+-----+-----+-----+-----+ 2 | c | 0 | 1 | 1 |
2 | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ 3 | b | 0 | | | | | | | +---
--+-----+-----+-----+-----+-----+-----+-----+ 4 | c | 0 | | | | | | | +-----+-----+---
--+-----+-----+-----+-----+-----+ 5 | f | 0 | | | | | | | +-----+-----+-----+-----+----
-+-----+-----+-----+
Table [ 0 ][ i] = 0
endfor
for i from 1 to s2.length
Table [ i][ 0 ] = 0
endfor
for i from 1 to s2.length
The time complexity for this algorithm is: O(mn) where m and n
denotes the length of each strings.
How do we find out the longest common subsequence? We'll start
from the bottom-right corner. We will check from where the value is
coming. If the value is coming from the diagonal, that is if Table[i-1]
[j-1] is equal to Table[i][j] - 1 , we push either s2[i] or s1[j] (both
are the same) and move diagonally. If the value is coming from top,
that means, if Table[i-1][j] is equal to Table[i][j] , we move to the
top. If the value is coming from left, that means, if Table[i][j-1] is
equal to Table[i][j] , we move to the left. When we reach the
leftmost or topmost column, our search ends. Then we pop the
values from the stack and print them. The pseudo-code:
Pseudo Code:
2. Give a recurrence.
For 1 <= i <= n, A( i) = 1 + max{ A( j)| 1 ≤ j < i and input( j) < input( i)} .
3. Compute the values of A.
4. Find the optimal solution.
Part 1:
m←1
for i : 2 ..n
if A( i) > A( m) then m ← i end if
end for
Part 2:
put a
while A( m) > 1 do
i ← m−1
while not( ai < am and A( i) = A( m) −1 ) do i ← i−1
end while
m←i
put a
end while
Recursive Solution:
Approach 1:
L[ i] = 1
for j = 1 to i − 1 do
if ( A[ j] < A[ i]) do
L[ i] = max( L[ i] , 1 + L[ j]) return L
MAIN( A[ 1 ..n ]):
L = LIS( A[ 1 ..n ])
return the maximum value in L
Time complexity in Iterative approach: O( n^ 2 )
Auxiliary Space: O( n)
Lets take {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}
as input. So, Longest Increasing Subsequence for the given input is
{0, 2, 6, 9, 11, 15} .
hashMap = {
s:1,
t:1,
a:1,
c:1,
k:1,
o:2,
v:1,
e:1,
r:1,
f:1,
l:1,
w:1
}
You can see hashKey 'o' is containing value 2 because o is 2 times in
string.
Now loop over str2 and check for each character are present in
hashMap, if yes, decrease value of hashMap Key, else return false
(which indicate it's not anagram).
hashMap = { s : 0 , t : 0 , a : 0 , c : 0 , k : 0 , o : 0 , v : 0 , e : 0 , r : 0 , f
:0,l:0,w:0
}
Now, loop over hashMap object and check all values are zero in the
key of hashMap. In our case all values are zero so its a anagram.
// Create hash map of str1 character and increase value one (+1).
createStr1HashMap( str1);
// Check str2 character are key in hash map and decrease value by
one(-1); var valueExist = createStr2HashMap( str2);
// Check all value of hashMap keys are zero, so it will be anagram.
return isStringsAnagram( valueExist);
}
});
}
function createStr2HashMap ( str2) {
var valueExist = [] .every .call ( str2, function( value, index, array){ if (
value in hashMap) {
});
return valueExist;
}
function isStringsAnagram ( valueExist) {
if (! valueExist) {
return valueExist;
} else {
var isAnagram;
for ( var i in hashMap) {
if ( hashMap[ i] !== 0 ) {
isAnagram = false ;
break ;
} else {
isAnagram = true ;
}
}
return isAnagram; }
}
isAnagram( 'stackoverflow' , 'flowerovstack' ); // true isAnagram(
'stackoverflow' , 'flowervvstack' ); // false
})(); Time complexity: 3n i.e O(n).
while ( k != 2 * i- 1 ) {
if ( count <= rows- 1 ) {
printf( "%d " , i+ k); ++ count;
}
else
{
++ count1;
printf( "%d " , ( i+ k- 2 * count1)); }
++ k;
}
count1 = count = k = 0 ;
1
232
34543
4567654567898765
14 15 16 17 18 21
19 10 20 11 54 36
64 55 44 23 80 39
91 92 93 94 95 42
Output:
print value in index
14 15 16 17 18 21 36 39 42 95 94 93 92 91 64 19 10 20 11 54 80 23
44 55
or print index
00 01 02 03 04 05 15 25 35 34 33 32 31 30 20 10 11 12 13 14 24 23
22 21
} else {
smallestValue = m;
}
squarePrint( 6 ,4 );
Given two matrices, know how to find their product. Further, given the
product matrix of two matrices, and one of them, know how to find the
other matrix.
Given a matrix of size d X d , know how to find its nth power in
O(d3log(n)) .
Patterns:
| f( n) | | f( n+ 1 ) | | f( n- 1 ) | | f( n) | M X | f( n- 2 ) | = | f( n- 1 ) | | ...... |
| ...... | | f( n- k) | | f( n- k+ 1 )|
So, if we can design M accordingly, our job will be done! The matrix
will then be used to represent the recurrence relation.
Type 1:
Let's start with the simplest one, f( n) = f( n- 1 ) + f( n- 2 )
We get, f( n+ 1 ) = f( n) + f( n- 1 ) .
Let's assume, we know f( n) and f( n- 1 ) ; We want to find out f( n+ 1
).
From the situation stated above, matrix A and matrix B can be
formed as shown below:
Matrix A Matrix B
| f( n) | | f( n+ 1 ) | | f( n- 1 ) | | f( n) |
[Note: Matrix A will be always designed in such a way that, every
state on which f( n+ 1 ) depends, will be present] Now, we need to
design a 2X2 matrix M such that, it satisfies M X A = B as stated
above.
The first element of B is f( n+ 1 ) which is actually f( n) + f( n- 1 ) . To
get this, from matrix A , we need, 1 X f(n) and 1 X f(n-1) . So the
first row of M will be [1 1] .
| 1 1 | X | f( n) | = | f( n+ 1 ) | | ----- | | f( n- 1 ) | | ------ |
[Note: ----- means we are not concerned about this value.]
Similarly, 2nd item of B is f( n) which can be got by simply taking 1 X
f(n) from A , so the 2nd row of M is [1 0].
| ----- | X | f( n) | = | ------ | | 1 0 | | f( n- 1 ) | | f( n) |
Then we get our desired 2 X 2 matrix M .
| 1 1 | X | f( n) | = | f( n+ 1 ) | | 1 0 | | f( n- 1 ) | | f( n) |
These matrices are simply derived using matrix multiplication.
Type 2:
Matrix A Matrix B | f( n) | | f( n+ 1 ) | | f( n- 1 ) | | f( n) |
| a b | X | f( n) | = | f( n+ 1 ) | | 1 0 | | f( n- 1 ) | | f( n) |
Pretty simple, eh?
Type 3:
| a 0 c | | f( n) | | f( n+ 1 ) | | 1 0 0 | X | f( n- 1 ) | = | f( n) | | 0 1 0 | | f( n-
2 ) | | f( n- 1 ) |
These are calculated in the same way as type 2, if you find it difficult,
try it on pen and paper.
Type 4:
Life is getting complex as hell, and Mr, Problem now asks you to find
f( n) = f( n- 1 ) + f( n- 2 ) + c where c is any constant.
Now this is a new one and all we have seen in past, after the
multiplication, each state in A transforms to its next state in B .
f ( n) = f( n- 1 ) + f( n- 2 ) + c f( n+ 1 ) = f( n) + f( n- 1 ) + c f( n+ 2 ) = f(
n+ 1 ) + f( n) + c .................... so on
| 1 1 1 | | f( n) | | f( n+ 1 ) | | 1 0 0 | X | f( n- 1 ) | = | f( n) | | 0 0 1 | | c | |
c|
Type 5:
|a0cd1||10000||01000||00100||00001|
Type 6:
Sometimes the recurrence is given like this:
f( n) = f( n- 1 ) -> if n is odd f( n) = f( n- 2 ) -> if n is even
In short:
f( n) = ( n& 1 ) X f( n- 1 ) + (!( n& 1 )) X f( n- 2 )
Here, we can split the functions in the basis of odd even and keep 2
different matrix for both of them and calculate them separately.
Type 7:
Feeling little too confident? Good for you. Sometimes we may need to
maintain more than one recurrence, where they are interested. For
example, let a recurrence re;atopm be:
g( n) = 2g( n- 1 ) + 2g( n- 2 ) + f( n)
Here, recurrence g(n) is dependent upon f( n) and this can be
calculated in the same matrix but of increased dimensions. From
these let's at first design the matrices A and B .
Matrix A Matrix B | g( n) | | g( n+ 1 ) | | g( n- 1 ) | | g( n) | | f( n+ 1 ) | | f(
n+ 2 ) | | f( n) | | f( n+ 1 ) |
|2210||1000||0022||0010|
So, these are the basic categories of recurrence relations which are
used to solveby this simple technique.
Chapter 53: polynomial-time
bounded algorithm for
Minimum Vertex Cover
Variable Meaning
G Input connected un-directed graph
X Set of vertices
C Final set of vertices
Sample = { 1 , 2 , 3 , 5 , 5 , 5 , 6 }
Test = { 1 , 1 , 2 , 2 , 3 , 5 }
We want to find out the optimal match between these two sequences.
At first, we define the distance between two points, d(x, y) where x
and y represent the two points. Let,
d( x, y) = | x - y| //absolute difference
Let's create a 2D matrix Table using these two sequences. We'll
calculate the distances between each point of Sample with every
points of Test and find the optimal match between them.
+------+------+------+------+------+------+------+------+ | | 0 | 1 | 1 | 2 | 2 | 3 |
5 | +------+------+------+------+------+------+------+------+ | 0 | | | | | | | | +-----
-+------+------+------+------+------+------+------+ | 1 | | | | | | | | +------+------
+------+------+------+------+------+------+ | 2 | | | | | | | | +------+------+------+-
-----+------+------+------+------+ | 3 | | | | | | | | +------+------+------+------+----
--+------+------+------+ | 5 | | | | | | | | +------+------+------+------+------+------
+------+------+ | 5 | | | | | | | | +------+------+------+------+------+------+------+-
-----+ | 5 | | | | | | | | +------+------+------+------+------+------+------+------+ | 6
| | | | | | | | +------+------+------+------+------+------+------+------+
For the first row, if we take no values from Sample , the distance
between this and Test will be infinity . So we put infinity on the first
row. Same goes for the first column. If we take no values from Test ,
the distance between this one and Sample will also be infinity. And
the distance between 0 and 0 will simply be 0 . We get,
+------+------+------+------+------+------+------+------+ | | 0 | 1 | 1 | 2 | 2 | 3 |
5 | +------+------+------+------+------+------+------+------+ | 0 | 0 | inf | inf |
inf | inf | inf | inf | +------+------+------+------+------+------+------+------+ | 1 |
inf | | | | | | | +------+------+------+------+------+------+------+------+ | 2 | inf | |
| | | | | +------+------+------+------+------+------+------+------+ | 3 | inf | | | | | |
| +------+------+------+------+------+------+------+------+ | 5 | inf | | | | | | | +---
---+------+------+------+------+------+------+------+ | 5 | inf | | | | | | | +------+--
----+------+------+------+------+------+------+ | 5 | inf | | | | | | | +------+------+-
-----+------+------+------+------+------+ | 6 | inf | | | | | | | +------+------+------
+------+------+------+------+------+
Now for each step, we'll consider the distance between each points in
concern and add it with the minimum distance we found so far. This
will give us the optimal distance of two sequences up to that position.
Our formula will be,
+------+------+------+------+------+------+------+------+ | | 0 | 1 | 1 | 2 | 2 | 3 |
5 | +------+------+------+------+------+------+------+------+ | 0 | 0 | inf | inf |
inf | inf | inf | inf | +------+------+------+------+------+------+------+------+ | 1 |
inf | 0 | 0 | 1 | 2 | 4 | 8 | +------+------+------+------+------+------+------+------
+ | 2 | inf | 1 | 1 | 0 | 0 | 1 | 4 | +------+------+------+------+------+------+-----
-+------+ | 3 | inf | 3 | 3 | 1 | 1 | 0 | 2 | +------+------+------+------+------+----
--+------+------+ | 5 | inf | 7 | 7 | 4 | 4 | 2 | 0 | +------+------+------+------+---
---+------+------+------+ | 5 | inf | 11 | 11 | 7 | 7 | 4 | 0 | +------+------+------
+------+------+------+------+------+ | 5 | inf | 15 | 15 | 10 | 10 | 6 | 0 | +------
+------+------+------+------+------+------+------+ | 6 | inf | 20 | 20 | 14 | 14 |
9 | 1 | +------+------+------+------+------+------+------+------+
We'll continue this till we reach (0, 0) . Each move has its own
meaning:
Table [ 0 ][ i] := infinity
end for
Table[ 0 ][ 0 ] := 0
for i from 1 to n
for j from 1 to m
Table[ i][ j] := d( Sample[ i] , Test[ j])
end for
end for
Return Table[ n + 1 ][ m + 1 ]
.
Signal decomposition, or ‘decimation in time’ is achieved by bit
reversing the indices for the array of time domain data. Thus, for a
sixteen-point signal, sample 1 (Binary 0001) is swapped with sample
8 (1000), sample 2 (0010) is swapped with 4 (0100) and so on.
Sample swapping using the bit reverse technique can be achieved
simply in software, but limits the use of the Radix 2 FFT to signals of
length N = 2^M.
The value of a 1-point signal in the time domain is equal to its value in
the frequency domain, thus this array of decomposed single time-
domain points requires no transformation to become an array of
frequency domain points. The N single points; however, need to be
reconstructed into one N-point frequency spectra. Optimal
reconstruction of the complete frequency spectrum is performed
using butterfly calculations. Each reconstruction stage in the Radix-2
FFT performs a number of two point butterflies, using a similar set of
exponential weighting functions, Wn^R.
The complete butterfly flow diagram for an eight point Radix 2 FFT is
shown below. Note the input signals have previously been reordered
according to the decimation in time procedure outlined previously.
A C/C++ code sample for computing the Radix 2 FFT can be found
below. This is a simple implementation which works for any size N
where N is a power of 2. It is approx 3x slower than the fastest FFTw
implementation, but still a very good basis for future optimisation or
for learning about how this algorithm works.
#include <math.h>
int BSep;
int BWidth;
int P;
int j;
int stage = 1;
(1 to M).
int HiIndex;
butterfly calc
unsigned int iaddr; int ii;
int MM1 = M - 1;
// HiIndex is the index of the DFT array for the top value of each
// bitmask for bit reversal
// Integer bitfield for bit reversal (Decimation in Time)
// Pointer to x[n]
2.
TwoPi_NP = TwoPi_N*P;
}
else
{
}
}
}
}
Note : both frequency and time domain data are complex variables.
Typically the imaginary component of the time domain signal following
an inverse FFT is either zero, or ignored as rounding error. Increasing
the precision of variables from 32-bit float to 64-bit double, or 128-bit
long double significantly reduces rounding errors produced by several
consecutive FFT operations.
int i;
complex* x;
for ( i = 0, x = pX; i < N; i++, x++){
Appendix A: Pseudocode
Section A.1: Variable a☐ectations
You could describe variable affectation in different ways.
Typed
int a = 1
int a := 1
let int a = 1
int a < 1
No type
a=1
a := 1
let a = 1 a < 1