You are on page 1of 15

I

Ξ Getting Started

Tools to Start
A programming language (e.g. c, c++, java, c#). A C/C++ compiler (Microsoft Visual
C++ or GCC is the best and provides nice debugging and STL facilities, while Turbo
C/C++ does not)

Choosing Online Judge


‘UVa Online Judge’ is a good (judges your solution 24 hours) one. Link
http://uva.onlinejudge.org/ . Most BD students are there. Hundreds of problems are
available there.

Common Problems
There are some common problems we may face when practicing in ‘UVa Online Judge’.
They are discussed here http://www.acm.org/crossroads/xrds7-5/contests.html

Useful Links
http://felix-halim.net/uva/hunting.php - helpful if we practice in ‘UVa Online judge’ site.
http://ace.delos.com/usacogate - provides training program gateway

Ξ Orientation Problem

Problem Statement:

For a given integer N, compute the sum of integers between 1 and N inclusive.

Input:
The input consists of integers, one per line. The end of input is denoted by the integer 0.
Output:
For each integer N in the input except the terminating 0, print a single line of output
containing two values: the input value N, appropriately labeled, and the sum of all the
integers between 1 and N inclusive, also appropriately labeled.

Sample Input Acceptable Output for the Sample Input


3 N = 3 Sum = 6
6 N = 6 Sum = 21
0

So for this orientation problem we must produce the exact output for every N except for
N = 0 and for this case the program must terminate. And also we must output exactly the
same way as illustrated in the sample output. We must always follow the input and output
constraints of a given problem.
II

Ξ Simple Data Structures

Even though computers can perform literally millions of mathematical computations per
second, when a problem gets large and complicated, performance can nonetheless be an
important consideration. One of the most crucial aspects to how quickly a problem can be
solved is how the data is stored in memory.

Variables
The simplest data structures are primitive variables. They hold a single value, and beyond
that, are of limited use.

Arrays
Arrays are a very simple data structure, and may be thought of as a list of a fixed length.
Arrays are nice because of their simplicity, and are well suited for situations where the
number of data items is known (or can be programmatically determined). Suppose you
need a piece of code to calculate the average of several numbers. An array is a perfect
data structure to hold the individual values, since they have no specific order, and the
required computations do not require any special handling other than to iterate through all
of the values. The other big strength of arrays is that they can be accessed randomly, by
index. For instance, if you have an array containing a list of names of students seated in a
classroom, where each seat is numbered 1 through n, then studentName[i] is a trivial way
to read or store the name of the student in seat i.
An array might also be thought of as a pre-bound pad of paper. It has a fixed number of
pages, each page holds information, and is in a predefined location that never changes.

Excercises:
1. Suppose you have a calculation which may produce a integer result <=10 15 . If
we want to store the result in a variable, what should be the type of the variable?
2. Name some other data structures used frequently.

Σ Fundamentals

Divisors and Divisibility


We say that m divides n (or n is divisible by m) if m > 0 and the ratio n/m is an integer.
We therefore write m|n where m > 0 and n = km for some integer k.

Theorem: Let n be an integer and d a positive integer. Then there are unique integers q
and r , with 0 ≤ r < d , such that n = dq + r.

If d is a divisor of n, then so is n/d, but d & n/d cannot both be greater than sqrt(n). 2 is a divisor
of 6, so 6/2 = 3 is also a divisor of 6.
Exercises: How many divisors 10 3 have? (without help of a computer)

GCD
The greatest common divisor (GCD) of two numbers a and b is the greatest number that
divides evenly into both a and b. Naively we could start from the smallest of the two
III

numbers and work our way downwards until we find a number that divides into both of
them:
for (int i=Math.min(a,b); i>=1; i--)
if (a%i==0 && b%i==0)
return i;
Although this method is fast enough for most applications, there is a faster method called
Euclid's algorithm. Euclid's algorithm iterates over the two numbers until a remainder of
0 is found. For example, suppose we want to find the GCD of 2336 and 1314. We begin
by expressing the larger number (2336) in terms of the smaller number (1314) plus a
remainder:
2336 = 1314 x 1 + 1022
We now do the same with 1314 and 1022:
1314 = 1022 x 1 + 292
We continue this process until we reach a remainder of 0:
1022 = 292 x 3 + 146
292 = 146 x 2 + 0
The last non-zero remainder is the GCD. So the GCD of 2336 and 1314 is 146.
Exercises:
1. Find the LCM of two positive integer a and b, LCM(a, b).
a
2. We are given a fraction where a is the numerator and b is the denominator
b
6 2
a,b > 0. Reduce the fraction in lowest term. e.g. is reduced to .
15 5

