Algorithms 4
Algorithms 4
Contents
1 Binary Powering Algorithm 4
1.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.2 Explanation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.3 Time Complexity Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.4 Space Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Karatsuba’s Algorithm 5
2.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Explanation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3 Time Complexity Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Space Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1
6 Quicksort Algorithm 14
6.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.2 Explanation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.3 Time Complexity Analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
6.4 Space Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
6.5 Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2
11 Lempel-Ziv-Welsh (LZW) Compression Algorithm 24
11.1 Algorithm Description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
11.2 Explanation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
11.3 Decoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
11.4 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3
1 Binary Powering Algorithm
The Binary Powering Algorithm, also known as Exponentiation by Squaring, is an efficient algorithm
to compute xn for integers x and n.
while n > 0:
if n % 2 == 1: # If n is odd
result *= base
base *= base
n //= 2
return result
1.2 Explanation
The algorithm works as follows:
1. Initialize ‘result‘ to 1 and ‘base‘ to x. 2. While n > 0: - If n is odd, multiply ‘result‘ by
‘base‘. - Square the ‘base‘. - Halve n (using integer division). 3. Return the ‘result‘.
The key idea is that we break down the computation of xn into smaller problems using the
properties of exponents: (
n (xn/2 )2 if n is even,
x = (n−1)/2 2
x · (x ) if n is odd.
4
2 Karatsuba’s Algorithm
Karatsuba’s Algorithm is an efficient algorithm for multiplying large integers or polynomials. It
reduces the complexity of multiplication from the naive O(n2 ) to O(nlog2 3 ) ≈ O(n1.585 ).
2.2 Explanation
The algorithm works as follows:
1. If the numbers are small (single-digit), multiply them directly. 2. Otherwise, split each
number into two halves: - x = xhigh · 10half + xlow - y = yhigh · 10half + ylow 3. Compute three
recursive products: - z0 = xlow · ylow - z1 = (xlow + xhigh ) · (ylow + yhigh ) - z2 = xhigh · yhigh 4.
Combine the results using the formula:
• Each recursive step splits the problem into three smaller subproblems of half the size.
5
• The depth of the recursion tree is log2 n.
• Combining results at each level takes O(n).
• Thus, the total complexity is O(nlog2 3 ).
6
# Helper functions for matrix operations ( split , add , subtract , combine )
are omitted for brevity .
3.2 Explanation
The algorithm works as follows: 1. Divide the input matrices A and B into four equal-sized
submatrices:
A11 A12 B11 B12
A= , B=
A21 A22 B21 B22
2. Compute seven products:
C11 = M1 + M4 − M5 + M7
C12 = M3 + M5
C21 = M2 + M4
C22 = M1 − M2 + M3 + M6
7
• The algorithm divides the matrices into four submatrices of size n/2 × n/2.
• Seven recursive calls are made, and combining the results requires O(n2 ) operations.
• The overall complexity is T (n) = 7T (n/2) + O(n2 ), which solves to O(nlog2 7 ).
8
4 Quickselect Algorithm and Median of Medians
The Quickselect algorithm is an efficient algorithm to find the k-th smallest element in an unsorted
array. To improve its worst-case behavior, the Median of Medians algorithm can be used as a pivot
selection strategy.
4.2 Explanation
The algorithm works as follows: 1. Select a pivot element from the array (randomly or determin-
istically). 2. Partition the array into: - ‘low‘: Elements less than the pivot. - ‘equal‘: Elements
equal to the pivot. - ‘high‘: Elements greater than the pivot. 3. Recurse into the part of the array
containing the k-th smallest element: - If k lies in ‘low‘, recurse on ‘low‘. - If k lies in ‘equal‘, return
the pivot. - If k lies in ‘high‘, recurse on ‘high‘.
• On average, the size of the subarray is halved at each step, leading to O(n+n/2+n/4+. . .) =
O(n).
9
The worst-case complexity is O(n2 ) if the pivot selection is poor (e.g., always selecting the smallest
or largest element).
10
4.4 Median of Medians Algorithm
The Median of Medians algorithm improves the pivot selection to ensure a good split and guarantee
O(n) worst-case complexity for Quickselect.
4.5 Explanation
The algorithm works as follows: 1. Divide the array into groups of at most 5 elements. 2. Find the
median of each group (sort each group and pick the middle element). 3. Recursively compute the
median of the medians to use as the pivot.
• Solving the recurrence T (n) = T (n/5) + T (7n/10) + O(n) leads to T (n) = O(n).
11
5 Closest Pair of Points Algorithm
The Closest Pair of Points algorithm is used to find the two points in a set of points (in a plane)
that are closest to each other. A naive approach has O(n2 ) complexity, but the divide-and-conquer
approach reduces this to O(n log n).
Qx , Rx = px [: mid ] , px [ mid :]
Qy , Ry = [ p for p in py if p in Qx ] , [ p for p in py if p in Rx ]
12
return d , best_pair
5.2 Explanation
The algorithm works as follows: 1. **Base Case**: If the number of points is 3 or fewer, compute
the distances using brute force. 2. **Divide**: Split the points into two halves based on their
x-coordinates. 3. **Recur**: Recursively find the closest pairs in the left and right halves. 4.
**Combine**: - The closest pair could lie across the two halves. To find this, examine a ”strip” of
points around the dividing line. - In this strip, only points within d (the smaller of the distances
from the two halves) need to be checked. - Further, only at most 6 points in the strip need to be
compared for each point, reducing the time complexity.
5.4 Example
If the input is:
points = [(2, 3), (12, 30), (40, 50), (5, 1), (12, 10), (3, 4)]
√
The closest pair of points is (2, 3) and (3, 4), with a distance of 2.
13
6 Quicksort Algorithm
Quicksort is a divide-and-conquer algorithm for sorting an array or list. It works by selecting a
pivot, partitioning the array around the pivot, and recursively sorting the subarrays. Quicksort has
an average-case time complexity of O(n log n), making it one of the most efficient sorting algorithms
in practice.
6.2 Explanation
The algorithm works as follows: 1. **Base Case**: If the array has one or no elements, it is already
sorted. 2. **Choose a Pivot**: Select a pivot element (e.g., the last element of the array). 3.
**Partition**: - Divide the array into three subarrays: - ‘less‘: Elements less than the pivot. -
‘equal‘: Elements equal to the pivot. - ‘greater‘: Elements greater than the pivot. 4. **Recur**:
- Recursively sort the ‘less‘ and ‘greater‘ subarrays. 5. **Combine**: - Concatenate the results of
the recursive calls with the pivot to produce the sorted array.
14
6.4 Space Complexity
The space complexity depends on the recursion stack:
• **Average Case**: O(log n) for balanced partitions.
6.5 Example
If the input array is:
arr = [3, 6, 8, 10, 1, 2, 1]
The steps of Quicksort are:
• Choose pivot: 1.
• Partition into:
less = [], equal = [1, 1], greater = [3, 6, 8, 10, 2].
15
7 Karger’s Contraction Algorithm for Global Minimum Cut
Karger’s Contraction Algorithm is a randomized algorithm used to compute the global minimum
cut of a connected, undirected graph. It achieves this by iteratively contracting edges and reducing
the graph until only two vertices remain.
7.2 Explanation
The algorithm works as follows: 1. **Input**: A connected, undirected graph represented as an
adjacency list. 2. **Random Edge Selection**: - Choose an edge (u, v) uniformly at random. 3.
**Contraction**: - Merge the two vertices u and v into a single vertex. - Update the adjacency
list to reflect the contraction: - Append the neighbors of v to u’s adjacency list. - Replace all
occurrences of v in the graph with u. - Remove any self-loops (edges where u = v). 4. **Repeat**:
- Continue contracting random edges until only two vertices remain. 5. **Output**: - The number
of edges between the two remaining vertices is the size of the minimum cut.
16
7.3 Time Complexity Analysis
The expected time complexity of the algorithm is O(n2 ):
• Each contraction step takes O(n) time to update the adjacency list.
to a high constant (e.g., 1 − 1/e), the algorithm must be repeated O(log n) times.
7.6 Example
Consider the following input graph:
Vertices: {A, B, C, D} Edges: {(A, B), (A, C), (B, C), (B, D), (C, D)}
Steps of the algorithm: 1. Randomly select an edge, e.g., (A, B), and contract A and B into a
single vertex. 2. Update the graph and remove self-loops. 3. Repeat until two vertices remain.
The minimum cut is the number of edges between the final two vertices.
7.7 Summary
Karger’s Contraction Algorithm provides an elegant and simple way to compute the global minimum
cut of a graph using randomization. By repeating the algorithm a sufficient number of times, the
minimum cut can be found with high probability.
17
8 Random Walk Algorithm for Maze Navigation
The Random Walk algorithm is a simple probabilistic algorithm used to navigate a graph or a
maze. It selects neighboring vertices uniformly at random to explore the graph.
8.2 Explanation
The algorithm works as follows: 1. **Input**: A graph G, an initial vertex u, a target vertex v,
and an optional limit on the number of steps. 2. **Random Walk**: - At each step, choose a
random neighbor of the current vertex. - Move to that neighbor. 3. **Termination**: - Stop if the
target vertex is reached or the maximum number of steps is exceeded. 4. **Output**: - Return
whether the target vertex was reached.
18
8.5 Example
Consider a simple undirected graph with 6 vertices:
Vertices: {1, 2, 3, 4, 5, 6}, Edges: {(1, 2), (1, 3), (2, 4), (3, 4), (4, 5), (5, 6)}.
• **Random Walk**: Probabilistic with O(nm) time complexity but O(log n) memory com-
plexity.
8.7 Applications
• **Maze Solving**: - Use random walks to find a path out of a maze.
• **Graph Exploration**: - Estimate connectivity and distances in a graph.
• **Markov Chains**: - Basis for modeling random processes on graphs.
19
9 WalkSAT Algorithm for Boolean Satisfiability (SAT)
The WalkSAT algorithm is a probabilistic, local search-based Monte Carlo algorithm used to solve
Boolean satisfiability problems (SAT). It is particularly effective for k-SAT formulas, where each
clause involves at most k variables.
9.2 Explanation
The algorithm works as follows: 1. **Initialization**: - Start with a random assignment of truth
values to variables. 2. **Main Loop**: - Check if the current assignment satisfies the formula. -
If not, pick a random clause that is unsatisfied. - Flip a variable in the clause: - With probability
p, flip a random variable. - Otherwise, flip the variable that maximizes the number of satisfied
clauses. 3. **Termination**: - Stop when a satisfying assignment is found or when the maximum
number of flips is reached. 4. **Output**: - Return the satisfying assignment if found; otherwise,
return ”FAIL.”
20
9.3 Time Complexity Analysis
The time complexity of WalkSAT is O(max flips · k):
• **Per Flip**: - Evaluating the unsatisfied clauses and flipping a variable takes O(k) opera-
tions, where k is the maximum clause length.
• **Total**: - For max flips iterations, the total complexity is O(max flips · k).
The algorithm is efficient for practical instances, even with a large number of variables and
clauses.
9.5 Example
Consider the formula:
Steps: 1. Start with a random assignment, e.g., (x1 , x2 , x3 ) = (0, 1, 0). 2. Identify unsatisfied
clauses, e.g., (x1 ∨ x2 ∨ ¬x3 ) is unsatisfied. 3. Flip a variable in the clause, e.g., flip x1 , resulting
in (1, 1, 0). 4. Repeat until the formula is satisfied or the maximum number of flips is reached.
• **WalkSAT**: - Uses randomization and local search, with expected polynomial-time per-
formance for many practical problems.
• **Modern SAT Solvers**: - Combine local search with other techniques to handle much larger
formulas efficiently.
9.7 Applications
WalkSAT is widely used in:
• Hardware and software verification.
21
10 Union-Find Algorithm with Path Compression and Amor-
tization
The Union-Find algorithm is a data structure used to efficiently manage and query disjoint sets.
It supports two primary operations: - **Find**: Determine which set a particular element belongs
to. - **Union**: Merge two sets into one.
With the use of path compression and union by rank, the amortized time complexity becomes
nearly constant per operation.
# Path Compression
def find ( parent , x ):
if parent [ x ] != x :
parent [ x ] = find ( parent , parent [ x ]) # Point directly to root
return parent [ x ]
# Union by Rank
def union ( parent , rank , x , y ):
root_x = find ( parent , x )
root_y = find ( parent , y )
22
10.3 Time Complexity Analysis
The amortized time complexity of a sequence of m union and find operations on a forest with n
elements is O(m log∗ n):
• **Log-Star Function (log∗ n)**: - log∗ n is the number of times the logarithm function must
be applied before the result is less than or equal to 1. - log∗ 2 = 1, log∗ 4 = 2, log∗ 16 = 3, and
so on. - In practice, log∗ n is extremely small (e.g., log∗ 1019 ≈ 5).
• This results in **near-constant time complexity** for practical purposes.
10.5 Applications
The Union-Find algorithm is widely used in:
• **Kruskal’s Algorithm** for finding the minimum spanning tree.
10.6 Example
Consider a graph with 5 vertices: {0, 1, 2, 3, 4}.
1. Initialize: ‘parent = [0, 1, 2, 3, 4], rank = [0, 0, 0, 0, 0]‘.
2. Perform unions: - ‘union(0, 1)‘ → ‘parent = [0, 0, 2, 3, 4]‘, ‘rank = [1, 0, 0, 0, 0]‘. - ‘union(1,
2)‘ → ‘parent = [0, 0, 0, 3, 4]‘, ‘rank = [1, 0, 0, 0, 0]‘.
10.7 Properties
• **Path compression** ensures every node points directly to the root, reducing future ‘find‘
calls.
• **Union by rank** ensures the trees remain shallow.
• Combined, these techniques yield excellent amortized performance.
23
11 Lempel-Ziv-Welsh (LZW) Compression Algorithm
LZW compression is a lossless data compression algorithm that builds a dictionary of substrings
dynamically as it encodes the input. It is efficient for repetitive data and operates in a single pass.
if prefix :
compressed_data . append ( dictionary [ prefix ])
return compressed_data
11.2 Explanation
1. **Initialization**: - The dictionary starts with all single-character strings. 2. **Encoding**: -
For each character in the input, check if the current string (‘prefix + char‘) exists in the dictionary.
- If it exists, continue building the string. - If it doesn’t, output the code for the prefix, add the
new string to the dictionary, and reset the prefix. 3. **Output**: - Output the remaining string if
any.
11.3 Decoding
Decoding reconstructs the dictionary dynamically as the encoded data is processed. An exceptional
case occurs when a new code needs to be inferred from the existing dictionary.
24
dictionary = { i : chr ( i ) for i in range (256)}
dict_size = 256
prefix = chr ( compressed_data . pop (0))
decompressed_data = [ prefix ]
11.4 Applications
- Used in tools like ‘gzip‘ and GIF image compression. - Effective for data with repetitive patterns.
—
25
heappush ( heap , [ low1 [0] + low2 [0]] + low1 [1:] + low2 [1:])
# Example input : { ’ a ’: 11 , ’b ’: 4 , ’c ’: 3 , ’d ’: 3 , ’r ’: 4 , ’! ’: 1}
12.2 Explanation
1. **Build a Frequency Table**: - Count the occurrences of each symbol in the input. 2. **Con-
struct a Binary Trie**: - Use a priority queue to repeatedly combine the two least frequent nodes
into a single parent node. 3. **Generate Codes**: - Traverse the binary trie to assign binary codes
to each symbol.
12.3 Decoding
To decode a Huffman-encoded string, traverse the binary trie using the bits of the encoded string
until reaching a leaf node.
12.4 Properties
- **Prefix-Free Codes**: - No code is a prefix of another, ensuring unique decodings. - **Optimal-
ity**: - Produces the shortest average encoding length for a given symbol distribution.
12.5 Applications
- Widely used in compression formats like JPEG, PNG, and MP3. - Useful for transmitting data
efficiently in communication systems.
—
13.1 Summary
Many compression systems, such as ‘gzip‘, combine both algorithms: first applying LZW to capture
repetitions, and then Huffman encoding to optimize based on symbol frequencies.
26