Okoko
Okoko
python
1. Improved Performance: Randomization often helps achieve better average-case performance Conclusion
compared to deterministic algorithms.
2. Simplicity: Some problems are easier to solve using random choices rather than deterministic A randomized algorithm introduces randomness in its execution to improve efficiency, avoid worst-
logic. case scenarios, and simplify problem-solving. Randomized QuickSort is a prime example where
3. Avoiding Worst-Case Scenarios: Randomization helps in avoiding specific worst-case inputs that randomization helps in achieving an expected O(n log n) complexity, making it more reliable in
might degrade the performance of a deterministic algorithm.
practice compared to its deterministic counterpart.
4. Load Balancing: In distributed systems, randomization helps in balancing the workload efficiently.
5. Security: Many cryptographic algorithms use randomization to enhance security.
Give the formal definition of the string matching problem and a naïve algorithm for its solution.
Example: Randomized QuickSort What is the
complexity of your algorithm? Give an example to show that the bound you have given is tight.
A classic example of a randomized algorithm is Randomized QuickSort.
Deterministic QuickSort
Formal Definition of the String Matching Problem
Algorithm Steps:
The expected time complexity remains O(n log n), irrespective of the input order.
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 1/12 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 2/12
1. Iterate over all possible starting positions i in T from 0 to n − m. Thus, for n = 10 and m = 5:
2. For each position i, compare the substring T [i : i + m] with P .
3. If they match, store or print the index i. The algorithm checks 6 possible shifts.
Each shift compares 5 characters.
4. Continue until all positions are checked.
Total comparisons = 6 × 5 = 30.
python Conclusion
def naive_string_matching(T, P):
n, m = len(T), len(P)
The naïve string matching algorithm checks all possible alignments, leading to O(nm) worst-case
positions = [] # List to store matching indices complexity. This makes it inefficient for large texts, motivating the need for optimized algorithms like
for i in range(n - m + 1): # Iterate over all possible shifts Knuth-Morris-Pratt (KMP) and Boyer-Moore.
match = True
for j in range(m): # Compare character by character
if T[i + j] != P[j]: Describe a naïve string matching algorithm and show the comparisons that the naïve string
match = False
break matcher makes for the
if match: pattern P = 0001 in the text T = 000010001010001.
positions.append(i)
return positions
Naïve String Matching Algorithm
# Example usage
text = "abcabcabc"
pattern = "abc" The naïve string matching algorithm slides the pattern over the text one character at a time and
matches = naive_string_matching(text, pattern)
print("Pattern found at indices:", matches)
checks for a match at each position.
Algorithm Steps:
Output:
1. Iterate over all possible starting positions i in T from 0 to n − m.
less 2. For each position i, compare the substring T [i : i + m] with P character by character.
3. If a complete match is found, store or print the index i.
Pattern found at indices: [0, 3, 6]
4. Continue until all positions are checked.
Consider the worst-case scenario where: Shift i Compare T [i : i + 4] with P = 0001 Match?
7 0100 vs. 0001 ❌ (Mismatch at first character) H(T [i + 1 : i + m]) = d ⋅ (H(T [i : i + m − 1]) − T [i] ⋅ dm−1 ) + T [i + m]
8 1001 vs. 0001 ❌ (Mismatch at first character) Where:
9 0010 vs. 0001 ❌ (Mismatch at third character)
10 0100 vs. 0001 ❌ (Mismatch at first character) d is a base (commonly 256 for ASCII characters).
11 1000 vs. 0001 ❌ (Mismatch at first character) q is a prime number used for modulo to avoid overflow.
H(T [i : i + m]) is the hash value of the substring starting at index i of length m.
Matches Found
Rabin-Karp Algorithm (Python)
The pattern "0001" appears at indices 1 and 5.
python
Time Complexity Analysis def rabin_karp(T, P, d=256, q=101): # d=256 (ASCII), q=101 (prime)
n, m = len(T), len(P)
h_P = 0 # Hash value for pattern
Outer loop runs for (n − m + 1) = (15 − 4 + 1) = 12 shifts. h_T = 0 # Hash value for text window
Each shift requires at most O(m) = O(4) character comparisons. h = 1 # d^(m-1) % q
positions = [] # Stores indices of matches
Worst-case complexity: O(nm) = O(15 × 4) = O(60).
# Precompute h = d^(m-1) % q
for _ in range(m - 1):
h = (h * d) % q
Conclusion
# Compute initial hash values for P and first window of T
for i in range(m):
The naïve string matching algorithm successfully finds the pattern at indices 1 and 5 in O(nm) time. h_P = (d * h_P + ord(P[i])) % q
h_T = (d * h_T + ord(T[i])) % q
Although simple, this method is inefficient for large texts, which is why optimized algorithms like Knuth-
Morris-Pratt (KMP) or Boyer-Moore are preferred for practical applications. # Slide over text
for i in range(n - m + 1):
# If hash matches, check characters
if h_P == h_T:
Give Rabin-Karp algorithm for string matching and show how it works on a suitable example. if T[i:i + m] == P:
positions.append(i) # Confirm match
If at any index the hash matches H(P ), perform a direct character check.
Compute Hash for Pattern P ("0001")
Final Matches:
H(P ) = (0 × 103 + 0 × 102 + 0 × 101 + 1) mod 101
The pattern "979" is found at index 12.
H(P ) = (1) mod 101 = 1
✅ Rolling hash improves efficiency over naive matching. Comparisons for Each Shift
✅ Expected complexity is O(n), much faster than O(nm). Shift i Window T [i : i + 4] H(T [i]) Matches H(P ) = 1? Character Check?
However, false positives (hash collisions) require extra verification. 0 `"0000"` 0 ❌ (No match) No
1 `"0001"` 1 ✅ (Match) Yes (Match at index 1)
Show the comparisons that the Rabin-Karp algorithm makes for the pattern P = 0001 in the text T = 2 `"0010"` 10 ❌ (No match) No
000010001010001.
3 `"0100"` 100 ❌ (No match) No
4 `"1000"` 98 ❌ (No match) No
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 7/12 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 8/12
Shift i Window T [i : i + 4] H(T [i]) Matches H(P ) = 1? Character Check? 1. P (Polynomial Time):
5 `"0001"` 1 ✅ (Match) Yes (Match at index 5) The class of problems that can be solved in polynomial time, i.e., in O(n^k) for some constant
6 `"0010"` 10 ❌ (No match) No
k.
Initial Hash Computation: O(m) = O(4) a. Decision Problems vs. Optimization Problems
Sliding Window Hash Updates: O(n − m) = O(15 − 4) = O(11)
Character Comparisons (only for matches): O(m) per match Decision Problems: Problems where the answer is YES or NO.
In this case, 2 matches → 2 × 4 = 8 extra operations. Example: "Does a Hamiltonian cycle exist in this graph?"
Optimization Problems: Problems where the goal is to find the best solution among many
Overall, the expected time complexity is O(n), significantly better than the naïve O(nm). possible solutions.
Example: "Find the shortest Hamiltonian cycle."
6. Distinguish clearly between the following classes of problems (i) P (ii) NP (iii) NP Complete.
b. Reductions
7. Explain the following key concepts in relation to NPC:
a. Decision problems vs. Optimization problems A method to transform one problem into another in polynomial time.
b. Reductions If problem A is reduced to problem B, and B is solvable in polynomial time, then A is also solvable
8. What are approximation algorithms? What are the situations in which approximation algorithms in polynomial time.
are useful? Used to prove NP-completeness.
9. What is the significance of approximation ratio of an approximation algorithm?
10. Define the following in the context of approximation algorithms:
a. Approximation scheme 8. Approximation Algorithms
b. Polynomial-time approximation scheme
c. Fully polynomial-time approximation scheme Approximation algorithms find near-optimal solutions for NP-hard problems in polynomial time.
11. Give an approximation algorithm for vertex cover and describe its running time analysis and Used when finding an exact solution is computationally infeasible.
approximation Example: The Greedy algorithm for the Vertex Cover problem.
guarantee.
12. What is the set-covering problem? Give the outline of a greedy approximation algorithm for set
covering 9. Approximation Ratio
problem. How good is this algorithm?
Measures how close an approximation algorithm's solution is to the optimal solution.
Defined as:
Here are detailed answers to your questions:
Solution Cost (Approximate Algorithm)
R=
Solution Cost (Optimal Algorithm)
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 9/12 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 10/12
A ratio closer to 1 is better. 2. Pick the subset that covers the most uncovered elements.
3. Repeat until all elements are covered.
A PTAS where the runtime is polynomial in both input size and 1/ε.
Example: Fractional Knapsack problem.
The algorithm runs in O(E) since each edge is considered at most once.
Approximation Guarantee
The obtained cover is at most twice the size of the optimal vertex cover.
Approximation Ratio: 2-approximation.
Given a universe U and a collection of subsets S1, S2, ..., Sm, find the minimum number of subsets
whose union covers U.
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 11/12 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 12/12
Can be used for cycle detection, topological sorting, connected components, etc.
✅ Good for level-wise traversal. Recursive call stack can go up to O(V ) in the worst case.
Space Complexity:
4. Minimum Spanning Tree (MST) - Kruskal’s Algorithm
O(V + E)
Working:
Sorts all edges in increasing order and picks the smallest edge that doesn’t form a cycle. 6. All-Pairs Shortest Path - Floyd-Warshall Algorithm
Uses Disjoint Set Union (DSU) for cycle detection.
Working:
Benefits:
Using Union-Find: O(E log E) ❌ Very slow for large graphs due to O(V ) complexity.
3
Working: O(V 2 )
Uses Min-Heap (Priority Queue) to pick the next minimum distance vertex.
Works only for graphs with non-negative weights. 7. All-Pairs Shortest Path - Bellman-Ford Algorithm
Benefits: Working:
✅ Guarantees shortest path for non-negative weights.
✅ Faster than Bellman-Ford for sparse graphs. Relax all edges V − 1 times to update distances.
Can detect negative weight cycles.
Drawbacks: Benefits:
❌ Does not work with negative weights (use Bellman-Ford instead). ✅ Works with negative weights.
❌ Heap-based implementation can be tricky. ✅ Can detect negative weight cycles.
Time Complexity: Drawbacks:
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 3/6 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 4/6
❌ Slower than Dijkstra’s for positive weight graphs.
❌ O(V E) time makes it infeasible for large graphs.
Time Space
Algorithm Use Case Pros Cons
Complexity Complexity
Benefits:
Space Complexity:
O(V + E)
Comparison Table
Time Space
Algorithm Use Case Pros Cons
Complexity Complexity
Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 5/6 Printed using ChatGPT to PDF, powered by PDFCrowd HTML to PDF API. 6/6