Moduler Arithmatic

Definition: If a and b are integers and m is a positive integer, then a is congruent to b


modulo m if m divides a – b. Notation: a ≡ b (mod m) means that a is congruent to b
modulo m.
Theorem: Let a and b be integers, and let m be a positive integer. Then a ≡ b (mod m) if
and only if a mod m = b mod m.
Formulae: (a*b*c) mod N == ((a mod N) * (b mod N) * (c mod N)) mod N
Exercise: What is the last digit of 2 700 ?

Primes

A number is prime if it is only divisible by 1 and itself. So for example 2, 3, 5, 79, 311
and 1931 are all prime, while 21 is not prime because it is divisible by 3 and 7The
Fundamental Theorem of Arithmetic:
Any positive integer can be divided in primes in essentially only one way. The phrase
'essentially one way' means that we do not consider the order of the factors important.

One is neither a prime nor composite number. One is not composite because it doesn’t
have two distinct divisors. If one is prime, then number 6, for example, has two different
representations as a product of prime numbers: 6 = 2 * 3 and 6 = 1 * 2 * 3. This would
contradict the fundamental theorem of arithmetic.
IV

.To find if a number n is prime we could simply check if it divides any numbers below it.
We can use the modulus (%) operator to check for divisibility:

for (int i=2; i<n; i++)

if (n%i==0) return false;

return true;

We can make this code run faster by noticing that we only need to check divisibility for
values of i that are less or equal to the square root of n (call this m). If n divides a number
that is greater than m then the result of that division will be some number less than m and
thus n will also divide a number less or equal to m. Another optimization is to realize that
there are no even primes greater than 2. Once we've checked that n is not even we can
safely increment the value of i by 2. We can now write the final method for checking
whether a number is prime:

boolean isPrime (int n)


{
if (n<=1) return false;
if (n==2) return true;
if (n%2==0) return false;
int m=Math.sqrt(n);

for (int i=3; i<=m; i+=2)


if (n%i==0)
return false;

return true;
}
Now suppose we wanted to find all the primes from 1 to 100000, then we would have to
call the above method 100000 times. This would be very inefficient since we would be
repeating the same calculations over and over again. In this situation it is best to use a
method known as the Sieve of Eratosthenes. The Sieve of Eratosthenes will generate all
the primes from 2 to a given number n. It begins by assuming that all numbers are prime.
It then takes the first prime number and removes all of its multiples. It then applies the
same method to the next prime number. This is continued until all numbers have been
processed. For example, consider finding primes in the range 2 to 20. We begin by
writing all the numbers down:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
2 is the first prime. We now cross out all of its multiples, ie every second number:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
The next non-crossed out number is 3 and thus it is the second prime. We now cross out
all the multiples of 3, ie every third number from 3:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
All the remaining numbers are prime and we can safely terminate the algorithm. Below is
the code for the sieve:
V

public boolean[] sieve(int n)


{
boolean[] prime=new boolean[n+1];
Arrays.fill(prime,true);
prime[0]=false;
prime[1]=false;
int m=Math.sqrt(n);

for (int i=2; i<=m; i++)


if (prime[i])
for (int k=i*i; k<=n; k+=i)
prime[k]=false;

return prime;
}

In the above method, we create a boolean array prime which stores the primality of each
number less of equal than n. If prime[i] is true then number i is prime. The outer loop
finds the next prime while the inner loop removes all the multiples of the current prime.

Theorem: If n is a composite integer, then n has a prime divisor less than or equal to n.

Proof: If n is composite, it has a factor a with 1 < a < n. Hence, n = ab, (a; b both
positive integers > 1). So we see that a ≤ n or b ≤ n since otherwise, ab > n . n
Hence, n has a positive divisor not exceeding n . This divisor is either prime or, by the
Fundamental Theorem of Arithmetic, has a prime divisor. In either case, n has a prime
divisor p ≤ n

