Unit 1
1) Define the terms:
i. Algorithm
ii. Relation
iii. Time Complexity
iv. Space Complexity
i. Algorithm
An algorithm is a finite sequence of well-defined instructions or steps, designed to solve a specific problem or perform a computation. It provides a systematic method for carrying out a task, where each step is unambiguous and executable.
ii. Relation
A relation in mathematics and computer science refers to a connection between elements of two or more sets. More formally, a relation is a subset of the Cartesian product of sets, expressing associations such as equality, ordering, or correspondence between elements.
iii. Time Complexity
Time complexity measures the amount of computational time an algorithm takes to run as a function of the size of its input. It is typically expressed using Big O notation (such as O(n), O(log n)), which describes the upper bound on the growth rate of the algorithm’s running time as input size increases.
iv. Space Complexity
Space complexity refers to the total amount of memory or space an algorithm requires to run, as a function of the input size. It considers all memory used by an algorithm, including input storage, auxiliary space, and stack space, and is also expressed using Big O notation.
2) Explain various properties of an algorithm.
An algorithm is a well-defined computational procedure that takes some value or a set of values as input and produces some value or set of values as output. Here are the key properties of a valid algorithm:
1. Finiteness ⏳
An algorithm must terminate after a finite number of steps. Each step must also be finite in its execution. An infinite loop, for example, would violate this property.
2. Definiteness 📝
Every step of the algorithm must be precisely and unambiguously defined. The instructions should be clear and leave no room for subjective interpretation. For example, "add 5 to x" is definite, while "make x a little bigger" is not.
3. Input 📥
An algorithm must have zero or more well-defined inputs, which are the quantities supplied to it before the algorithm begins. These inputs are external to the algorithm.
4. Output 📤
An algorithm must have one or more well-defined outputs. These are the quantities that have a specified relation to the inputs. The output is the desired result of the algorithm.
5. Effectiveness 🎯
The operations of the algorithm must be sufficiently basic so that they can be performed in a finite amount of time by a person using a pencil and paper. This means each step should be simple and feasible, not requiring infinite time or resources. For example, "find the largest prime number" is not an effective step as it's not possible to perform in a finite time.
Additional Properties
While the above are the core properties, an effective algorithm often has these additional characteristics:
- Correctness: An algorithm is considered correct if, for every input instance, it halts with the correct output. An incorrect algorithm might not halt on some input instances or might halt with a wrong answer.
- Efficiency: An algorithm should be efficient in terms of both time complexity (the time it takes to run) and space complexity (the memory it uses). An efficient algorithm is one that uses minimal time and space resources.
- Generality: An algorithm should be general enough to solve a class of problems, not just a single, specific instance. For example, an algorithm to sort a list of 100 numbers is less general than one that can sort a list of any size.
3) What is matrix? Explain the various operations which can be performed on the matrix.
A matrix is a rectangular array of numbers, symbols, or expressions arranged in rows and columns. It's a fundamental concept in linear algebra used to represent and solve systems of linear equations and to perform linear transformations. The size, or dimension, of a matrix is given by the number of rows () and columns (), written as . For example, a matrix has 2 rows and 3 columns.
Common Matrix Operations
Various operations can be performed on matrices, each with specific rules and applications.
1. Addition and Subtraction ➕➖
Two matrices can be added or subtracted only if they have the exact same dimensions. The operation is performed by adding or subtracting the corresponding elements in each position.
- Example: If and are two matrices, their sum is given by: ,
2. Scalar Multiplication 🎯
This operation involves multiplying every element of a matrix by a single number, called a scalar. The result is a new matrix of the same dimensions.
- Example: If is a matrix and is a scalar, the product is:
3. Matrix Multiplication ✖️
Multiplying two matrices is more complex than scalar multiplication. The product of two matrices, and , is defined only if the number of columns in equals the number of rows in . If is an matrix and is an matrix, the resulting product will be an matrix. Each element of the product matrix is the sum of the products of the corresponding elements from the -th row of and the -th column of .
- Note: Matrix multiplication is not commutative ( in most cases).
4. Transpose 🔄
The transpose of a matrix is a new matrix created by flipping its rows into columns and its columns into rows. The transpose of matrix is denoted by . If is an matrix, its transpose will be an matrix.
- Example:
5. Determinant and Inverse 🔺↩️
These are special operations typically defined for square matrices (matrices with the same number of rows and columns).
- Determinant: A determinant is a scalar value calculated from the elements of a square matrix. It provides important information about the matrix, such as whether it has an inverse. A matrix with a determinant of zero is called a singular matrix and does not have an inverse.
- Inverse: The inverse of a square matrix , denoted , is another square matrix that, when multiplied by , results in the identity matrix (). The identity matrix is the matrix equivalent of the number 1, with ones on the main diagonal and zeros elsewhere. An inverse exists only if the matrix's determinant is non-zero.
4) List types of algorithms.
There are many ways to classify algorithms, but they are often categorized based on their underlying design paradigm or the problem they are used to solve. Here are some of the most common types:
1. Simple Recursive Algorithms ↩️
These algorithms solve a problem by calling themselves repeatedly with smaller inputs until they reach a base case. A classic example is the algorithm for calculating the factorial of a number, where factorial(n) is defined in terms of factorial(n-1).
2. Backtracking Algorithms 👣
Backtracking is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems. It incrementally builds a solution and, if at any point the solution is not valid, it "backs up" and tries a different path. This is often used for solving puzzles like Sudoku or the N-Queens problem.
3. Divide and Conquer Algorithms ➗
This powerful paradigm involves three steps:
- Divide: Break the problem into smaller subproblems of the same type.
- Conquer: Recursively solve these subproblems.
- Combine: Merge the solutions of the subproblems to get the final solution. Examples include Merge Sort and Quick Sort.
4. Dynamic Programming Algorithms 🧠
Dynamic programming is used to solve complex problems by breaking them down into simpler subproblems. Unlike simple recursion, it stores the results of subproblems to avoid recomputing them. This approach is often used for optimization problems, such as the Fibonacci sequence or the knapsack problem.
5. Greedy Algorithms ✨
A greedy algorithm makes the locally optimal choice at each stage with the hope of finding a global optimum. It doesn't look ahead to see if the current choice will be beneficial in the long run. A good example is Dijkstra's algorithm for finding the shortest path in a graph.
6. Brute Force Algorithms 💪
This is the simplest and most straightforward approach, which tries all possible solutions to a problem until a satisfactory one is found. While often inefficient for large inputs, it's guaranteed to find a solution if one exists. A common example is trying every possible password in a password attack.
7. Randomized Algorithms 🎲
These algorithms make use of a random number generator to make decisions. They are particularly useful when a deterministic approach is too slow or complex. Examples include Randomized Quick Sort and some primality testing algorithms.
8. Sorting and Searching Algorithms 🔍
- Sorting Algorithms: Arrange data in a specific order (e.g., numerical or alphabetical). Examples include Bubble Sort, Insertion Sort, and Heap Sort.
- Searching Algorithms: Find a specific element within a data structure. Examples include Linear Search and Binary Search.
9. Cryptographic Algorithms 🔐
These algorithms are designed to protect data and communication from unauthorized access. They use mathematical principles to encrypt and decrypt information. Examples include AES (Advanced Encryption Standard) and RSA.
5) Explain the various operations which can be performed on the sets.
Various operations can be performed on sets, which are unordered collections of unique elements. These operations allow you to combine, compare, and modify sets. Here are the primary ones:
1. Union ∪
The union of two sets, and , is a new set that contains all the elements that are in , or in , or in both. The symbol for union is .
- Example: If and , then .
2. Intersection ∩
The intersection of two sets, and , is a new set containing only the elements that are common to both and . The symbol for intersection is .
- Example: If and , then .
3. Difference -
The difference of two sets, and (written as ), is the set of all elements that are in but not in . Note that is not the same as .
- Example: If and , then .
4. Symmetric Difference
The symmetric difference of two sets, and , is the set of elements that are in either or , but not in their intersection. It can be expressed as or . The symbol for symmetric difference is .
- Example: If and , then .
5. Cartesian Product ×
The Cartesian product of two sets, and (written as ), is the set of all possible ordered pairs where the first element is from and the second element is from .
- Example: If and , then .
6. Subset ⊆ and Proper Subset ⊂
A set is a subset of set if every element of is also an element of . The symbol is . A set is a proper subset of set if is a subset of and is not equal to . The symbol is .
- Example: If and , then and . If , then , but is not a proper subset of .
6) Why do we use asymptotic notations in the study of algorithms? Briefly describe the commonly used asymptotic notations.
We use asymptotic notations in the study of algorithms to analyze their efficiency and understand how their performance changes as the input size grows. Instead of measuring the exact runtime or memory usage, which can vary based on hardware and specific implementation, asymptotic notations provide a high-level, machine-independent way to describe the growth rate of an algorithm's complexity.
This allows us to compare algorithms theoretically and determine which one is more efficient for large datasets, without needing to run them.
Commonly Used Asymptotic Notations
1. Big O Notation () 📈
Big O notation describes the upper bound of an algorithm's running time. It gives the worst-case scenario for an algorithm's performance. When we say an algorithm is , we mean its runtime will not grow faster than a constant times for large values of . It is the most common notation for describing an algorithm's complexity.
2. Omega Notation () 📉
Omega notation describes the lower bound of an algorithm's running time. It represents the best-case scenario. If an algorithm is , it means its running time will be at least a constant times for large values of .
3. Theta Notation () ⚖️
Theta notation describes the tight bound of an algorithm's running time. It is used when the upper bound (Big O) and the lower bound (Omega) are the same. An algorithm that is means its runtime grows at a rate that is both bounded above and below by . It provides a more precise measure of an algorithm's performance.
7) Arrange following growth rates in increasing order.
(i)
(ii)
(i) .
(ii) .
8) Check the correctness for the following equality:
Yes — the equality is correct.
Proof (Big-O). We need constants and such that for all ,
For we have , so
Thus take and . So .
Extra note. Because for , it is also . Therefore .
9)
(i) Find out big-oh notation of the function
(ii) Find Omega notation of the function
(iii) Find out the -notation for the function
(i) Big-O of
Proof: for , and . So
Take .
(ii) Omega of
First simplify: .
Proof: all terms are nonnegative, so for ,
Take constant .
(So a valid lower bound is .)
(iii) -notation for
Proof (upper): for , , so . Proof (lower): for , . Thus with we have .
10) Write algorithm for insertion sort. Sort given array A = 78 using insertion sort algorithm. Also perform analysis ofinsertion sort algorithm.
Algorithm for Insertion Sort
- Start with the second element (index 1) of the array as the key.
- Compare the key with elements before it.
- Move the elements larger than the key one position ahead to make space.
- Insert the key into the correct position.
- Repeat steps 1-4 for all elements in the array.
Insertion Sort Algorithm (Pseudocode)
InsertionSort(A)
for i = 1 to length(A) - 1
key = A[i]
j = i - 1
while j >= 0 and A[j] > key
A[j + 1] = A[j]
j = j - 1
A[j + 1] = keySorting Array A = 78 Using Insertion Sort
- Initial array: 78
Step-by-step sorting:
- i=1, key=46; compare with 27 → 46 > 27, no change → 78
- i=2, key=11; compare with 46 and 27 → move 46, 27 right → insert 11 → 78
- i=3, key=95; compare with 46 → 95 > 46, no change → 78
- i=4, key=67; compare with 95 → move 95 right → insert 67 → 78
- i=5, key=32; compare with 95, 67, 46, 27 → move 95, 67, 46 right → insert 32 → 78
- i=6, key=78; compare with 95 → move 95 right → insert 78 → 95
Sorted array: 95
Analysis of Insertion Sort Algorithm
| Aspect | Description |
|---|---|
| Time Complexity | - Worst case: O(n²) (array is reverse sorted) - Best case: O(n) (array is already sorted) - Average case: O(n²) |
| Space Complexity | O(1) (in-place sorting, uses constant extra space) |
| Stable Sorting | Yes (equal elements maintain their relative order) |
| Adaptive | Yes (efficient for nearly sorted arrays) |
| Use Cases | Small or partially sorted datasets |
The algorithm iterates through the array, inserting each element into its correct position relative to sorted elements on the left. This leads to quadratic time in general but linear time if the array is almost sorted.
11) Sort the letters of word “PROGRAMMING” in alphabetical order using bubble sort. Write an algorithm for Bubble sort. Also perform analysis of bubble sort algorithm.
Bubble Sort Algorithm ✍️
Bubble sort is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. The algorithm gets its name because smaller elements "bubble" to the top of the list with each iteration.
Here is the algorithm:
Algorithm BubbleSort(A)
Input: An array A of n elements
Output: The sorted array A
for i = 1 to n-1
for j = 1 to n-i
if A[j] > A[j+1]
swap(A[j], A[j+1])Sorting "PROGRAMMING" with Bubble Sort 🎈
Let's sort the letters of the word "PROGRAMMING". First, we'll represent the letters as an array: P, R, O, G, R, A, M, M, I, N, G.
Initial Array: [P, R, O, G, R, A, M, M, I, N, G]
Pass 1:
- Compare
PandR: No swap.[P, R, O, G, R, A, M, M, I, N, G] - Compare
RandO: Swap.[P, O, R, G, R, A, M, M, I, N, G] - Compare
RandG: Swap.[P, O, G, R, R, A, M, M, I, N, G] - Compare
RandR: No swap.[P, O, G, R, R, A, M, M, I, N, G] - Compare
RandA: Swap.[P, O, G, R, A, R, M, M, I, N, G] - Compare
RandM: Swap.[P, O, G, R, A, M, R, M, I, N, G] - Compare
RandM: Swap.[P, O, G, R, A, M, M, R, I, N, G] - Compare
RandI: Swap.[P, O, G, R, A, M, M, I, R, N, G] - Compare
RandN: No swap.[P, O, G, R, A, M, M, I, N, R, G] - Compare
RandG: Swap.[P, O, G, R, A, M, M, I, N, G, R]After Pass 1, the largest element 'R' is at the end.
Pass 2:
[P, O, G, R, A, M, M, I, N, G, R]... and so on.
This process continues for multiple passes. To show a simplified trace, let's skip to the result of each pass's swaps:
- Initial:
[P, R, O, G, R, A, M, M, I, N, G] - End of Pass 1:
[P, O, G, R, A, M, M, I, N, G, R] - End of Pass 2:
[O, P, G, R, A, M, M, I, N, G, R] - End of Pass 3:
[O, G, P, R, A, M, M, I, N, G, R] - End of Pass 4:
[G, O, P, A, R, M, M, I, N, G, R] - End of Pass 5:
[G, A, O, P, M, R, M, I, N, G, R] - End of Pass 6:
[A, G, O, P, M, M, R, I, N, G, R] - End of Pass 7:
[A, G, M, O, P, M, I, R, N, G, R] - End of Pass 8:
[A, G, M, O, M, P, I, N, R, G, R] - End of Pass 9:
[A, G, M, M, O, I, P, N, G, R, R] - End of Pass 10:
[A, G, M, M, I, O, N, P, G, R, R] - End of Pass 11:
[A, G, I, M, M, N, O, P, G, R, R] - End of Pass 12:
[A, G, I, M, M, N, O, P, G, R, R](no swaps) The algorithm terminates when a full pass is completed with no swaps.
Final Sorted Array: [A, G, G, I, M, M, N, O, P, R, R]
Analysis of Bubble Sort 📊
The performance of bubble sort is generally poor compared to other sorting algorithms.
-
Best-Case Analysis: This occurs when the array is already sorted. The algorithm still has to make a full pass to confirm that no swaps are needed. The number of comparisons is , making the time complexity if an optimization is included to stop early. If not, it will still take time.
-
Worst-Case Analysis: This occurs when the array is sorted in reverse order. In each pass, every element needs to be compared and potentially swapped. The total number of comparisons is about the sum of , which is proportional to . The time complexity is .
-
Average-Case Analysis: On average, the number of comparisons and swaps is still proportional to . Therefore, the average-case time complexity is also .
Space Complexity: Bubble sort is an in-place algorithm. It requires only a constant amount of extra memory for temporary variables during swaps. The space complexity is .
12) What is an amortized analysis? Explain accounting method and aggregate analysis with suitable example.
Amortized Analysis ✍️
Amortized analysis is a method for analyzing the time complexity of an algorithm, particularly for a sequence of operations. Instead of looking at the worst-case cost of a single operation, it considers the average cost over a series of operations. This is useful for algorithms where occasional expensive operations are balanced out by many cheap operations, ensuring that the overall average cost per operation is low.
Amortized analysis provides a stronger guarantee than average-case analysis because it doesn't depend on the probability distribution of the input. It gives a worst-case average performance for a sequence of operations.
1. The Accounting Method 💰
The accounting method is a form of amortized analysis that assigns different costs to different operations. We think of these costs as "credits" or "debits." A cheap operation is assigned a slightly higher amortized cost than its actual cost, and the difference is stored as a credit. An expensive operation is then assigned a lower amortized cost, and the stored credits are used to pay for its actual high cost. The total amortized cost for a sequence of operations must be greater than or equal to the total actual cost.
-
Example: A Dynamic Array (Resizable Array)
Consider a dynamic array that doubles in size when it gets full.
- Actual Costs:
- Insertion: Most insertions take a constant amount of time, .
- Resizing: When the array is full, inserting a new element requires creating a new, larger array and copying all elements. This costs .
- Accounting Method:
- Assign an amortized cost of 3 credits to each insertion.
- Scenario: The array has a capacity of .
- Operation: Insert an element.
- Cost: 1 credit to perform the insertion.
- Credit: 2 credits are banked.
- Resizing: When the array is full, we have elements. Let's say we have just inserted the element. We have banked credits.
- Cost of Resizing: The actual cost of copying elements to a new array is .
- We can use the banked credits to pay for this. We use of the banked credits to pay for the copying, and we still have credits left over.
- Since we can always pay for the expensive resizing operation with the banked credits, the amortized cost of each insertion is constant, .
- Actual Costs:
2. The Aggregate Analysis ⚖️
Aggregate analysis is the simplest form of amortized analysis. It calculates the total cost of a sequence of operations and then divides this total by to find the average amortized cost per operation. This method does not involve assigning individual costs or using a credit system.
-
Example: A Dynamic Array (Resizable Array)
Let's use the same example of a dynamic array that doubles in size.
- Scenario: We start with an empty array and perform a sequence of insertions.
- Operations: Most of the insertions will be cheap, costing .
- Expensive Operations: Resizing occurs when the array's capacity is a power of two (e.g., at capacities 1, 2, 4, 8, ...).
- The first resizing copies 1 element.
- The second resizing copies 2 elements.
- The third resizing copies 4 elements.
- ...and so on.
- Total Cost: The total cost for insertions is the sum of the constant costs for all insertions plus the costs of all resizing operations.
- The constant costs are .
- The resizing costs are approximately , where is the size of the array just before the final resizing. This sum is less than .
- Total cost for operations: , which is bounded by .
- Amortized Cost: The total cost is . To find the amortized cost per operation, we divide the total cost by the number of operations, .
- Amortized cost = .
- The amortized cost of a single insertion is .