Trial division:
Trial division is the simplest of all factorization techniques. It represents a brute-force
method, in which we are trying to divide n by every number i not greater than the square
root of n. (Why don't we need to test values larger than the square root of n?) The
procedure factor prints the factorization of number n. The factors will be printed in a line,
separated with one space. The number n can contain no more than one factor, greater than
n.

factor(int n)
{
for(i=2;i<=(int)sqrt(n);i++)
{
while(n % i == 0)
{
// is is a factor
n /= i;
}
}
VI

if (n > 1) // n itself is prime;


}

Consider a problem that asks you to find the factorization of integer g(-231 < g <231) in
the form g = f1 x f2 x … x fn

Goldbach's Conjecture:
For any integer n (n ≥ 4) there exist two prime numbers p1 and p2 such that p1 + p2 = n. In
a problem we might need to find the number of essentially different pairs (p1, p2),
satisfying the condition in the conjecture for a given even number n (4 ≤ n ≤ 2 15). (The
word ‘essentially’ means that for each pair (p1, p2) we have p1 ≤ p2.)

For example, for n = 10 we have two such pairs: 10 = 5 + 5 and 10 = 3 + 7. To solve


this,as n ≤ 215 = 32768, we’ll fill an array primes[32768] using function sieve. We are
interested in primes, not greater than 32768.

The function FindSol(n) finds the number of different pairs (p1, p2), for which n = p1 + p2.
As p1 ≤ p2, we have p1 ≤ n/2. So to solve the problem we need to find the number of pairs
(i, n – i), such that i and n – i are prime numbers and 2 ≤ i ≤ n/2.

Matrices and 2D array


Matrices can be used in a wide variety of models. Many algorithms is developed that use
matrix models. Matrices can be manipulated as tow dimensional array. Consider the
1 2 3 
following 3 × 3 matrix: 4 5 6 this matrix can be stored as 2D array A[3][3] in C.
7 8 9 
If we want to iterate through all the elements of the matrix in row major , we can code it
as:
for (i=0; i<numRow; i++)
for (j=0; j<numCol; j++){
// process A[i][j]
}
Exercises:
1. Find the trace of a matrix (n by n). The answer for the above matrix is 15.
In linear algebra, the trace of an n-by-n square matrix A is defined to be the sum of the
elements on the main diagonal (the diagonal from the upper left to the lower right) of A,
i.e.,

2. Find the sum of the upper triangle for a matrix(n by n). For the above triangle
the sum is 26.
VII

3. Find the transpose of the above matrix. Interchange the rows into columns and
columns into rows.

Basics of Combinatorics: [Permutaion & Combination, The Pigeonhole Principle]

Combinatorial primitives:
The rule of sum

For example, each position on a car’s license plate is a letter or a digit. The numbers of
possibilities for each position is therefore 26 + 10 = 36, since there are 26 choices if it is a
letter an 10 choices if it is a digit.

Exercise: What is the value of k after the following code has been executed?
k := 0;
for i 1 := 1 to n 1
k := k + 1;
for i 2 := 1 to n 2
k := k + 1;
………..
for i m := 1 to n m
k := k + 1;

The rule of product

For example, if we have three towns -- A, B and C -- and there are 3 roads from A to B
and 5 roads from B to C, then we can get from A to C through B in 3*5=15 different
ways.

Exercise:: What is the value of k after the following code has been executed?

k := 0;
for i 1 := 1 to n 1
for i 2 := 1 to n 2
………..
for i m := 1 to n m
k := k + 1;

These rules can be used for a finite collections of sets.

Permutation (without repetition)


VIII

Let consider a, b, c, taking two at a time we get the order ab, ba, ac, ca, bc, cb = 6

When we choose k objects from n-element set in such a way that the order matters and
n!
each object can be chosen only once: n P r =
(n − r )!

For example, suppose we are planning the next 3 contests and we have a set of 10 easy
problems to choose from. We will only use one easy problem in each contest, so we can
10!
choose our problems in 10 P 3 = different ways.
(10 − 3)!

Permutation (variation) with repetition

The number of possible choices of k objects from a set of n objects when order is
important and one object can be chosen more than once:

nk
For example, if we have 10 different prizes that need to be divided among 5 people, we
can do so in 510 ways.

Permutation with repetition


n n!
Pr = (p, q, r repeated objects)
p!q!r!

Combination

Let consider a, b, c, taking two at a time we get the set (ignoring order) ab or ba, ac or ca,
bc or cb = 3

n n!
Cr =
r!(n − r )!
Corollary:
Let n and r be nonnegative integers with r ≤ n , then n C r = n C n − r

An example: How many ways there to select five coder from a 10 member contestants
team to make a trip to a contest at another university?
We have to take 5 combinations of a set with ten elements. So there are 10 C 5 ways to do
this.

Exercises:
1. How many permutations of the letter s ABCDEFGH contain the string ABC?
2. Among 100 people, at least how many people were born in the same month?
3. what STL algorithm “next_permutation(),prev_permutation()”
does?
IX

The Pigeonhole Principle


If N objects are placed into K boxes, then there is at least one box containing at least
N 
 K  objects. (or, If K + 1 or more objects are placed into K boxes, then there is at least
one box containing two or more of the objects.)
Exercise: Show that for every integer n there is a multiple of n (the least one) that has
only 0s and 1s in its decimal expansion. For example, if n = 4 then the multiple is 100.

Flavors of Graphs
A graph G = (V,E) is defined by a set of vertices V , and a set of edges E consisting
of ordered or unordered pairs of vertices from V . In modeling a road network, the
vertices may represent the cities or junctions, certain pairs of which are connected by
roads/edges.

The graph is normally represented using that analogy. Vertices are points or circles,
edges are lines between them. In this example graph: V = {1, 2, 3, 4, 5, 6}
E = {(1,3), (1,6), (2,5), (3,4), (3,6)}. Each vertex is a member of the set V. A vertex is
sometimes called a node. Each edge is a member of the set E. Note that some vertices
might not be the end point of any edge. Such vertices are termed ``isolated''.
Sometimes, numerical values are associated with edges, specifying lengths or costs; such
graphs are called edge-weighted graphs (or weighted graphs). The value associated with
an edge is called the weight of the edge.

Terminology: A graph is simple if it neither contains self-loops nor contains an edge that
is repeated in E. A graph is called a multigraph if it contains a given edge more than once
or contain self-loops. For our discussions, graphs are assumed to be simple. The example
graph is a simple graph. The degree of a vertex is the number of edges which are incident
to it. For example, vertex 3 has degree 3, while vertex 4 has degree 1. Vertex u is
adjacent to vertex v if there is some edge to which both are incident (that is, there is an
edge between them). For example, vertex 2 is adjacent to vertex 5.
A path from vertex u to vertex x is a sequence of vertices (v 0, v 1, ..., v k) such that v 0 =
u and v k = x and (v 0, v 1) is an edge in the graph, as is (v 1, v 2), (v 2, v 3), etc. The
length of such a path is k. For example, in the undirected graph above, (4,
X

3, 1, 6) is a path. This path is said to contain the vertices v 0, v 1, etc., as well as the
edges (v 0, v 1), (v 1,v 2), etc.

Exercise: There are n cities numbered through 1, 2, 3…i… n. Given some pair of integer
(x i , y j ) That means there is railway between city x i and y j . Can we go from a city x to
city y directly or indirectly by train. Say n = 10, given pairs are (1,2), (7, 2), (9,2), (5,4),
(10, 3), (3, 6), (6, 7) then can we go from city 2 to city 5?
Hint: graph

Graph Representation
Adjacency matrix representation of the sample graph.

Θ Algorithms

Introduction
The first step towards an understanding of why the study and knowledge of algorithms
are so important is to define exactly what we mean by an algorithm. According to the
popular algorithms textbook Introduction to Algorithms (Second Edition by Thomas H.
Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein), "an algorithm is any
well-defined computational procedure that takes some value, or set of values, as input
and produces some value, or set of values as output." In other words, algorithms are like
road maps for accomplishing a given, well-defined task. So, a chunk of code that
calculates the terms of the Fibonacci sequence is an implementation of a particular
algorithm. Even a simple function for adding two numbers is an algorithm in a sense,
albeit a simple one.

Some algorithms, like those that compute the Fibonacci sequences, are intuitive and may
be innately embedded into our logical thinking and problem solving skills. However, for
most of us, complex algorithms are best studied so we can use them as building blocks
for more efficient logical problem solving in the future. In fact, you may be surprised to
learn just how many complex algorithms people use every day when they check their e-
mail or listen to music on their computers. This article will introduce some basic ideas
related to the analysis of algorithms, and then put these into practice with a few examples
illustrating why it is important to know about algorithms.

Runtime Analysis
One of the most important aspects of an algorithm is how fast it is. It is often easy to
come up with an algorithm to solve a problem, but if the algorithm is too slow, it's back
XI

to the drawing board. Since the exact speed of an algorithm depends on where the
algorithm is run, as well as the exact details of its implementation, computer scientists
typically talk about the runtime relative to the size of the input. For example, if the input
consists of N integers, an algorithm might have a runtime proportional to N2, represented
as O(N2). This means that if you were to run an implementation of the algorithm on your
computer with an input of size N, it would take C*N2 seconds, where C is some constant
that doesn't change with the size of the input.

However, the execution time of many complex algorithms can vary due to factors other
than the size of the input. For example, a sorting algorithm may run much faster when
given a set of integers that are already sorted than it would when given the same set of
integers in a random order. As a result, you often hear people talk about the worst-case
runtime, or the average-case runtime. The worst-case runtime is how long it would take
for the algorithm to run if it were given the most insidious of all possible inputs. The
average-case runtime is the average of how long it would take the algorithm to run if it
were given all possible inputs. Of the two, the worst-case is often easier to reason about,
and therefore is more frequently used as a benchmark for a given algorithm. The process
of determining the worst-case and average-case runtimes for a given algorithm can be
tricky, since it is usually impossible to run an algorithm on all possible inputs. There are
many good online resources that can help you in estimating these values.

Approximate completion time for algorithms, N = 100

O(Log(N)) 10-7 seconds

O(N) 10-6 seconds

O(N*Log(N)) 10-5 seconds

O(N2) 10-4 seconds

O(N6) 3 minutes

O(2N) 1014 years.

O(N!) 10142 years.


XII

Binary Search
Binary search is one of the fundamental algorithms in computer science.

Finding a value in a sorted sequence


In its simplest form, binary search is used to quickly find a value in a sorted sequence
(consider a sequence an ordinary array for now). We'll call the sought value the target
value for clarity. Binary search maintains a contiguous subsequence of the starting
sequence where the target value is surely located. This is called the search space. The
search space is initially the entire sequence. At each step, the algorithm compares the
median value in the search space to the target value. Based on the comparison and
because the sequence is sorted, it can then eliminate half of the search space. By doing
this repeatedly, it will eventually be left with a search space consisting of a single
element, the target value.

For example, consider the following sequence of integers sorted in ascending order and
say we are looking for the number 55:

0 5 13 19 22 41 55 68 72 81 98

We are interested in the location of the target value in the sequence so we will represent
the search space as indices into the sequence. Initially, the search space contains indices 1
through 11. Since the search space is really an interval, it suffices to store just two
numbers, the low and high indices. As described above, we now choose the median
value, which is the value at index 6 (the midpoint between 1 and 11): this value is 41 and
it is smaller than the target value. From this we conclude not only that the element at
index 6 is not the target value, but also that no element at indices between 1 and 5 can be
the target value, because all elements at these indices are smaller than 41, which is
smaller than the target value. This brings the search space down to indices 7 through 11:

55 68 72 81 98

Proceeding in a similar fashion, we chop off the second half of the search space and are
left with:

55 68

Depending on how we choose the median of an even number of elements we will either
find 55 in the next step or chop off 68 to get a search space of only one element. Either
way, we conclude that the index where the target value is located is 7.

If the target value was not present in the sequence, binary search would empty the search
space entirely. This condition is easy to check and handle. Here is some code to go with
the description:
XIII

binary_search(A, target):
lo = 0, hi = size(A)
while lo <= hi:
mid = (hi+lo)/2
if A[mid] == target:
return mid
else if A[mid] < target:
lo = mid+1
else:
hi = mid-1

// target was not found


Complexity:
Since each comparison binary search uses halves the search space, we can assert and
easily prove that binary search will never use more than (in big-oh notation) O(log N)
comparisons to find the target value.
The logarithm is an awfully slowly growing function. In case you're not aware of just
how efficient binary search is, consider looking up a name in a phone book containing a
million names. Binary search lets you systematically find any given name using at most
21 comparisons. If you could manage a list containing all the people in the world sorted
by name, you could find any person in less than 35 steps.
Exercises: Comparison between Binary Search with Linear Search. Breadth-First
Search, (on graph): Depth-First Search, Minimum Spanning Tree, Disjoint Set Forest,
getting shortest path,

Basics of Geometry & C Structure

Consider this problem which was used in SRM 374.


"Given the rink's specification and the players' positions, create a method numPlayers
that returns the number of players on the boundary or inside the rink."

So, there are 2 circles, 1 rectangle, the result is the union of the points inside these three
area. to check weather a point is inside a circle or a rectangle or on the boundary we
XIV

know- if (x 1 , y 1 ) be Lower Left corner and (x 2 ,y 2 ) be Upper Right corner of the


rectangle. then (p x ,p y ) is inside or on the rectangle boundary if and only if (x 1 ≤ p x
&& p x ≤ x 2 ) and (y 1 ≤ p y && p y ≤ y 2 ). if (c x ,c y ) be circle center, r be radius, then
(p x , p y ) is inside or on the circle boundary if and only if (p x - c x ) 2 + (p y - c y ) 2 ≤ r 2

Point struct point {


int x;
int y;
}
Exercise: Find the distance between two points.
High-Precision Arithmetic: Big Integer

Today’s PCs are typically 32-bit machines, meaning that the standard integer data
type supports integers roughly in the range ±2 31 = ± 2,147,483,648. Thus we can safely
count up to a billion or so with standard integers on conventional machines. Most
programming languages support long or even long long integer data types, which
often define 64-bit. Since 2 63 = 9,223,372,036,854,775,808, we are talking several orders
of magnitude beyond trillions.

But representing truly enormous integers requires stringing digits together. Fortunately,
the integer classes for C++ and Java are very powerful.
The java.math.BigInteger class both provide full support for high precision
integers.

Basic idea on stringing integers:


We can write 1234510 = 1x104 + 2x103 + 3x102 + 4x101 + 5x100. In general, a positive
number can be written as a polynomial of order k, where k is non-negative and ak != 0.

n = akbk + ak-1bk-1 + ... + a1b1 + a0 , b represents the base with 0 <= a <= b-1.

For base 10 representation, b = 10 and the digits allowed at each position is 0 <= a <= 9.

The addition of two numbers can be represented as the addition of two polynomials as
shown below.

Let
n = akbk + ak-1bk-1 + ... + a1b1 + a0
m = ckbk + ck-1bk-1 + ... + c1b1 + c0

Then
n + m = (ak + ck)bk + (ak-1 + ck-1)bk-1 + ... + (a1 + c1)b1 + (a0 + c0)

A rough implementation of the above idea:


XV

// F[], reversed first number, S[] reversed second number,


// R[] , reversed result.
for(now = 0,carry = 0;(now < f && now < s); now++){
sum =(F[now] - '0') + (S[now] - '0') + carry;
R[now] = sum % 10 + '0';
Carry = sum / 10;
}
for( ;now < f; now++){
sum = F[now] + carry - '0';
R[now] = sum % 10 + '0';
carry = sum/10;
}

for( ;now < s; now++){


sum = F[now] + carry - '0';
R[now] = sum % 10 + '0';
carry = sum / 10;
}
if(carry != 0) R[now++] = carry + '0';
R[now] = '\0';
The above rough implementation is just a approach to show that how we can perform
arithmetic on a string.
The details implementation of the addition, subtraction, multiplication and division of
large integer using string can be found at Ref. 3.
Exercises:
1. What are the limitations of the above implementation of adding two big number?
2. Handling Java BigInteger class

References
1. Discrete Mathematics and Its Applications – Rosen
2. Concrete Mathematics – Graham, Knuth, Patashnik
3. Programming Challenges – Skiena, Revilla
4. Art of Computer Programming- Arefin
5. TopCoder articles: http://www.topcoder.com/tc

Instructed by:
Md. Iqbal Aziz Khan.
Assistant Professor
Dept of CSE, University of Rajshahi.
Prepared by:
Md. Abul Kalam Azad.
Student, Dept of CSE, University of Rajshahi.
Acknowledgements
Md. Abul Kalam Azad is grateful to Dr. Shamim Ahmad, Mr. Iqbal Aziz Khan, Md. Ashraf-ul Asad,
Mahboob Qaosar, Md. Mahbubul Islam, Jane Alam Jan, above all his dept. family for their helps and for
inspiring him. He is also grateful to all his friends for helping him to prosper and inspiring him.

You might also like